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)
7599 if(!this.errPopover){
7603 target.inputEl().focus();
7605 var oIndex = target.el.getStyle('z-index');
7607 target.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
7609 target.el.addClass('roo-invalid-outline');
7611 var fadeout = function(){
7613 target.inputEl().un('blur', fadeout);
7614 target.inputEl().un('keyup', fadeout);
7616 target.el.setStyle('z-index', oIndex);
7618 target.el.removeClass('roo-invalid-outline');
7622 target.inputEl().on('blur', fadeout);
7623 target.inputEl().on('keyup', fadeout);
7632 * Returns true if any fields in this form have changed since their original load.
7635 isDirty : function(){
7637 var items = this.getItems();
7638 items.each(function(f){
7648 * Performs a predefined action (submit or load) or custom actions you define on this form.
7649 * @param {String} actionName The name of the action type
7650 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
7651 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7652 * accept other config options):
7654 Property Type Description
7655 ---------------- --------------- ----------------------------------------------------------------------------------
7656 url String The url for the action (defaults to the form's url)
7657 method String The form method to use (defaults to the form's method, or POST if not defined)
7658 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
7659 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
7660 validate the form on the client (defaults to false)
7662 * @return {BasicForm} this
7664 doAction : function(action, options){
7665 if(typeof action == 'string'){
7666 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7668 if(this.fireEvent('beforeaction', this, action) !== false){
7669 this.beforeAction(action);
7670 action.run.defer(100, action);
7676 beforeAction : function(action){
7677 var o = action.options;
7680 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7682 // not really supported yet.. ??
7684 //if(this.waitMsgTarget === true){
7685 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7686 //}else if(this.waitMsgTarget){
7687 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7688 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7690 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7696 afterAction : function(action, success){
7697 this.activeAction = null;
7698 var o = action.options;
7700 //if(this.waitMsgTarget === true){
7702 //}else if(this.waitMsgTarget){
7703 // this.waitMsgTarget.unmask();
7705 // Roo.MessageBox.updateProgress(1);
7706 // Roo.MessageBox.hide();
7713 Roo.callback(o.success, o.scope, [this, action]);
7714 this.fireEvent('actioncomplete', this, action);
7718 // failure condition..
7719 // we have a scenario where updates need confirming.
7720 // eg. if a locking scenario exists..
7721 // we look for { errors : { needs_confirm : true }} in the response.
7723 (typeof(action.result) != 'undefined') &&
7724 (typeof(action.result.errors) != 'undefined') &&
7725 (typeof(action.result.errors.needs_confirm) != 'undefined')
7728 Roo.log("not supported yet");
7731 Roo.MessageBox.confirm(
7732 "Change requires confirmation",
7733 action.result.errorMsg,
7738 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
7748 Roo.callback(o.failure, o.scope, [this, action]);
7749 // show an error message if no failed handler is set..
7750 if (!this.hasListener('actionfailed')) {
7751 Roo.log("need to add dialog support");
7753 Roo.MessageBox.alert("Error",
7754 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7755 action.result.errorMsg :
7756 "Saving Failed, please check your entries or try again"
7761 this.fireEvent('actionfailed', this, action);
7766 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7767 * @param {String} id The value to search for
7770 findField : function(id){
7771 var items = this.getItems();
7772 var field = items.get(id);
7774 items.each(function(f){
7775 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7782 return field || null;
7785 * Mark fields in this form invalid in bulk.
7786 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7787 * @return {BasicForm} this
7789 markInvalid : function(errors){
7790 if(errors instanceof Array){
7791 for(var i = 0, len = errors.length; i < len; i++){
7792 var fieldError = errors[i];
7793 var f = this.findField(fieldError.id);
7795 f.markInvalid(fieldError.msg);
7801 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7802 field.markInvalid(errors[id]);
7806 //Roo.each(this.childForms || [], function (f) {
7807 // f.markInvalid(errors);
7814 * Set values for fields in this form in bulk.
7815 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7816 * @return {BasicForm} this
7818 setValues : function(values){
7819 if(values instanceof Array){ // array of objects
7820 for(var i = 0, len = values.length; i < len; i++){
7822 var f = this.findField(v.id);
7824 f.setValue(v.value);
7825 if(this.trackResetOnLoad){
7826 f.originalValue = f.getValue();
7830 }else{ // object hash
7833 if(typeof values[id] != 'function' && (field = this.findField(id))){
7835 if (field.setFromData &&
7837 field.displayField &&
7838 // combos' with local stores can
7839 // be queried via setValue()
7840 // to set their value..
7841 (field.store && !field.store.isLocal)
7845 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7846 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7847 field.setFromData(sd);
7850 field.setValue(values[id]);
7854 if(this.trackResetOnLoad){
7855 field.originalValue = field.getValue();
7861 //Roo.each(this.childForms || [], function (f) {
7862 // f.setValues(values);
7869 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7870 * they are returned as an array.
7871 * @param {Boolean} asString
7874 getValues : function(asString){
7875 //if (this.childForms) {
7876 // copy values from the child forms
7877 // Roo.each(this.childForms, function (f) {
7878 // this.setValues(f.getValues());
7884 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7885 if(asString === true){
7888 return Roo.urlDecode(fs);
7892 * Returns the fields in this form as an object with key/value pairs.
7893 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7896 getFieldValues : function(with_hidden)
7898 var items = this.getItems();
7900 items.each(function(f){
7904 var v = f.getValue();
7905 if (f.inputType =='radio') {
7906 if (typeof(ret[f.getName()]) == 'undefined') {
7907 ret[f.getName()] = ''; // empty..
7910 if (!f.el.dom.checked) {
7918 // not sure if this supported any more..
7919 if ((typeof(v) == 'object') && f.getRawValue) {
7920 v = f.getRawValue() ; // dates..
7922 // combo boxes where name != hiddenName...
7923 if (f.name != f.getName()) {
7924 ret[f.name] = f.getRawValue();
7926 ret[f.getName()] = v;
7933 * Clears all invalid messages in this form.
7934 * @return {BasicForm} this
7936 clearInvalid : function(){
7937 var items = this.getItems();
7939 items.each(function(f){
7950 * @return {BasicForm} this
7953 var items = this.getItems();
7954 items.each(function(f){
7958 Roo.each(this.childForms || [], function (f) {
7965 getItems : function()
7967 var r=new Roo.util.MixedCollection(false, function(o){
7968 return o.id || (o.id = Roo.id());
7970 var iter = function(el) {
7977 Roo.each(el.items,function(e) {
7995 * Ext JS Library 1.1.1
7996 * Copyright(c) 2006-2007, Ext JS, LLC.
7998 * Originally Released Under LGPL - original licence link has changed is not relivant.
8001 * <script type="text/javascript">
8004 * @class Roo.form.VTypes
8005 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8008 Roo.form.VTypes = function(){
8009 // closure these in so they are only created once.
8010 var alpha = /^[a-zA-Z_]+$/;
8011 var alphanum = /^[a-zA-Z0-9_]+$/;
8012 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8013 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8015 // All these messages and functions are configurable
8018 * The function used to validate email addresses
8019 * @param {String} value The email address
8021 'email' : function(v){
8022 return email.test(v);
8025 * The error text to display when the email validation function returns false
8028 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8030 * The keystroke filter mask to be applied on email input
8033 'emailMask' : /[a-z0-9_\.\-@]/i,
8036 * The function used to validate URLs
8037 * @param {String} value The URL
8039 'url' : function(v){
8043 * The error text to display when the url validation function returns false
8046 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8049 * The function used to validate alpha values
8050 * @param {String} value The value
8052 'alpha' : function(v){
8053 return alpha.test(v);
8056 * The error text to display when the alpha validation function returns false
8059 'alphaText' : 'This field should only contain letters and _',
8061 * The keystroke filter mask to be applied on alpha input
8064 'alphaMask' : /[a-z_]/i,
8067 * The function used to validate alphanumeric values
8068 * @param {String} value The value
8070 'alphanum' : function(v){
8071 return alphanum.test(v);
8074 * The error text to display when the alphanumeric validation function returns false
8077 'alphanumText' : 'This field should only contain letters, numbers and _',
8079 * The keystroke filter mask to be applied on alphanumeric input
8082 'alphanumMask' : /[a-z0-9_]/i
8092 * @class Roo.bootstrap.Input
8093 * @extends Roo.bootstrap.Component
8094 * Bootstrap Input class
8095 * @cfg {Boolean} disabled is it disabled
8096 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8097 * @cfg {String} name name of the input
8098 * @cfg {string} fieldLabel - the label associated
8099 * @cfg {string} placeholder - placeholder to put in text.
8100 * @cfg {string} before - input group add on before
8101 * @cfg {string} after - input group add on after
8102 * @cfg {string} size - (lg|sm) or leave empty..
8103 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8104 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8105 * @cfg {Number} md colspan out of 12 for computer-sized screens
8106 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8107 * @cfg {string} value default value of the input
8108 * @cfg {Number} labelWidth set the width of label (0-12)
8109 * @cfg {String} labelAlign (top|left)
8110 * @cfg {Boolean} readOnly Specifies that the field should be read-only
8111 * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8112 * @cfg {String} indicatorpos (left|right) default left
8114 * @cfg {String} align (left|center|right) Default left
8115 * @cfg {Boolean} forceFeedback (true|false) Default false
8121 * Create a new Input
8122 * @param {Object} config The config object
8125 Roo.bootstrap.Input = function(config){
8126 Roo.bootstrap.Input.superclass.constructor.call(this, config);
8131 * Fires when this field receives input focus.
8132 * @param {Roo.form.Field} this
8137 * Fires when this field loses input focus.
8138 * @param {Roo.form.Field} this
8143 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
8144 * {@link Roo.EventObject#getKey} to determine which key was pressed.
8145 * @param {Roo.form.Field} this
8146 * @param {Roo.EventObject} e The event object
8151 * Fires just before the field blurs if the field value has changed.
8152 * @param {Roo.form.Field} this
8153 * @param {Mixed} newValue The new value
8154 * @param {Mixed} oldValue The original value
8159 * Fires after the field has been marked as invalid.
8160 * @param {Roo.form.Field} this
8161 * @param {String} msg The validation message
8166 * Fires after the field has been validated with no errors.
8167 * @param {Roo.form.Field} this
8172 * Fires after the key up
8173 * @param {Roo.form.Field} this
8174 * @param {Roo.EventObject} e The event Object
8180 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
8182 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8183 automatic validation (defaults to "keyup").
8185 validationEvent : "keyup",
8187 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8189 validateOnBlur : true,
8191 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8193 validationDelay : 250,
8195 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8197 focusClass : "x-form-focus", // not needed???
8201 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8203 invalidClass : "has-warning",
8206 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8208 validClass : "has-success",
8211 * @cfg {Boolean} hasFeedback (true|false) default true
8216 * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8218 invalidFeedbackClass : "glyphicon-warning-sign",
8221 * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8223 validFeedbackClass : "glyphicon-ok",
8226 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8228 selectOnFocus : false,
8231 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8235 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8240 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8242 disableKeyFilter : false,
8245 * @cfg {Boolean} disabled True to disable the field (defaults to false).
8249 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8253 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8255 blankText : "This field is required",
8258 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8262 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8264 maxLength : Number.MAX_VALUE,
8266 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8268 minLengthText : "The minimum length for this field is {0}",
8270 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8272 maxLengthText : "The maximum length for this field is {0}",
8276 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8277 * If available, this function will be called only after the basic validators all return true, and will be passed the
8278 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8282 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8283 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8284 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
8288 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8292 autocomplete: false,
8311 formatedValue : false,
8312 forceFeedback : false,
8314 indicatorpos : 'left',
8316 parentLabelAlign : function()
8319 while (parent.parent()) {
8320 parent = parent.parent();
8321 if (typeof(parent.labelAlign) !='undefined') {
8322 return parent.labelAlign;
8329 getAutoCreate : function()
8331 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8337 if(this.inputType != 'hidden'){
8338 cfg.cls = 'form-group' //input-group
8344 type : this.inputType,
8346 cls : 'form-control',
8347 placeholder : this.placeholder || '',
8348 autocomplete : this.autocomplete || 'new-password'
8352 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8355 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8356 input.maxLength = this.maxLength;
8359 if (this.disabled) {
8360 input.disabled=true;
8363 if (this.readOnly) {
8364 input.readonly=true;
8368 input.name = this.name;
8372 input.cls += ' input-' + this.size;
8376 ['xs','sm','md','lg'].map(function(size){
8377 if (settings[size]) {
8378 cfg.cls += ' col-' + size + '-' + settings[size];
8382 var inputblock = input;
8386 cls: 'glyphicon form-control-feedback'
8389 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8392 cls : 'has-feedback',
8400 if (this.before || this.after) {
8403 cls : 'input-group',
8407 if (this.before && typeof(this.before) == 'string') {
8409 inputblock.cn.push({
8411 cls : 'roo-input-before input-group-addon',
8415 if (this.before && typeof(this.before) == 'object') {
8416 this.before = Roo.factory(this.before);
8418 inputblock.cn.push({
8420 cls : 'roo-input-before input-group-' +
8421 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8425 inputblock.cn.push(input);
8427 if (this.after && typeof(this.after) == 'string') {
8428 inputblock.cn.push({
8430 cls : 'roo-input-after input-group-addon',
8434 if (this.after && typeof(this.after) == 'object') {
8435 this.after = Roo.factory(this.after);
8437 inputblock.cn.push({
8439 cls : 'roo-input-after input-group-' +
8440 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
8444 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8445 inputblock.cls += ' has-feedback';
8446 inputblock.cn.push(feedback);
8450 if (align ==='left' && this.fieldLabel.length) {
8455 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8456 tooltip : 'This field is required'
8461 cls : 'control-label col-sm-' + this.labelWidth,
8462 html : this.fieldLabel
8466 cls : "col-sm-" + (12 - this.labelWidth),
8474 if(this.indicatorpos == 'right'){
8479 cls : 'control-label col-sm-' + this.labelWidth,
8480 html : this.fieldLabel
8485 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8486 tooltip : 'This field is required'
8489 cls : "col-sm-" + (12 - this.labelWidth),
8498 } else if ( this.fieldLabel.length) {
8503 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8504 tooltip : 'This field is required'
8508 //cls : 'input-group-addon',
8509 html : this.fieldLabel
8517 if(this.indicatorpos == 'right'){
8522 //cls : 'input-group-addon',
8523 html : this.fieldLabel
8528 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8529 tooltip : 'This field is required'
8549 if (this.parentType === 'Navbar' && this.parent().bar) {
8550 cfg.cls += ' navbar-form';
8553 if (this.parentType === 'NavGroup') {
8554 cfg.cls += ' navbar-form';
8562 * return the real input element.
8564 inputEl: function ()
8566 return this.el.select('input.form-control',true).first();
8569 tooltipEl : function()
8571 return this.inputEl();
8574 indicatorEl : function()
8576 var indicator = this.el.select('i.roo-required-indicator',true).first();
8586 setDisabled : function(v)
8588 var i = this.inputEl().dom;
8590 i.removeAttribute('disabled');
8594 i.setAttribute('disabled','true');
8596 initEvents : function()
8599 this.inputEl().on("keydown" , this.fireKey, this);
8600 this.inputEl().on("focus", this.onFocus, this);
8601 this.inputEl().on("blur", this.onBlur, this);
8603 this.inputEl().relayEvent('keyup', this);
8605 this.indicator = this.indicatorEl();
8608 this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8609 this.indicator.hide();
8612 // reference to original value for reset
8613 this.originalValue = this.getValue();
8614 //Roo.form.TextField.superclass.initEvents.call(this);
8615 if(this.validationEvent == 'keyup'){
8616 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8617 this.inputEl().on('keyup', this.filterValidation, this);
8619 else if(this.validationEvent !== false){
8620 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8623 if(this.selectOnFocus){
8624 this.on("focus", this.preFocus, this);
8627 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8628 this.inputEl().on("keypress", this.filterKeys, this);
8630 this.inputEl().relayEvent('keypress', this);
8633 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
8634 this.el.on("click", this.autoSize, this);
8637 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8638 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8641 if (typeof(this.before) == 'object') {
8642 this.before.render(this.el.select('.roo-input-before',true).first());
8644 if (typeof(this.after) == 'object') {
8645 this.after.render(this.el.select('.roo-input-after',true).first());
8650 filterValidation : function(e){
8651 if(!e.isNavKeyPress()){
8652 this.validationTask.delay(this.validationDelay);
8656 * Validates the field value
8657 * @return {Boolean} True if the value is valid, else false
8659 validate : function(){
8660 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8661 if(this.disabled || this.validateValue(this.getRawValue())){
8672 * Validates a value according to the field's validation rules and marks the field as invalid
8673 * if the validation fails
8674 * @param {Mixed} value The value to validate
8675 * @return {Boolean} True if the value is valid, else false
8677 validateValue : function(value){
8678 if(value.length < 1) { // if it's blank
8679 if(this.allowBlank){
8685 if(value.length < this.minLength){
8688 if(value.length > this.maxLength){
8692 var vt = Roo.form.VTypes;
8693 if(!vt[this.vtype](value, this)){
8697 if(typeof this.validator == "function"){
8698 var msg = this.validator(value);
8704 if(this.regex && !this.regex.test(value)){
8714 fireKey : function(e){
8715 //Roo.log('field ' + e.getKey());
8716 if(e.isNavKeyPress()){
8717 this.fireEvent("specialkey", this, e);
8720 focus : function (selectText){
8722 this.inputEl().focus();
8723 if(selectText === true){
8724 this.inputEl().dom.select();
8730 onFocus : function(){
8731 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8732 // this.el.addClass(this.focusClass);
8735 this.hasFocus = true;
8736 this.startValue = this.getValue();
8737 this.fireEvent("focus", this);
8741 beforeBlur : Roo.emptyFn,
8745 onBlur : function(){
8747 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8748 //this.el.removeClass(this.focusClass);
8750 this.hasFocus = false;
8751 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8754 var v = this.getValue();
8755 if(String(v) !== String(this.startValue)){
8756 this.fireEvent('change', this, v, this.startValue);
8758 this.fireEvent("blur", this);
8762 * Resets the current field value to the originally loaded value and clears any validation messages
8765 this.setValue(this.originalValue);
8769 * Returns the name of the field
8770 * @return {Mixed} name The name field
8772 getName: function(){
8776 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
8777 * @return {Mixed} value The field value
8779 getValue : function(){
8781 var v = this.inputEl().getValue();
8786 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
8787 * @return {Mixed} value The field value
8789 getRawValue : function(){
8790 var v = this.inputEl().getValue();
8796 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
8797 * @param {Mixed} value The value to set
8799 setRawValue : function(v){
8800 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8803 selectText : function(start, end){
8804 var v = this.getRawValue();
8806 start = start === undefined ? 0 : start;
8807 end = end === undefined ? v.length : end;
8808 var d = this.inputEl().dom;
8809 if(d.setSelectionRange){
8810 d.setSelectionRange(start, end);
8811 }else if(d.createTextRange){
8812 var range = d.createTextRange();
8813 range.moveStart("character", start);
8814 range.moveEnd("character", v.length-end);
8821 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
8822 * @param {Mixed} value The value to set
8824 setValue : function(v){
8827 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8833 processValue : function(value){
8834 if(this.stripCharsRe){
8835 var newValue = value.replace(this.stripCharsRe, '');
8836 if(newValue !== value){
8837 this.setRawValue(newValue);
8844 preFocus : function(){
8846 if(this.selectOnFocus){
8847 this.inputEl().dom.select();
8850 filterKeys : function(e){
8852 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8855 var c = e.getCharCode(), cc = String.fromCharCode(c);
8856 if(Roo.isIE && (e.isSpecialKey() || !cc)){
8859 if(!this.maskRe.test(cc)){
8864 * Clear any invalid styles/messages for this field
8866 clearInvalid : function(){
8868 if(!this.el || this.preventMark){ // not rendered
8873 this.indicator.hide();
8876 this.el.removeClass(this.invalidClass);
8878 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8880 var feedback = this.el.select('.form-control-feedback', true).first();
8883 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8888 this.fireEvent('valid', this);
8892 * Mark this field as valid
8894 markValid : function()
8896 if(!this.el || this.preventMark){ // not rendered
8900 this.el.removeClass([this.invalidClass, this.validClass]);
8902 var feedback = this.el.select('.form-control-feedback', true).first();
8905 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8912 if(this.allowBlank && !this.getRawValue().length){
8917 this.indicator.hide();
8920 this.el.addClass(this.validClass);
8922 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8924 var feedback = this.el.select('.form-control-feedback', true).first();
8927 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8928 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8933 this.fireEvent('valid', this);
8937 * Mark this field as invalid
8938 * @param {String} msg The validation message
8940 markInvalid : function(msg)
8942 if(!this.el || this.preventMark){ // not rendered
8946 this.el.removeClass([this.invalidClass, this.validClass]);
8948 var feedback = this.el.select('.form-control-feedback', true).first();
8951 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8958 if(this.allowBlank && !this.getRawValue().length){
8963 this.indicator.show();
8966 this.el.addClass(this.invalidClass);
8968 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8970 var feedback = this.el.select('.form-control-feedback', true).first();
8973 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8975 if(this.getValue().length || this.forceFeedback){
8976 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8983 this.fireEvent('invalid', this, msg);
8986 SafariOnKeyDown : function(event)
8988 // this is a workaround for a password hang bug on chrome/ webkit.
8989 if (this.inputEl().dom.type != 'password') {
8993 var isSelectAll = false;
8995 if(this.inputEl().dom.selectionEnd > 0){
8996 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8998 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8999 event.preventDefault();
9004 if(isSelectAll && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9006 event.preventDefault();
9007 // this is very hacky as keydown always get's upper case.
9009 var cc = String.fromCharCode(event.getCharCode());
9010 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
9014 adjustWidth : function(tag, w){
9015 tag = tag.toLowerCase();
9016 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9017 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9021 if(tag == 'textarea'){
9024 }else if(Roo.isOpera){
9028 if(tag == 'textarea'){
9047 * @class Roo.bootstrap.TextArea
9048 * @extends Roo.bootstrap.Input
9049 * Bootstrap TextArea class
9050 * @cfg {Number} cols Specifies the visible width of a text area
9051 * @cfg {Number} rows Specifies the visible number of lines in a text area
9052 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9053 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9054 * @cfg {string} html text
9057 * Create a new TextArea
9058 * @param {Object} config The config object
9061 Roo.bootstrap.TextArea = function(config){
9062 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9066 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
9076 getAutoCreate : function(){
9078 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9089 value : this.value || '',
9090 html: this.html || '',
9091 cls : 'form-control',
9092 placeholder : this.placeholder || ''
9096 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9097 input.maxLength = this.maxLength;
9101 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9105 input.cols = this.cols;
9108 if (this.readOnly) {
9109 input.readonly = true;
9113 input.name = this.name;
9117 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9121 ['xs','sm','md','lg'].map(function(size){
9122 if (settings[size]) {
9123 cfg.cls += ' col-' + size + '-' + settings[size];
9127 var inputblock = input;
9129 if(this.hasFeedback && !this.allowBlank){
9133 cls: 'glyphicon form-control-feedback'
9137 cls : 'has-feedback',
9146 if (this.before || this.after) {
9149 cls : 'input-group',
9153 inputblock.cn.push({
9155 cls : 'input-group-addon',
9160 inputblock.cn.push(input);
9162 if(this.hasFeedback && !this.allowBlank){
9163 inputblock.cls += ' has-feedback';
9164 inputblock.cn.push(feedback);
9168 inputblock.cn.push({
9170 cls : 'input-group-addon',
9177 if (align ==='left' && this.fieldLabel.length) {
9178 // Roo.log("left and has label");
9184 cls : 'control-label col-sm-' + this.labelWidth,
9185 html : this.fieldLabel
9189 cls : "col-sm-" + (12 - this.labelWidth),
9196 } else if ( this.fieldLabel.length) {
9197 // Roo.log(" label");
9202 //cls : 'input-group-addon',
9203 html : this.fieldLabel
9213 // Roo.log(" no label && no align");
9223 if (this.disabled) {
9224 input.disabled=true;
9231 * return the real textarea element.
9233 inputEl: function ()
9235 return this.el.select('textarea.form-control',true).first();
9239 * Clear any invalid styles/messages for this field
9241 clearInvalid : function()
9244 if(!this.el || this.preventMark){ // not rendered
9248 var label = this.el.select('label', true).first();
9249 var icon = this.el.select('i.fa-star', true).first();
9255 this.el.removeClass(this.invalidClass);
9257 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9259 var feedback = this.el.select('.form-control-feedback', true).first();
9262 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9267 this.fireEvent('valid', this);
9271 * Mark this field as valid
9273 markValid : function()
9275 if(!this.el || this.preventMark){ // not rendered
9279 this.el.removeClass([this.invalidClass, this.validClass]);
9281 var feedback = this.el.select('.form-control-feedback', true).first();
9284 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9287 if(this.disabled || this.allowBlank){
9291 var label = this.el.select('label', true).first();
9292 var icon = this.el.select('i.fa-star', true).first();
9298 this.el.addClass(this.validClass);
9300 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9302 var feedback = this.el.select('.form-control-feedback', true).first();
9305 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9306 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9311 this.fireEvent('valid', this);
9315 * Mark this field as invalid
9316 * @param {String} msg The validation message
9318 markInvalid : function(msg)
9320 if(!this.el || this.preventMark){ // not rendered
9324 this.el.removeClass([this.invalidClass, this.validClass]);
9326 var feedback = this.el.select('.form-control-feedback', true).first();
9329 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9332 if(this.disabled || this.allowBlank){
9336 var label = this.el.select('label', true).first();
9337 var icon = this.el.select('i.fa-star', true).first();
9339 if(!this.getValue().length && label && !icon){
9340 this.el.createChild({
9342 cls : 'text-danger fa fa-lg fa-star',
9343 tooltip : 'This field is required',
9344 style : 'margin-right:5px;'
9348 this.el.addClass(this.invalidClass);
9350 if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9352 var feedback = this.el.select('.form-control-feedback', true).first();
9355 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9357 if(this.getValue().length || this.forceFeedback){
9358 this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9365 this.fireEvent('invalid', this, msg);
9373 * trigger field - base class for combo..
9378 * @class Roo.bootstrap.TriggerField
9379 * @extends Roo.bootstrap.Input
9380 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9381 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9382 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9383 * for which you can provide a custom implementation. For example:
9385 var trigger = new Roo.bootstrap.TriggerField();
9386 trigger.onTriggerClick = myTriggerFn;
9387 trigger.applyTo('my-field');
9390 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9391 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9392 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
9393 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9394 * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9397 * Create a new TriggerField.
9398 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9399 * to the base TextField)
9401 Roo.bootstrap.TriggerField = function(config){
9402 this.mimicing = false;
9403 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9406 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
9408 * @cfg {String} triggerClass A CSS class to apply to the trigger
9411 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9416 * @cfg {Boolean} removable (true|false) special filter default false
9420 /** @cfg {Boolean} grow @hide */
9421 /** @cfg {Number} growMin @hide */
9422 /** @cfg {Number} growMax @hide */
9428 autoSize: Roo.emptyFn,
9435 actionMode : 'wrap',
9440 getAutoCreate : function(){
9442 var align = this.labelAlign || this.parentLabelAlign();
9447 cls: 'form-group' //input-group
9454 type : this.inputType,
9455 cls : 'form-control',
9456 autocomplete: 'new-password',
9457 placeholder : this.placeholder || ''
9461 input.name = this.name;
9464 input.cls += ' input-' + this.size;
9467 if (this.disabled) {
9468 input.disabled=true;
9471 var inputblock = input;
9473 if(this.hasFeedback && !this.allowBlank){
9477 cls: 'glyphicon form-control-feedback'
9480 if(this.removable && !this.editable && !this.tickable){
9482 cls : 'has-feedback',
9488 cls : 'roo-combo-removable-btn close'
9495 cls : 'has-feedback',
9504 if(this.removable && !this.editable && !this.tickable){
9506 cls : 'roo-removable',
9512 cls : 'roo-combo-removable-btn close'
9519 if (this.before || this.after) {
9522 cls : 'input-group',
9526 inputblock.cn.push({
9528 cls : 'input-group-addon',
9533 inputblock.cn.push(input);
9535 if(this.hasFeedback && !this.allowBlank){
9536 inputblock.cls += ' has-feedback';
9537 inputblock.cn.push(feedback);
9541 inputblock.cn.push({
9543 cls : 'input-group-addon',
9556 cls: 'form-hidden-field'
9570 cls: 'form-hidden-field'
9574 cls: 'roo-select2-choices',
9578 cls: 'roo-select2-search-field',
9591 cls: 'roo-select2-container input-group',
9596 // cls: 'typeahead typeahead-long dropdown-menu',
9597 // style: 'display:none'
9602 if(!this.multiple && this.showToggleBtn){
9608 if (this.caret != false) {
9611 cls: 'fa fa-' + this.caret
9618 cls : 'input-group-addon btn dropdown-toggle',
9623 cls: 'combobox-clear',
9637 combobox.cls += ' roo-select2-container-multi';
9640 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9642 // Roo.log("left and has label");
9646 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9647 tooltip : 'This field is required'
9652 cls : 'control-label col-sm-' + this.labelWidth,
9653 html : this.fieldLabel
9657 cls : "col-sm-" + (12 - this.labelWidth),
9665 if(this.indicatorpos == 'right'){
9670 cls : 'control-label col-sm-' + this.labelWidth,
9671 html : this.fieldLabel
9676 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9677 tooltip : 'This field is required'
9680 cls : "col-sm-" + (12 - this.labelWidth),
9689 } else if ( this.fieldLabel.length) {
9690 // Roo.log(" label");
9694 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9695 tooltip : 'This field is required'
9699 //cls : 'input-group-addon',
9700 html : this.fieldLabel
9708 if(this.indicatorpos == 'right'){
9713 //cls : 'input-group-addon',
9714 html : this.fieldLabel
9719 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9720 tooltip : 'This field is required'
9731 // Roo.log(" no label && no align");
9738 ['xs','sm','md','lg'].map(function(size){
9739 if (settings[size]) {
9740 cfg.cls += ' col-' + size + '-' + settings[size];
9751 onResize : function(w, h){
9752 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9753 // if(typeof w == 'number'){
9754 // var x = w - this.trigger.getWidth();
9755 // this.inputEl().setWidth(this.adjustWidth('input', x));
9756 // this.trigger.setStyle('left', x+'px');
9761 adjustSize : Roo.BoxComponent.prototype.adjustSize,
9764 getResizeEl : function(){
9765 return this.inputEl();
9769 getPositionEl : function(){
9770 return this.inputEl();
9774 alignErrorIcon : function(){
9775 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9779 initEvents : function(){
9783 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9784 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9785 if(!this.multiple && this.showToggleBtn){
9786 this.trigger = this.el.select('span.dropdown-toggle',true).first();
9787 if(this.hideTrigger){
9788 this.trigger.setDisplayed(false);
9790 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9794 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9797 if(this.removable && !this.editable && !this.tickable){
9798 var close = this.closeTriggerEl();
9801 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9802 close.on('click', this.removeBtnClick, this, close);
9806 //this.trigger.addClassOnOver('x-form-trigger-over');
9807 //this.trigger.addClassOnClick('x-form-trigger-click');
9810 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9814 closeTriggerEl : function()
9816 var close = this.el.select('.roo-combo-removable-btn', true).first();
9817 return close ? close : false;
9820 removeBtnClick : function(e, h, el)
9824 if(this.fireEvent("remove", this) !== false){
9826 this.fireEvent("afterremove", this)
9830 createList : function()
9832 this.list = Roo.get(document.body).createChild({
9834 cls: 'typeahead typeahead-long dropdown-menu',
9835 style: 'display:none'
9838 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9843 initTrigger : function(){
9848 onDestroy : function(){
9850 this.trigger.removeAllListeners();
9851 // this.trigger.remove();
9854 // this.wrap.remove();
9856 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9860 onFocus : function(){
9861 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9864 this.wrap.addClass('x-trigger-wrap-focus');
9865 this.mimicing = true;
9866 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9867 if(this.monitorTab){
9868 this.el.on("keydown", this.checkTab, this);
9875 checkTab : function(e){
9876 if(e.getKey() == e.TAB){
9882 onBlur : function(){
9887 mimicBlur : function(e, t){
9889 if(!this.wrap.contains(t) && this.validateBlur()){
9896 triggerBlur : function(){
9897 this.mimicing = false;
9898 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9899 if(this.monitorTab){
9900 this.el.un("keydown", this.checkTab, this);
9902 //this.wrap.removeClass('x-trigger-wrap-focus');
9903 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9907 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9908 validateBlur : function(e, t){
9913 onDisable : function(){
9914 this.inputEl().dom.disabled = true;
9915 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9917 // this.wrap.addClass('x-item-disabled');
9922 onEnable : function(){
9923 this.inputEl().dom.disabled = false;
9924 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9926 // this.el.removeClass('x-item-disabled');
9931 onShow : function(){
9932 var ae = this.getActionEl();
9935 ae.dom.style.display = '';
9936 ae.dom.style.visibility = 'visible';
9942 onHide : function(){
9943 var ae = this.getActionEl();
9944 ae.dom.style.display = 'none';
9948 * The function that should handle the trigger's click event. This method does nothing by default until overridden
9949 * by an implementing function.
9951 * @param {EventObject} e
9953 onTriggerClick : Roo.emptyFn
9957 * Ext JS Library 1.1.1
9958 * Copyright(c) 2006-2007, Ext JS, LLC.
9960 * Originally Released Under LGPL - original licence link has changed is not relivant.
9963 * <script type="text/javascript">
9968 * @class Roo.data.SortTypes
9970 * Defines the default sorting (casting?) comparison functions used when sorting data.
9972 Roo.data.SortTypes = {
9974 * Default sort that does nothing
9975 * @param {Mixed} s The value being converted
9976 * @return {Mixed} The comparison value
9983 * The regular expression used to strip tags
9987 stripTagsRE : /<\/?[^>]+>/gi,
9990 * Strips all HTML tags to sort on text only
9991 * @param {Mixed} s The value being converted
9992 * @return {String} The comparison value
9994 asText : function(s){
9995 return String(s).replace(this.stripTagsRE, "");
9999 * Strips all HTML tags to sort on text only - Case insensitive
10000 * @param {Mixed} s The value being converted
10001 * @return {String} The comparison value
10003 asUCText : function(s){
10004 return String(s).toUpperCase().replace(this.stripTagsRE, "");
10008 * Case insensitive string
10009 * @param {Mixed} s The value being converted
10010 * @return {String} The comparison value
10012 asUCString : function(s) {
10013 return String(s).toUpperCase();
10018 * @param {Mixed} s The value being converted
10019 * @return {Number} The comparison value
10021 asDate : function(s) {
10025 if(s instanceof Date){
10026 return s.getTime();
10028 return Date.parse(String(s));
10033 * @param {Mixed} s The value being converted
10034 * @return {Float} The comparison value
10036 asFloat : function(s) {
10037 var val = parseFloat(String(s).replace(/,/g, ""));
10046 * @param {Mixed} s The value being converted
10047 * @return {Number} The comparison value
10049 asInt : function(s) {
10050 var val = parseInt(String(s).replace(/,/g, ""));
10058 * Ext JS Library 1.1.1
10059 * Copyright(c) 2006-2007, Ext JS, LLC.
10061 * Originally Released Under LGPL - original licence link has changed is not relivant.
10064 * <script type="text/javascript">
10068 * @class Roo.data.Record
10069 * Instances of this class encapsulate both record <em>definition</em> information, and record
10070 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10071 * to access Records cached in an {@link Roo.data.Store} object.<br>
10073 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10074 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10077 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10079 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10080 * {@link #create}. The parameters are the same.
10081 * @param {Array} data An associative Array of data values keyed by the field name.
10082 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10083 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10084 * not specified an integer id is generated.
10086 Roo.data.Record = function(data, id){
10087 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10092 * Generate a constructor for a specific record layout.
10093 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10094 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10095 * Each field definition object may contain the following properties: <ul>
10096 * <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,
10097 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10098 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10099 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10100 * is being used, then this is a string containing the javascript expression to reference the data relative to
10101 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10102 * to the data item relative to the record element. If the mapping expression is the same as the field name,
10103 * this may be omitted.</p></li>
10104 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10105 * <ul><li>auto (Default, implies no conversion)</li>
10110 * <li>date</li></ul></p></li>
10111 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10112 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10113 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10114 * by the Reader into an object that will be stored in the Record. It is passed the
10115 * following parameters:<ul>
10116 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10118 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10120 * <br>usage:<br><pre><code>
10121 var TopicRecord = Roo.data.Record.create(
10122 {name: 'title', mapping: 'topic_title'},
10123 {name: 'author', mapping: 'username'},
10124 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10125 {name: 'lastPost', mapping: 'post_time', type: 'date'},
10126 {name: 'lastPoster', mapping: 'user2'},
10127 {name: 'excerpt', mapping: 'post_text'}
10130 var myNewRecord = new TopicRecord({
10131 title: 'Do my job please',
10134 lastPost: new Date(),
10135 lastPoster: 'Animal',
10136 excerpt: 'No way dude!'
10138 myStore.add(myNewRecord);
10143 Roo.data.Record.create = function(o){
10144 var f = function(){
10145 f.superclass.constructor.apply(this, arguments);
10147 Roo.extend(f, Roo.data.Record);
10148 var p = f.prototype;
10149 p.fields = new Roo.util.MixedCollection(false, function(field){
10152 for(var i = 0, len = o.length; i < len; i++){
10153 p.fields.add(new Roo.data.Field(o[i]));
10155 f.getField = function(name){
10156 return p.fields.get(name);
10161 Roo.data.Record.AUTO_ID = 1000;
10162 Roo.data.Record.EDIT = 'edit';
10163 Roo.data.Record.REJECT = 'reject';
10164 Roo.data.Record.COMMIT = 'commit';
10166 Roo.data.Record.prototype = {
10168 * Readonly flag - true if this record has been modified.
10177 join : function(store){
10178 this.store = store;
10182 * Set the named field to the specified value.
10183 * @param {String} name The name of the field to set.
10184 * @param {Object} value The value to set the field to.
10186 set : function(name, value){
10187 if(this.data[name] == value){
10191 if(!this.modified){
10192 this.modified = {};
10194 if(typeof this.modified[name] == 'undefined'){
10195 this.modified[name] = this.data[name];
10197 this.data[name] = value;
10198 if(!this.editing && this.store){
10199 this.store.afterEdit(this);
10204 * Get the value of the named field.
10205 * @param {String} name The name of the field to get the value of.
10206 * @return {Object} The value of the field.
10208 get : function(name){
10209 return this.data[name];
10213 beginEdit : function(){
10214 this.editing = true;
10215 this.modified = {};
10219 cancelEdit : function(){
10220 this.editing = false;
10221 delete this.modified;
10225 endEdit : function(){
10226 this.editing = false;
10227 if(this.dirty && this.store){
10228 this.store.afterEdit(this);
10233 * Usually called by the {@link Roo.data.Store} which owns the Record.
10234 * Rejects all changes made to the Record since either creation, or the last commit operation.
10235 * Modified fields are reverted to their original values.
10237 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10238 * of reject operations.
10240 reject : function(){
10241 var m = this.modified;
10243 if(typeof m[n] != "function"){
10244 this.data[n] = m[n];
10247 this.dirty = false;
10248 delete this.modified;
10249 this.editing = false;
10251 this.store.afterReject(this);
10256 * Usually called by the {@link Roo.data.Store} which owns the Record.
10257 * Commits all changes made to the Record since either creation, or the last commit operation.
10259 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10260 * of commit operations.
10262 commit : function(){
10263 this.dirty = false;
10264 delete this.modified;
10265 this.editing = false;
10267 this.store.afterCommit(this);
10272 hasError : function(){
10273 return this.error != null;
10277 clearError : function(){
10282 * Creates a copy of this record.
10283 * @param {String} id (optional) A new record id if you don't want to use this record's id
10286 copy : function(newId) {
10287 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10291 * Ext JS Library 1.1.1
10292 * Copyright(c) 2006-2007, Ext JS, LLC.
10294 * Originally Released Under LGPL - original licence link has changed is not relivant.
10297 * <script type="text/javascript">
10303 * @class Roo.data.Store
10304 * @extends Roo.util.Observable
10305 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10306 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10308 * 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
10309 * has no knowledge of the format of the data returned by the Proxy.<br>
10311 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10312 * instances from the data object. These records are cached and made available through accessor functions.
10314 * Creates a new Store.
10315 * @param {Object} config A config object containing the objects needed for the Store to access data,
10316 * and read the data into Records.
10318 Roo.data.Store = function(config){
10319 this.data = new Roo.util.MixedCollection(false);
10320 this.data.getKey = function(o){
10323 this.baseParams = {};
10325 this.paramNames = {
10330 "multisort" : "_multisort"
10333 if(config && config.data){
10334 this.inlineData = config.data;
10335 delete config.data;
10338 Roo.apply(this, config);
10340 if(this.reader){ // reader passed
10341 this.reader = Roo.factory(this.reader, Roo.data);
10342 this.reader.xmodule = this.xmodule || false;
10343 if(!this.recordType){
10344 this.recordType = this.reader.recordType;
10346 if(this.reader.onMetaChange){
10347 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10351 if(this.recordType){
10352 this.fields = this.recordType.prototype.fields;
10354 this.modified = [];
10358 * @event datachanged
10359 * Fires when the data cache has changed, and a widget which is using this Store
10360 * as a Record cache should refresh its view.
10361 * @param {Store} this
10363 datachanged : true,
10365 * @event metachange
10366 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10367 * @param {Store} this
10368 * @param {Object} meta The JSON metadata
10373 * Fires when Records have been added to the Store
10374 * @param {Store} this
10375 * @param {Roo.data.Record[]} records The array of Records added
10376 * @param {Number} index The index at which the record(s) were added
10381 * Fires when a Record has been removed from the Store
10382 * @param {Store} this
10383 * @param {Roo.data.Record} record The Record that was removed
10384 * @param {Number} index The index at which the record was removed
10389 * Fires when a Record has been updated
10390 * @param {Store} this
10391 * @param {Roo.data.Record} record The Record that was updated
10392 * @param {String} operation The update operation being performed. Value may be one of:
10394 Roo.data.Record.EDIT
10395 Roo.data.Record.REJECT
10396 Roo.data.Record.COMMIT
10402 * Fires when the data cache has been cleared.
10403 * @param {Store} this
10407 * @event beforeload
10408 * Fires before a request is made for a new data object. If the beforeload handler returns false
10409 * the load action will be canceled.
10410 * @param {Store} this
10411 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10415 * @event beforeloadadd
10416 * Fires after a new set of Records has been loaded.
10417 * @param {Store} this
10418 * @param {Roo.data.Record[]} records The Records that were loaded
10419 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10421 beforeloadadd : true,
10424 * Fires after a new set of Records has been loaded, before they are added to the store.
10425 * @param {Store} this
10426 * @param {Roo.data.Record[]} records The Records that were loaded
10427 * @param {Object} options The loading options that were specified (see {@link #load} for details)
10428 * @params {Object} return from reader
10432 * @event loadexception
10433 * Fires if an exception occurs in the Proxy during loading.
10434 * Called with the signature of the Proxy's "loadexception" event.
10435 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10438 * @param {Object} return from JsonData.reader() - success, totalRecords, records
10439 * @param {Object} load options
10440 * @param {Object} jsonData from your request (normally this contains the Exception)
10442 loadexception : true
10446 this.proxy = Roo.factory(this.proxy, Roo.data);
10447 this.proxy.xmodule = this.xmodule || false;
10448 this.relayEvents(this.proxy, ["loadexception"]);
10450 this.sortToggle = {};
10451 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10453 Roo.data.Store.superclass.constructor.call(this);
10455 if(this.inlineData){
10456 this.loadData(this.inlineData);
10457 delete this.inlineData;
10461 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10463 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
10464 * without a remote query - used by combo/forms at present.
10468 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10471 * @cfg {Array} data Inline data to be loaded when the store is initialized.
10474 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10475 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10478 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10479 * on any HTTP request
10482 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10485 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10489 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10490 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10492 remoteSort : false,
10495 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10496 * loaded or when a record is removed. (defaults to false).
10498 pruneModifiedRecords : false,
10501 lastOptions : null,
10504 * Add Records to the Store and fires the add event.
10505 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10507 add : function(records){
10508 records = [].concat(records);
10509 for(var i = 0, len = records.length; i < len; i++){
10510 records[i].join(this);
10512 var index = this.data.length;
10513 this.data.addAll(records);
10514 this.fireEvent("add", this, records, index);
10518 * Remove a Record from the Store and fires the remove event.
10519 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10521 remove : function(record){
10522 var index = this.data.indexOf(record);
10523 this.data.removeAt(index);
10524 if(this.pruneModifiedRecords){
10525 this.modified.remove(record);
10527 this.fireEvent("remove", this, record, index);
10531 * Remove all Records from the Store and fires the clear event.
10533 removeAll : function(){
10535 if(this.pruneModifiedRecords){
10536 this.modified = [];
10538 this.fireEvent("clear", this);
10542 * Inserts Records to the Store at the given index and fires the add event.
10543 * @param {Number} index The start index at which to insert the passed Records.
10544 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10546 insert : function(index, records){
10547 records = [].concat(records);
10548 for(var i = 0, len = records.length; i < len; i++){
10549 this.data.insert(index, records[i]);
10550 records[i].join(this);
10552 this.fireEvent("add", this, records, index);
10556 * Get the index within the cache of the passed Record.
10557 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10558 * @return {Number} The index of the passed Record. Returns -1 if not found.
10560 indexOf : function(record){
10561 return this.data.indexOf(record);
10565 * Get the index within the cache of the Record with the passed id.
10566 * @param {String} id The id of the Record to find.
10567 * @return {Number} The index of the Record. Returns -1 if not found.
10569 indexOfId : function(id){
10570 return this.data.indexOfKey(id);
10574 * Get the Record with the specified id.
10575 * @param {String} id The id of the Record to find.
10576 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10578 getById : function(id){
10579 return this.data.key(id);
10583 * Get the Record at the specified index.
10584 * @param {Number} index The index of the Record to find.
10585 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10587 getAt : function(index){
10588 return this.data.itemAt(index);
10592 * Returns a range of Records between specified indices.
10593 * @param {Number} startIndex (optional) The starting index (defaults to 0)
10594 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10595 * @return {Roo.data.Record[]} An array of Records
10597 getRange : function(start, end){
10598 return this.data.getRange(start, end);
10602 storeOptions : function(o){
10603 o = Roo.apply({}, o);
10606 this.lastOptions = o;
10610 * Loads the Record cache from the configured Proxy using the configured Reader.
10612 * If using remote paging, then the first load call must specify the <em>start</em>
10613 * and <em>limit</em> properties in the options.params property to establish the initial
10614 * position within the dataset, and the number of Records to cache on each read from the Proxy.
10616 * <strong>It is important to note that for remote data sources, loading is asynchronous,
10617 * and this call will return before the new data has been loaded. Perform any post-processing
10618 * in a callback function, or in a "load" event handler.</strong>
10620 * @param {Object} options An object containing properties which control loading options:<ul>
10621 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10622 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10623 * passed the following arguments:<ul>
10624 * <li>r : Roo.data.Record[]</li>
10625 * <li>options: Options object from the load call</li>
10626 * <li>success: Boolean success indicator</li></ul></li>
10627 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10628 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10631 load : function(options){
10632 options = options || {};
10633 if(this.fireEvent("beforeload", this, options) !== false){
10634 this.storeOptions(options);
10635 var p = Roo.apply(options.params || {}, this.baseParams);
10636 // if meta was not loaded from remote source.. try requesting it.
10637 if (!this.reader.metaFromRemote) {
10638 p._requestMeta = 1;
10640 if(this.sortInfo && this.remoteSort){
10641 var pn = this.paramNames;
10642 p[pn["sort"]] = this.sortInfo.field;
10643 p[pn["dir"]] = this.sortInfo.direction;
10645 if (this.multiSort) {
10646 var pn = this.paramNames;
10647 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10650 this.proxy.load(p, this.reader, this.loadRecords, this, options);
10655 * Reloads the Record cache from the configured Proxy using the configured Reader and
10656 * the options from the last load operation performed.
10657 * @param {Object} options (optional) An object containing properties which may override the options
10658 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10659 * the most recently used options are reused).
10661 reload : function(options){
10662 this.load(Roo.applyIf(options||{}, this.lastOptions));
10666 // Called as a callback by the Reader during a load operation.
10667 loadRecords : function(o, options, success){
10668 if(!o || success === false){
10669 if(success !== false){
10670 this.fireEvent("load", this, [], options, o);
10672 if(options.callback){
10673 options.callback.call(options.scope || this, [], options, false);
10677 // if data returned failure - throw an exception.
10678 if (o.success === false) {
10679 // show a message if no listener is registered.
10680 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10681 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10683 // loadmask wil be hooked into this..
10684 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10687 var r = o.records, t = o.totalRecords || r.length;
10689 this.fireEvent("beforeloadadd", this, r, options, o);
10691 if(!options || options.add !== true){
10692 if(this.pruneModifiedRecords){
10693 this.modified = [];
10695 for(var i = 0, len = r.length; i < len; i++){
10699 this.data = this.snapshot;
10700 delete this.snapshot;
10703 this.data.addAll(r);
10704 this.totalLength = t;
10706 this.fireEvent("datachanged", this);
10708 this.totalLength = Math.max(t, this.data.length+r.length);
10711 this.fireEvent("load", this, r, options, o);
10712 if(options.callback){
10713 options.callback.call(options.scope || this, r, options, true);
10719 * Loads data from a passed data block. A Reader which understands the format of the data
10720 * must have been configured in the constructor.
10721 * @param {Object} data The data block from which to read the Records. The format of the data expected
10722 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10723 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10725 loadData : function(o, append){
10726 var r = this.reader.readRecords(o);
10727 this.loadRecords(r, {add: append}, true);
10731 * Gets the number of cached records.
10733 * <em>If using paging, this may not be the total size of the dataset. If the data object
10734 * used by the Reader contains the dataset size, then the getTotalCount() function returns
10735 * the data set size</em>
10737 getCount : function(){
10738 return this.data.length || 0;
10742 * Gets the total number of records in the dataset as returned by the server.
10744 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10745 * the dataset size</em>
10747 getTotalCount : function(){
10748 return this.totalLength || 0;
10752 * Returns the sort state of the Store as an object with two properties:
10754 field {String} The name of the field by which the Records are sorted
10755 direction {String} The sort order, "ASC" or "DESC"
10758 getSortState : function(){
10759 return this.sortInfo;
10763 applySort : function(){
10764 if(this.sortInfo && !this.remoteSort){
10765 var s = this.sortInfo, f = s.field;
10766 var st = this.fields.get(f).sortType;
10767 var fn = function(r1, r2){
10768 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10769 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10771 this.data.sort(s.direction, fn);
10772 if(this.snapshot && this.snapshot != this.data){
10773 this.snapshot.sort(s.direction, fn);
10779 * Sets the default sort column and order to be used by the next load operation.
10780 * @param {String} fieldName The name of the field to sort by.
10781 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10783 setDefaultSort : function(field, dir){
10784 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10788 * Sort the Records.
10789 * If remote sorting is used, the sort is performed on the server, and the cache is
10790 * reloaded. If local sorting is used, the cache is sorted internally.
10791 * @param {String} fieldName The name of the field to sort by.
10792 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10794 sort : function(fieldName, dir){
10795 var f = this.fields.get(fieldName);
10797 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10799 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10800 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10805 this.sortToggle[f.name] = dir;
10806 this.sortInfo = {field: f.name, direction: dir};
10807 if(!this.remoteSort){
10809 this.fireEvent("datachanged", this);
10811 this.load(this.lastOptions);
10816 * Calls the specified function for each of the Records in the cache.
10817 * @param {Function} fn The function to call. The Record is passed as the first parameter.
10818 * Returning <em>false</em> aborts and exits the iteration.
10819 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10821 each : function(fn, scope){
10822 this.data.each(fn, scope);
10826 * Gets all records modified since the last commit. Modified records are persisted across load operations
10827 * (e.g., during paging).
10828 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10830 getModifiedRecords : function(){
10831 return this.modified;
10835 createFilterFn : function(property, value, anyMatch){
10836 if(!value.exec){ // not a regex
10837 value = String(value);
10838 if(value.length == 0){
10841 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10843 return function(r){
10844 return value.test(r.data[property]);
10849 * Sums the value of <i>property</i> for each record between start and end and returns the result.
10850 * @param {String} property A field on your records
10851 * @param {Number} start The record index to start at (defaults to 0)
10852 * @param {Number} end The last record index to include (defaults to length - 1)
10853 * @return {Number} The sum
10855 sum : function(property, start, end){
10856 var rs = this.data.items, v = 0;
10857 start = start || 0;
10858 end = (end || end === 0) ? end : rs.length-1;
10860 for(var i = start; i <= end; i++){
10861 v += (rs[i].data[property] || 0);
10867 * Filter the records by a specified property.
10868 * @param {String} field A field on your records
10869 * @param {String/RegExp} value Either a string that the field
10870 * should start with or a RegExp to test against the field
10871 * @param {Boolean} anyMatch True to match any part not just the beginning
10873 filter : function(property, value, anyMatch){
10874 var fn = this.createFilterFn(property, value, anyMatch);
10875 return fn ? this.filterBy(fn) : this.clearFilter();
10879 * Filter by a function. The specified function will be called with each
10880 * record in this data source. If the function returns true the record is included,
10881 * otherwise it is filtered.
10882 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10883 * @param {Object} scope (optional) The scope of the function (defaults to this)
10885 filterBy : function(fn, scope){
10886 this.snapshot = this.snapshot || this.data;
10887 this.data = this.queryBy(fn, scope||this);
10888 this.fireEvent("datachanged", this);
10892 * Query the records by a specified property.
10893 * @param {String} field A field on your records
10894 * @param {String/RegExp} value Either a string that the field
10895 * should start with or a RegExp to test against the field
10896 * @param {Boolean} anyMatch True to match any part not just the beginning
10897 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10899 query : function(property, value, anyMatch){
10900 var fn = this.createFilterFn(property, value, anyMatch);
10901 return fn ? this.queryBy(fn) : this.data.clone();
10905 * Query by a function. The specified function will be called with each
10906 * record in this data source. If the function returns true the record is included
10908 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10909 * @param {Object} scope (optional) The scope of the function (defaults to this)
10910 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10912 queryBy : function(fn, scope){
10913 var data = this.snapshot || this.data;
10914 return data.filterBy(fn, scope||this);
10918 * Collects unique values for a particular dataIndex from this store.
10919 * @param {String} dataIndex The property to collect
10920 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10921 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10922 * @return {Array} An array of the unique values
10924 collect : function(dataIndex, allowNull, bypassFilter){
10925 var d = (bypassFilter === true && this.snapshot) ?
10926 this.snapshot.items : this.data.items;
10927 var v, sv, r = [], l = {};
10928 for(var i = 0, len = d.length; i < len; i++){
10929 v = d[i].data[dataIndex];
10931 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10940 * Revert to a view of the Record cache with no filtering applied.
10941 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10943 clearFilter : function(suppressEvent){
10944 if(this.snapshot && this.snapshot != this.data){
10945 this.data = this.snapshot;
10946 delete this.snapshot;
10947 if(suppressEvent !== true){
10948 this.fireEvent("datachanged", this);
10954 afterEdit : function(record){
10955 if(this.modified.indexOf(record) == -1){
10956 this.modified.push(record);
10958 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10962 afterReject : function(record){
10963 this.modified.remove(record);
10964 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10968 afterCommit : function(record){
10969 this.modified.remove(record);
10970 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10974 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10975 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10977 commitChanges : function(){
10978 var m = this.modified.slice(0);
10979 this.modified = [];
10980 for(var i = 0, len = m.length; i < len; i++){
10986 * Cancel outstanding changes on all changed records.
10988 rejectChanges : function(){
10989 var m = this.modified.slice(0);
10990 this.modified = [];
10991 for(var i = 0, len = m.length; i < len; i++){
10996 onMetaChange : function(meta, rtype, o){
10997 this.recordType = rtype;
10998 this.fields = rtype.prototype.fields;
10999 delete this.snapshot;
11000 this.sortInfo = meta.sortInfo || this.sortInfo;
11001 this.modified = [];
11002 this.fireEvent('metachange', this, this.reader.meta);
11005 moveIndex : function(data, type)
11007 var index = this.indexOf(data);
11009 var newIndex = index + type;
11013 this.insert(newIndex, data);
11018 * Ext JS Library 1.1.1
11019 * Copyright(c) 2006-2007, Ext JS, LLC.
11021 * Originally Released Under LGPL - original licence link has changed is not relivant.
11024 * <script type="text/javascript">
11028 * @class Roo.data.SimpleStore
11029 * @extends Roo.data.Store
11030 * Small helper class to make creating Stores from Array data easier.
11031 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11032 * @cfg {Array} fields An array of field definition objects, or field name strings.
11033 * @cfg {Array} data The multi-dimensional array of data
11035 * @param {Object} config
11037 Roo.data.SimpleStore = function(config){
11038 Roo.data.SimpleStore.superclass.constructor.call(this, {
11040 reader: new Roo.data.ArrayReader({
11043 Roo.data.Record.create(config.fields)
11045 proxy : new Roo.data.MemoryProxy(config.data)
11049 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11051 * Ext JS Library 1.1.1
11052 * Copyright(c) 2006-2007, Ext JS, LLC.
11054 * Originally Released Under LGPL - original licence link has changed is not relivant.
11057 * <script type="text/javascript">
11062 * @extends Roo.data.Store
11063 * @class Roo.data.JsonStore
11064 * Small helper class to make creating Stores for JSON data easier. <br/>
11066 var store = new Roo.data.JsonStore({
11067 url: 'get-images.php',
11069 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11072 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11073 * JsonReader and HttpProxy (unless inline data is provided).</b>
11074 * @cfg {Array} fields An array of field definition objects, or field name strings.
11076 * @param {Object} config
11078 Roo.data.JsonStore = function(c){
11079 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11080 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11081 reader: new Roo.data.JsonReader(c, c.fields)
11084 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11086 * Ext JS Library 1.1.1
11087 * Copyright(c) 2006-2007, Ext JS, LLC.
11089 * Originally Released Under LGPL - original licence link has changed is not relivant.
11092 * <script type="text/javascript">
11096 Roo.data.Field = function(config){
11097 if(typeof config == "string"){
11098 config = {name: config};
11100 Roo.apply(this, config);
11103 this.type = "auto";
11106 var st = Roo.data.SortTypes;
11107 // named sortTypes are supported, here we look them up
11108 if(typeof this.sortType == "string"){
11109 this.sortType = st[this.sortType];
11112 // set default sortType for strings and dates
11113 if(!this.sortType){
11116 this.sortType = st.asUCString;
11119 this.sortType = st.asDate;
11122 this.sortType = st.none;
11127 var stripRe = /[\$,%]/g;
11129 // prebuilt conversion function for this field, instead of
11130 // switching every time we're reading a value
11132 var cv, dateFormat = this.dateFormat;
11137 cv = function(v){ return v; };
11140 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11144 return v !== undefined && v !== null && v !== '' ?
11145 parseInt(String(v).replace(stripRe, ""), 10) : '';
11150 return v !== undefined && v !== null && v !== '' ?
11151 parseFloat(String(v).replace(stripRe, ""), 10) : '';
11156 cv = function(v){ return v === true || v === "true" || v == 1; };
11163 if(v instanceof Date){
11167 if(dateFormat == "timestamp"){
11168 return new Date(v*1000);
11170 return Date.parseDate(v, dateFormat);
11172 var parsed = Date.parse(v);
11173 return parsed ? new Date(parsed) : null;
11182 Roo.data.Field.prototype = {
11190 * Ext JS Library 1.1.1
11191 * Copyright(c) 2006-2007, Ext JS, LLC.
11193 * Originally Released Under LGPL - original licence link has changed is not relivant.
11196 * <script type="text/javascript">
11199 // Base class for reading structured data from a data source. This class is intended to be
11200 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11203 * @class Roo.data.DataReader
11204 * Base class for reading structured data from a data source. This class is intended to be
11205 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11208 Roo.data.DataReader = function(meta, recordType){
11212 this.recordType = recordType instanceof Array ?
11213 Roo.data.Record.create(recordType) : recordType;
11216 Roo.data.DataReader.prototype = {
11218 * Create an empty record
11219 * @param {Object} data (optional) - overlay some values
11220 * @return {Roo.data.Record} record created.
11222 newRow : function(d) {
11224 this.recordType.prototype.fields.each(function(c) {
11226 case 'int' : da[c.name] = 0; break;
11227 case 'date' : da[c.name] = new Date(); break;
11228 case 'float' : da[c.name] = 0.0; break;
11229 case 'boolean' : da[c.name] = false; break;
11230 default : da[c.name] = ""; break;
11234 return new this.recordType(Roo.apply(da, d));
11239 * Ext JS Library 1.1.1
11240 * Copyright(c) 2006-2007, Ext JS, LLC.
11242 * Originally Released Under LGPL - original licence link has changed is not relivant.
11245 * <script type="text/javascript">
11249 * @class Roo.data.DataProxy
11250 * @extends Roo.data.Observable
11251 * This class is an abstract base class for implementations which provide retrieval of
11252 * unformatted data objects.<br>
11254 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11255 * (of the appropriate type which knows how to parse the data object) to provide a block of
11256 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11258 * Custom implementations must implement the load method as described in
11259 * {@link Roo.data.HttpProxy#load}.
11261 Roo.data.DataProxy = function(){
11264 * @event beforeload
11265 * Fires before a network request is made to retrieve a data object.
11266 * @param {Object} This DataProxy object.
11267 * @param {Object} params The params parameter to the load function.
11272 * Fires before the load method's callback is called.
11273 * @param {Object} This DataProxy object.
11274 * @param {Object} o The data object.
11275 * @param {Object} arg The callback argument object passed to the load function.
11279 * @event loadexception
11280 * Fires if an Exception occurs during data retrieval.
11281 * @param {Object} This DataProxy object.
11282 * @param {Object} o The data object.
11283 * @param {Object} arg The callback argument object passed to the load function.
11284 * @param {Object} e The Exception.
11286 loadexception : true
11288 Roo.data.DataProxy.superclass.constructor.call(this);
11291 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11294 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11298 * Ext JS Library 1.1.1
11299 * Copyright(c) 2006-2007, Ext JS, LLC.
11301 * Originally Released Under LGPL - original licence link has changed is not relivant.
11304 * <script type="text/javascript">
11307 * @class Roo.data.MemoryProxy
11308 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11309 * to the Reader when its load method is called.
11311 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11313 Roo.data.MemoryProxy = function(data){
11317 Roo.data.MemoryProxy.superclass.constructor.call(this);
11321 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11324 * Load data from the requested source (in this case an in-memory
11325 * data object passed to the constructor), read the data object into
11326 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11327 * process that block using the passed callback.
11328 * @param {Object} params This parameter is not used by the MemoryProxy class.
11329 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11330 * object into a block of Roo.data.Records.
11331 * @param {Function} callback The function into which to pass the block of Roo.data.records.
11332 * The function must be passed <ul>
11333 * <li>The Record block object</li>
11334 * <li>The "arg" argument from the load function</li>
11335 * <li>A boolean success indicator</li>
11337 * @param {Object} scope The scope in which to call the callback
11338 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11340 load : function(params, reader, callback, scope, arg){
11341 params = params || {};
11344 result = reader.readRecords(this.data);
11346 this.fireEvent("loadexception", this, arg, null, e);
11347 callback.call(scope, null, arg, false);
11350 callback.call(scope, result, arg, true);
11354 update : function(params, records){
11359 * Ext JS Library 1.1.1
11360 * Copyright(c) 2006-2007, Ext JS, LLC.
11362 * Originally Released Under LGPL - original licence link has changed is not relivant.
11365 * <script type="text/javascript">
11368 * @class Roo.data.HttpProxy
11369 * @extends Roo.data.DataProxy
11370 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11371 * configured to reference a certain URL.<br><br>
11373 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11374 * from which the running page was served.<br><br>
11376 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11378 * Be aware that to enable the browser to parse an XML document, the server must set
11379 * the Content-Type header in the HTTP response to "text/xml".
11381 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11382 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
11383 * will be used to make the request.
11385 Roo.data.HttpProxy = function(conn){
11386 Roo.data.HttpProxy.superclass.constructor.call(this);
11387 // is conn a conn config or a real conn?
11389 this.useAjax = !conn || !conn.events;
11393 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11394 // thse are take from connection...
11397 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11400 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11401 * extra parameters to each request made by this object. (defaults to undefined)
11404 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11405 * to each request made by this object. (defaults to undefined)
11408 * @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)
11411 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11414 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11420 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11424 * Return the {@link Roo.data.Connection} object being used by this Proxy.
11425 * @return {Connection} The Connection object. This object may be used to subscribe to events on
11426 * a finer-grained basis than the DataProxy events.
11428 getConnection : function(){
11429 return this.useAjax ? Roo.Ajax : this.conn;
11433 * Load data from the configured {@link Roo.data.Connection}, read the data object into
11434 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11435 * process that block using the passed callback.
11436 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11437 * for the request to the remote server.
11438 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11439 * object into a block of Roo.data.Records.
11440 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11441 * The function must be passed <ul>
11442 * <li>The Record block object</li>
11443 * <li>The "arg" argument from the load function</li>
11444 * <li>A boolean success indicator</li>
11446 * @param {Object} scope The scope in which to call the callback
11447 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11449 load : function(params, reader, callback, scope, arg){
11450 if(this.fireEvent("beforeload", this, params) !== false){
11452 params : params || {},
11454 callback : callback,
11459 callback : this.loadResponse,
11463 Roo.applyIf(o, this.conn);
11464 if(this.activeRequest){
11465 Roo.Ajax.abort(this.activeRequest);
11467 this.activeRequest = Roo.Ajax.request(o);
11469 this.conn.request(o);
11472 callback.call(scope||this, null, arg, false);
11477 loadResponse : function(o, success, response){
11478 delete this.activeRequest;
11480 this.fireEvent("loadexception", this, o, response);
11481 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11486 result = o.reader.read(response);
11488 this.fireEvent("loadexception", this, o, response, e);
11489 o.request.callback.call(o.request.scope, null, o.request.arg, false);
11493 this.fireEvent("load", this, o, o.request.arg);
11494 o.request.callback.call(o.request.scope, result, o.request.arg, true);
11498 update : function(dataSet){
11503 updateResponse : function(dataSet){
11508 * Ext JS Library 1.1.1
11509 * Copyright(c) 2006-2007, Ext JS, LLC.
11511 * Originally Released Under LGPL - original licence link has changed is not relivant.
11514 * <script type="text/javascript">
11518 * @class Roo.data.ScriptTagProxy
11519 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11520 * other than the originating domain of the running page.<br><br>
11522 * <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
11523 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11525 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11526 * source code that is used as the source inside a <script> tag.<br><br>
11528 * In order for the browser to process the returned data, the server must wrap the data object
11529 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11530 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11531 * depending on whether the callback name was passed:
11534 boolean scriptTag = false;
11535 String cb = request.getParameter("callback");
11538 response.setContentType("text/javascript");
11540 response.setContentType("application/x-json");
11542 Writer out = response.getWriter();
11544 out.write(cb + "(");
11546 out.print(dataBlock.toJsonString());
11553 * @param {Object} config A configuration object.
11555 Roo.data.ScriptTagProxy = function(config){
11556 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11557 Roo.apply(this, config);
11558 this.head = document.getElementsByTagName("head")[0];
11561 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11563 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11565 * @cfg {String} url The URL from which to request the data object.
11568 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11572 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11573 * the server the name of the callback function set up by the load call to process the returned data object.
11574 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11575 * javascript output which calls this named function passing the data object as its only parameter.
11577 callbackParam : "callback",
11579 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11580 * name to the request.
11585 * Load data from the configured URL, read the data object into
11586 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11587 * process that block using the passed callback.
11588 * @param {Object} params An object containing properties which are to be used as HTTP parameters
11589 * for the request to the remote server.
11590 * @param {Roo.data.DataReader} reader The Reader object which converts the data
11591 * object into a block of Roo.data.Records.
11592 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11593 * The function must be passed <ul>
11594 * <li>The Record block object</li>
11595 * <li>The "arg" argument from the load function</li>
11596 * <li>A boolean success indicator</li>
11598 * @param {Object} scope The scope in which to call the callback
11599 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11601 load : function(params, reader, callback, scope, arg){
11602 if(this.fireEvent("beforeload", this, params) !== false){
11604 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11606 var url = this.url;
11607 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11609 url += "&_dc=" + (new Date().getTime());
11611 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11614 cb : "stcCallback"+transId,
11615 scriptId : "stcScript"+transId,
11619 callback : callback,
11625 window[trans.cb] = function(o){
11626 conn.handleResponse(o, trans);
11629 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11631 if(this.autoAbort !== false){
11635 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11637 var script = document.createElement("script");
11638 script.setAttribute("src", url);
11639 script.setAttribute("type", "text/javascript");
11640 script.setAttribute("id", trans.scriptId);
11641 this.head.appendChild(script);
11643 this.trans = trans;
11645 callback.call(scope||this, null, arg, false);
11650 isLoading : function(){
11651 return this.trans ? true : false;
11655 * Abort the current server request.
11657 abort : function(){
11658 if(this.isLoading()){
11659 this.destroyTrans(this.trans);
11664 destroyTrans : function(trans, isLoaded){
11665 this.head.removeChild(document.getElementById(trans.scriptId));
11666 clearTimeout(trans.timeoutId);
11668 window[trans.cb] = undefined;
11670 delete window[trans.cb];
11673 // if hasn't been loaded, wait for load to remove it to prevent script error
11674 window[trans.cb] = function(){
11675 window[trans.cb] = undefined;
11677 delete window[trans.cb];
11684 handleResponse : function(o, trans){
11685 this.trans = false;
11686 this.destroyTrans(trans, true);
11689 result = trans.reader.readRecords(o);
11691 this.fireEvent("loadexception", this, o, trans.arg, e);
11692 trans.callback.call(trans.scope||window, null, trans.arg, false);
11695 this.fireEvent("load", this, o, trans.arg);
11696 trans.callback.call(trans.scope||window, result, trans.arg, true);
11700 handleFailure : function(trans){
11701 this.trans = false;
11702 this.destroyTrans(trans, false);
11703 this.fireEvent("loadexception", this, null, trans.arg);
11704 trans.callback.call(trans.scope||window, null, trans.arg, false);
11708 * Ext JS Library 1.1.1
11709 * Copyright(c) 2006-2007, Ext JS, LLC.
11711 * Originally Released Under LGPL - original licence link has changed is not relivant.
11714 * <script type="text/javascript">
11718 * @class Roo.data.JsonReader
11719 * @extends Roo.data.DataReader
11720 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11721 * based on mappings in a provided Roo.data.Record constructor.
11723 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11724 * in the reply previously.
11729 var RecordDef = Roo.data.Record.create([
11730 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
11731 {name: 'occupation'} // This field will use "occupation" as the mapping.
11733 var myReader = new Roo.data.JsonReader({
11734 totalProperty: "results", // The property which contains the total dataset size (optional)
11735 root: "rows", // The property which contains an Array of row objects
11736 id: "id" // The property within each row object that provides an ID for the record (optional)
11740 * This would consume a JSON file like this:
11742 { 'results': 2, 'rows': [
11743 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11744 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11747 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11748 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11749 * paged from the remote server.
11750 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11751 * @cfg {String} root name of the property which contains the Array of row objects.
11752 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11753 * @cfg {Array} fields Array of field definition objects
11755 * Create a new JsonReader
11756 * @param {Object} meta Metadata configuration options
11757 * @param {Object} recordType Either an Array of field definition objects,
11758 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11760 Roo.data.JsonReader = function(meta, recordType){
11763 // set some defaults:
11764 Roo.applyIf(meta, {
11765 totalProperty: 'total',
11766 successProperty : 'success',
11771 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11773 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11776 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
11777 * Used by Store query builder to append _requestMeta to params.
11780 metaFromRemote : false,
11782 * This method is only used by a DataProxy which has retrieved data from a remote server.
11783 * @param {Object} response The XHR object which contains the JSON data in its responseText.
11784 * @return {Object} data A data block which is used by an Roo.data.Store object as
11785 * a cache of Roo.data.Records.
11787 read : function(response){
11788 var json = response.responseText;
11790 var o = /* eval:var:o */ eval("("+json+")");
11792 throw {message: "JsonReader.read: Json object not found"};
11798 this.metaFromRemote = true;
11799 this.meta = o.metaData;
11800 this.recordType = Roo.data.Record.create(o.metaData.fields);
11801 this.onMetaChange(this.meta, this.recordType, o);
11803 return this.readRecords(o);
11806 // private function a store will implement
11807 onMetaChange : function(meta, recordType, o){
11814 simpleAccess: function(obj, subsc) {
11821 getJsonAccessor: function(){
11823 return function(expr) {
11825 return(re.test(expr))
11826 ? new Function("obj", "return obj." + expr)
11831 return Roo.emptyFn;
11836 * Create a data block containing Roo.data.Records from an XML document.
11837 * @param {Object} o An object which contains an Array of row objects in the property specified
11838 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11839 * which contains the total size of the dataset.
11840 * @return {Object} data A data block which is used by an Roo.data.Store object as
11841 * a cache of Roo.data.Records.
11843 readRecords : function(o){
11845 * After any data loads, the raw JSON data is available for further custom processing.
11849 var s = this.meta, Record = this.recordType,
11850 f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11852 // Generate extraction functions for the totalProperty, the root, the id, and for each field
11854 if(s.totalProperty) {
11855 this.getTotal = this.getJsonAccessor(s.totalProperty);
11857 if(s.successProperty) {
11858 this.getSuccess = this.getJsonAccessor(s.successProperty);
11860 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11862 var g = this.getJsonAccessor(s.id);
11863 this.getId = function(rec) {
11865 return (r === undefined || r === "") ? null : r;
11868 this.getId = function(){return null;};
11871 for(var jj = 0; jj < fl; jj++){
11873 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11874 this.ef[jj] = this.getJsonAccessor(map);
11878 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11879 if(s.totalProperty){
11880 var vt = parseInt(this.getTotal(o), 10);
11885 if(s.successProperty){
11886 var vs = this.getSuccess(o);
11887 if(vs === false || vs === 'false'){
11892 for(var i = 0; i < c; i++){
11895 var id = this.getId(n);
11896 for(var j = 0; j < fl; j++){
11898 var v = this.ef[j](n);
11900 Roo.log('missing convert for ' + f.name);
11904 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11906 var record = new Record(values, id);
11908 records[i] = record;
11914 totalRecords : totalRecords
11919 * Ext JS Library 1.1.1
11920 * Copyright(c) 2006-2007, Ext JS, LLC.
11922 * Originally Released Under LGPL - original licence link has changed is not relivant.
11925 * <script type="text/javascript">
11929 * @class Roo.data.ArrayReader
11930 * @extends Roo.data.DataReader
11931 * Data reader class to create an Array of Roo.data.Record objects from an Array.
11932 * Each element of that Array represents a row of data fields. The
11933 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11934 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11938 var RecordDef = Roo.data.Record.create([
11939 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
11940 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
11942 var myReader = new Roo.data.ArrayReader({
11943 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
11947 * This would consume an Array like this:
11949 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11951 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11953 * Create a new JsonReader
11954 * @param {Object} meta Metadata configuration options.
11955 * @param {Object} recordType Either an Array of field definition objects
11956 * as specified to {@link Roo.data.Record#create},
11957 * or an {@link Roo.data.Record} object
11958 * created using {@link Roo.data.Record#create}.
11960 Roo.data.ArrayReader = function(meta, recordType){
11961 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11964 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11966 * Create a data block containing Roo.data.Records from an XML document.
11967 * @param {Object} o An Array of row objects which represents the dataset.
11968 * @return {Object} data A data block which is used by an Roo.data.Store object as
11969 * a cache of Roo.data.Records.
11971 readRecords : function(o){
11972 var sid = this.meta ? this.meta.id : null;
11973 var recordType = this.recordType, fields = recordType.prototype.fields;
11976 for(var i = 0; i < root.length; i++){
11979 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11980 for(var j = 0, jlen = fields.length; j < jlen; j++){
11981 var f = fields.items[j];
11982 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11983 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11985 values[f.name] = v;
11987 var record = new recordType(values, id);
11989 records[records.length] = record;
11993 totalRecords : records.length
12002 * @class Roo.bootstrap.ComboBox
12003 * @extends Roo.bootstrap.TriggerField
12004 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12005 * @cfg {Boolean} append (true|false) default false
12006 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12007 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12008 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12009 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12010 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12011 * @cfg {Boolean} animate default true
12012 * @cfg {Boolean} emptyResultText only for touch device
12013 * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12015 * Create a new ComboBox.
12016 * @param {Object} config Configuration options
12018 Roo.bootstrap.ComboBox = function(config){
12019 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12023 * Fires when the dropdown list is expanded
12024 * @param {Roo.bootstrap.ComboBox} combo This combo box
12029 * Fires when the dropdown list is collapsed
12030 * @param {Roo.bootstrap.ComboBox} combo This combo box
12034 * @event beforeselect
12035 * Fires before a list item is selected. Return false to cancel the selection.
12036 * @param {Roo.bootstrap.ComboBox} combo This combo box
12037 * @param {Roo.data.Record} record The data record returned from the underlying store
12038 * @param {Number} index The index of the selected item in the dropdown list
12040 'beforeselect' : true,
12043 * Fires when a list item is selected
12044 * @param {Roo.bootstrap.ComboBox} combo This combo box
12045 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12046 * @param {Number} index The index of the selected item in the dropdown list
12050 * @event beforequery
12051 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12052 * The event object passed has these properties:
12053 * @param {Roo.bootstrap.ComboBox} combo This combo box
12054 * @param {String} query The query
12055 * @param {Boolean} forceAll true to force "all" query
12056 * @param {Boolean} cancel true to cancel the query
12057 * @param {Object} e The query event object
12059 'beforequery': true,
12062 * Fires when the 'add' icon is pressed (add a listener to enable add button)
12063 * @param {Roo.bootstrap.ComboBox} combo This combo box
12068 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12069 * @param {Roo.bootstrap.ComboBox} combo This combo box
12070 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12075 * Fires when the remove value from the combobox array
12076 * @param {Roo.bootstrap.ComboBox} combo This combo box
12080 * @event afterremove
12081 * Fires when the remove value from the combobox array
12082 * @param {Roo.bootstrap.ComboBox} combo This combo box
12084 'afterremove' : true,
12086 * @event specialfilter
12087 * Fires when specialfilter
12088 * @param {Roo.bootstrap.ComboBox} combo This combo box
12090 'specialfilter' : true,
12093 * Fires when tick the element
12094 * @param {Roo.bootstrap.ComboBox} combo This combo box
12098 * @event touchviewdisplay
12099 * Fires when touch view require special display (default is using displayField)
12100 * @param {Roo.bootstrap.ComboBox} combo This combo box
12101 * @param {Object} cfg set html .
12103 'touchviewdisplay' : true
12108 this.tickItems = [];
12110 this.selectedIndex = -1;
12111 if(this.mode == 'local'){
12112 if(config.queryDelay === undefined){
12113 this.queryDelay = 10;
12115 if(config.minChars === undefined){
12121 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12124 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12125 * rendering into an Roo.Editor, defaults to false)
12128 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12129 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12132 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12135 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12136 * the dropdown list (defaults to undefined, with no header element)
12140 * @cfg {String/Roo.Template} tpl The template to use to render the output
12144 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12146 listWidth: undefined,
12148 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12149 * mode = 'remote' or 'text' if mode = 'local')
12151 displayField: undefined,
12154 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12155 * mode = 'remote' or 'value' if mode = 'local').
12156 * Note: use of a valueField requires the user make a selection
12157 * in order for a value to be mapped.
12159 valueField: undefined,
12161 * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12166 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12167 * field's data value (defaults to the underlying DOM element's name)
12169 hiddenName: undefined,
12171 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12175 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12177 selectedClass: 'active',
12180 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12184 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12185 * anchor positions (defaults to 'tl-bl')
12187 listAlign: 'tl-bl?',
12189 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12193 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
12194 * query specified by the allQuery config option (defaults to 'query')
12196 triggerAction: 'query',
12198 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12199 * (defaults to 4, does not apply if editable = false)
12203 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12204 * delay (typeAheadDelay) if it matches a known value (defaults to false)
12208 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12209 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12213 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12214 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
12218 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
12219 * when editable = true (defaults to false)
12221 selectOnFocus:false,
12223 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12225 queryParam: 'query',
12227 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
12228 * when mode = 'remote' (defaults to 'Loading...')
12230 loadingText: 'Loading...',
12232 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12236 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12240 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12241 * traditional select (defaults to true)
12245 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12249 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12253 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12254 * listWidth has a higher value)
12258 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12259 * allow the user to set arbitrary text into the field (defaults to false)
12261 forceSelection:false,
12263 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12264 * if typeAhead = true (defaults to 250)
12266 typeAheadDelay : 250,
12268 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12269 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12271 valueNotFoundText : undefined,
12273 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12275 blockFocus : false,
12278 * @cfg {Boolean} disableClear Disable showing of clear button.
12280 disableClear : false,
12282 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
12284 alwaysQuery : false,
12287 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
12292 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12294 invalidClass : "has-warning",
12297 * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12299 validClass : "has-success",
12302 * @cfg {Boolean} specialFilter (true|false) special filter default false
12304 specialFilter : false,
12307 * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12309 mobileTouchView : true,
12312 * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12314 useNativeIOS : false,
12316 ios_options : false,
12328 btnPosition : 'right',
12329 triggerList : true,
12330 showToggleBtn : true,
12332 emptyResultText: 'Empty',
12333 triggerText : 'Select',
12335 // element that contains real text value.. (when hidden is used..)
12337 getAutoCreate : function()
12342 * Render classic select for iso
12345 if(Roo.isIOS && this.useNativeIOS){
12346 cfg = this.getAutoCreateNativeIOS();
12354 if(Roo.isTouch && this.mobileTouchView){
12355 cfg = this.getAutoCreateTouchView();
12362 if(!this.tickable){
12363 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12368 * ComboBox with tickable selections
12371 var align = this.labelAlign || this.parentLabelAlign();
12374 cls : 'form-group roo-combobox-tickable' //input-group
12379 cls : 'tickable-buttons',
12384 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12385 html : this.triggerText
12391 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12398 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12405 buttons.cn.unshift({
12407 cls: 'roo-select2-search-field-input'
12413 Roo.each(buttons.cn, function(c){
12415 c.cls += ' btn-' + _this.size;
12418 if (_this.disabled) {
12429 cls: 'form-hidden-field'
12433 cls: 'roo-select2-choices',
12437 cls: 'roo-select2-search-field',
12449 cls: 'roo-select2-container input-group roo-select2-container-multi',
12454 // cls: 'typeahead typeahead-long dropdown-menu',
12455 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
12460 if(this.hasFeedback && !this.allowBlank){
12464 cls: 'glyphicon form-control-feedback'
12467 combobox.cn.push(feedback);
12470 if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12472 // Roo.log("left and has label");
12476 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12477 tooltip : 'This field is required'
12482 cls : 'control-label col-sm-' + this.labelWidth,
12483 html : this.fieldLabel
12487 cls : "col-sm-" + (12 - this.labelWidth),
12495 if(this.indicatorpos == 'right'){
12501 cls : 'control-label col-sm-' + this.labelWidth,
12502 html : this.fieldLabel
12507 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12508 tooltip : 'This field is required'
12511 cls : "col-sm-" + (12 - this.labelWidth),
12522 } else if ( this.fieldLabel.length) {
12523 // Roo.log(" label");
12527 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12528 tooltip : 'This field is required'
12532 //cls : 'input-group-addon',
12533 html : this.fieldLabel
12541 if(this.indicatorpos == 'right'){
12546 //cls : 'input-group-addon',
12547 html : this.fieldLabel
12553 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12554 tooltip : 'This field is required'
12565 // Roo.log(" no label && no align");
12572 ['xs','sm','md','lg'].map(function(size){
12573 if (settings[size]) {
12574 cfg.cls += ' col-' + size + '-' + settings[size];
12582 _initEventsCalled : false,
12585 initEvents: function()
12587 if (this._initEventsCalled) { // as we call render... prevent looping...
12590 this._initEventsCalled = true;
12593 throw "can not find store for combo";
12596 this.store = Roo.factory(this.store, Roo.data);
12598 // if we are building from html. then this element is so complex, that we can not really
12599 // use the rendered HTML.
12600 // so we have to trash and replace the previous code.
12601 if (Roo.XComponent.build_from_html) {
12603 // remove this element....
12604 var e = this.el.dom, k=0;
12605 while (e ) { e = e.previousSibling; ++k;}
12610 this.rendered = false;
12612 this.render(this.parent().getChildContainer(true), k);
12618 if(Roo.isIOS && this.useNativeIOS){
12619 this.initIOSView();
12627 if(Roo.isTouch && this.mobileTouchView){
12628 this.initTouchView();
12633 this.initTickableEvents();
12637 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12639 if(this.hiddenName){
12641 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12643 this.hiddenField.dom.value =
12644 this.hiddenValue !== undefined ? this.hiddenValue :
12645 this.value !== undefined ? this.value : '';
12647 // prevent input submission
12648 this.el.dom.removeAttribute('name');
12649 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12654 // this.el.dom.setAttribute('autocomplete', 'off');
12657 var cls = 'x-combo-list';
12659 //this.list = new Roo.Layer({
12660 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12666 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12667 _this.list.setWidth(lw);
12670 this.list.on('mouseover', this.onViewOver, this);
12671 this.list.on('mousemove', this.onViewMove, this);
12673 this.list.on('scroll', this.onViewScroll, this);
12676 this.list.swallowEvent('mousewheel');
12677 this.assetHeight = 0;
12680 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12681 this.assetHeight += this.header.getHeight();
12684 this.innerList = this.list.createChild({cls:cls+'-inner'});
12685 this.innerList.on('mouseover', this.onViewOver, this);
12686 this.innerList.on('mousemove', this.onViewMove, this);
12687 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12689 if(this.allowBlank && !this.pageSize && !this.disableClear){
12690 this.footer = this.list.createChild({cls:cls+'-ft'});
12691 this.pageTb = new Roo.Toolbar(this.footer);
12695 this.footer = this.list.createChild({cls:cls+'-ft'});
12696 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12697 {pageSize: this.pageSize});
12701 if (this.pageTb && this.allowBlank && !this.disableClear) {
12703 this.pageTb.add(new Roo.Toolbar.Fill(), {
12704 cls: 'x-btn-icon x-btn-clear',
12706 handler: function()
12709 _this.clearValue();
12710 _this.onSelect(false, -1);
12715 this.assetHeight += this.footer.getHeight();
12720 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12723 this.view = new Roo.View(this.list, this.tpl, {
12724 singleSelect:true, store: this.store, selectedClass: this.selectedClass
12726 //this.view.wrapEl.setDisplayed(false);
12727 this.view.on('click', this.onViewClick, this);
12731 this.store.on('beforeload', this.onBeforeLoad, this);
12732 this.store.on('load', this.onLoad, this);
12733 this.store.on('loadexception', this.onLoadException, this);
12735 if(this.resizable){
12736 this.resizer = new Roo.Resizable(this.list, {
12737 pinned:true, handles:'se'
12739 this.resizer.on('resize', function(r, w, h){
12740 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12741 this.listWidth = w;
12742 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12743 this.restrictHeight();
12745 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12748 if(!this.editable){
12749 this.editable = true;
12750 this.setEditable(false);
12755 if (typeof(this.events.add.listeners) != 'undefined') {
12757 this.addicon = this.wrap.createChild(
12758 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
12760 this.addicon.on('click', function(e) {
12761 this.fireEvent('add', this);
12764 if (typeof(this.events.edit.listeners) != 'undefined') {
12766 this.editicon = this.wrap.createChild(
12767 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
12768 if (this.addicon) {
12769 this.editicon.setStyle('margin-left', '40px');
12771 this.editicon.on('click', function(e) {
12773 // we fire even if inothing is selected..
12774 this.fireEvent('edit', this, this.lastData );
12780 this.keyNav = new Roo.KeyNav(this.inputEl(), {
12781 "up" : function(e){
12782 this.inKeyMode = true;
12786 "down" : function(e){
12787 if(!this.isExpanded()){
12788 this.onTriggerClick();
12790 this.inKeyMode = true;
12795 "enter" : function(e){
12796 // this.onViewClick();
12800 if(this.fireEvent("specialkey", this, e)){
12801 this.onViewClick(false);
12807 "esc" : function(e){
12811 "tab" : function(e){
12814 if(this.fireEvent("specialkey", this, e)){
12815 this.onViewClick(false);
12823 doRelay : function(foo, bar, hname){
12824 if(hname == 'down' || this.scope.isExpanded()){
12825 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12834 this.queryDelay = Math.max(this.queryDelay || 10,
12835 this.mode == 'local' ? 10 : 250);
12838 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12840 if(this.typeAhead){
12841 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12843 if(this.editable !== false){
12844 this.inputEl().on("keyup", this.onKeyUp, this);
12846 if(this.forceSelection){
12847 this.inputEl().on('blur', this.doForce, this);
12851 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12852 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12856 initTickableEvents: function()
12860 if(this.hiddenName){
12862 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12864 this.hiddenField.dom.value =
12865 this.hiddenValue !== undefined ? this.hiddenValue :
12866 this.value !== undefined ? this.value : '';
12868 // prevent input submission
12869 this.el.dom.removeAttribute('name');
12870 this.hiddenField.dom.setAttribute('name', this.hiddenName);
12875 // this.list = this.el.select('ul.dropdown-menu',true).first();
12877 this.choices = this.el.select('ul.roo-select2-choices', true).first();
12878 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12879 if(this.triggerList){
12880 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12883 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12884 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12886 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12887 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12889 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12890 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12892 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12893 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12894 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12897 this.cancelBtn.hide();
12902 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12903 _this.list.setWidth(lw);
12906 this.list.on('mouseover', this.onViewOver, this);
12907 this.list.on('mousemove', this.onViewMove, this);
12909 this.list.on('scroll', this.onViewScroll, this);
12912 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>';
12915 this.view = new Roo.View(this.list, this.tpl, {
12916 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12919 //this.view.wrapEl.setDisplayed(false);
12920 this.view.on('click', this.onViewClick, this);
12924 this.store.on('beforeload', this.onBeforeLoad, this);
12925 this.store.on('load', this.onLoad, this);
12926 this.store.on('loadexception', this.onLoadException, this);
12929 this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12930 "up" : function(e){
12931 this.inKeyMode = true;
12935 "down" : function(e){
12936 this.inKeyMode = true;
12940 "enter" : function(e){
12941 if(this.fireEvent("specialkey", this, e)){
12942 this.onViewClick(false);
12948 "esc" : function(e){
12949 this.onTickableFooterButtonClick(e, false, false);
12952 "tab" : function(e){
12953 this.fireEvent("specialkey", this, e);
12955 this.onTickableFooterButtonClick(e, false, false);
12962 doRelay : function(e, fn, key){
12963 if(this.scope.isExpanded()){
12964 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12973 this.queryDelay = Math.max(this.queryDelay || 10,
12974 this.mode == 'local' ? 10 : 250);
12977 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12979 if(this.typeAhead){
12980 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12983 if(this.editable !== false){
12984 this.tickableInputEl().on("keyup", this.onKeyUp, this);
12989 onDestroy : function(){
12991 this.view.setStore(null);
12992 this.view.el.removeAllListeners();
12993 this.view.el.remove();
12994 this.view.purgeListeners();
12997 this.list.dom.innerHTML = '';
13001 this.store.un('beforeload', this.onBeforeLoad, this);
13002 this.store.un('load', this.onLoad, this);
13003 this.store.un('loadexception', this.onLoadException, this);
13005 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13009 fireKey : function(e){
13010 if(e.isNavKeyPress() && !this.list.isVisible()){
13011 this.fireEvent("specialkey", this, e);
13016 onResize: function(w, h){
13017 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13019 // if(typeof w != 'number'){
13020 // // we do not handle it!?!?
13023 // var tw = this.trigger.getWidth();
13024 // // tw += this.addicon ? this.addicon.getWidth() : 0;
13025 // // tw += this.editicon ? this.editicon.getWidth() : 0;
13027 // this.inputEl().setWidth( this.adjustWidth('input', x));
13029 // //this.trigger.setStyle('left', x+'px');
13031 // if(this.list && this.listWidth === undefined){
13032 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13033 // this.list.setWidth(lw);
13034 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13042 * Allow or prevent the user from directly editing the field text. If false is passed,
13043 * the user will only be able to select from the items defined in the dropdown list. This method
13044 * is the runtime equivalent of setting the 'editable' config option at config time.
13045 * @param {Boolean} value True to allow the user to directly edit the field text
13047 setEditable : function(value){
13048 if(value == this.editable){
13051 this.editable = value;
13053 this.inputEl().dom.setAttribute('readOnly', true);
13054 this.inputEl().on('mousedown', this.onTriggerClick, this);
13055 this.inputEl().addClass('x-combo-noedit');
13057 this.inputEl().dom.setAttribute('readOnly', false);
13058 this.inputEl().un('mousedown', this.onTriggerClick, this);
13059 this.inputEl().removeClass('x-combo-noedit');
13065 onBeforeLoad : function(combo,opts){
13066 if(!this.hasFocus){
13070 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13072 this.restrictHeight();
13073 this.selectedIndex = -1;
13077 onLoad : function(){
13079 this.hasQuery = false;
13081 if(!this.hasFocus){
13085 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13086 this.loading.hide();
13089 if(this.store.getCount() > 0){
13091 this.restrictHeight();
13092 if(this.lastQuery == this.allQuery){
13093 if(this.editable && !this.tickable){
13094 this.inputEl().dom.select();
13098 !this.selectByValue(this.value, true) &&
13101 !this.store.lastOptions ||
13102 typeof(this.store.lastOptions.add) == 'undefined' ||
13103 this.store.lastOptions.add != true
13106 this.select(0, true);
13109 if(this.autoFocus){
13112 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13113 this.taTask.delay(this.typeAheadDelay);
13117 this.onEmptyResults();
13123 onLoadException : function()
13125 this.hasQuery = false;
13127 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13128 this.loading.hide();
13131 if(this.tickable && this.editable){
13136 // only causes errors at present
13137 //Roo.log(this.store.reader.jsonData);
13138 //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13140 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13146 onTypeAhead : function(){
13147 if(this.store.getCount() > 0){
13148 var r = this.store.getAt(0);
13149 var newValue = r.data[this.displayField];
13150 var len = newValue.length;
13151 var selStart = this.getRawValue().length;
13153 if(selStart != len){
13154 this.setRawValue(newValue);
13155 this.selectText(selStart, newValue.length);
13161 onSelect : function(record, index){
13163 if(this.fireEvent('beforeselect', this, record, index) !== false){
13165 this.setFromData(index > -1 ? record.data : false);
13168 this.fireEvent('select', this, record, index);
13173 * Returns the currently selected field value or empty string if no value is set.
13174 * @return {String} value The selected value
13176 getValue : function()
13178 if(Roo.isIOS && this.useNativeIOS){
13179 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13183 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13186 if(this.valueField){
13187 return typeof this.value != 'undefined' ? this.value : '';
13189 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13193 getRawValue : function()
13195 if(Roo.isIOS && this.useNativeIOS){
13196 return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13199 var v = this.inputEl().getValue();
13205 * Clears any text/value currently set in the field
13207 clearValue : function(){
13209 if(this.hiddenField){
13210 this.hiddenField.dom.value = '';
13213 this.setRawValue('');
13214 this.lastSelectionText = '';
13215 this.lastData = false;
13217 var close = this.closeTriggerEl();
13228 * Sets the specified value into the field. If the value finds a match, the corresponding record text
13229 * will be displayed in the field. If the value does not match the data value of an existing item,
13230 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13231 * Otherwise the field will be blank (although the value will still be set).
13232 * @param {String} value The value to match
13234 setValue : function(v)
13236 if(Roo.isIOS && this.useNativeIOS){
13237 this.setIOSValue(v);
13247 if(this.valueField){
13248 var r = this.findRecord(this.valueField, v);
13250 text = r.data[this.displayField];
13251 }else if(this.valueNotFoundText !== undefined){
13252 text = this.valueNotFoundText;
13255 this.lastSelectionText = text;
13256 if(this.hiddenField){
13257 this.hiddenField.dom.value = v;
13259 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13262 var close = this.closeTriggerEl();
13265 (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13271 * @property {Object} the last set data for the element
13276 * Sets the value of the field based on a object which is related to the record format for the store.
13277 * @param {Object} value the value to set as. or false on reset?
13279 setFromData : function(o){
13286 var dv = ''; // display value
13287 var vv = ''; // value value..
13289 if (this.displayField) {
13290 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13292 // this is an error condition!!!
13293 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13296 if(this.valueField){
13297 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13300 var close = this.closeTriggerEl();
13303 (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13306 if(this.hiddenField){
13307 this.hiddenField.dom.value = vv;
13309 this.lastSelectionText = dv;
13310 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13314 // no hidden field.. - we store the value in 'value', but still display
13315 // display field!!!!
13316 this.lastSelectionText = dv;
13317 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13324 reset : function(){
13325 // overridden so that last data is reset..
13332 this.setValue(this.originalValue);
13333 //this.clearInvalid();
13334 this.lastData = false;
13336 this.view.clearSelections();
13342 findRecord : function(prop, value){
13344 if(this.store.getCount() > 0){
13345 this.store.each(function(r){
13346 if(r.data[prop] == value){
13356 getName: function()
13358 // returns hidden if it's set..
13359 if (!this.rendered) {return ''};
13360 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
13364 onViewMove : function(e, t){
13365 this.inKeyMode = false;
13369 onViewOver : function(e, t){
13370 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13373 var item = this.view.findItemFromChild(t);
13376 var index = this.view.indexOf(item);
13377 this.select(index, false);
13382 onViewClick : function(view, doFocus, el, e)
13384 var index = this.view.getSelectedIndexes()[0];
13386 var r = this.store.getAt(index);
13390 if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13397 Roo.each(this.tickItems, function(v,k){
13399 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13401 _this.tickItems.splice(k, 1);
13403 if(typeof(e) == 'undefined' && view == false){
13404 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13416 if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13417 this.tickItems.push(r.data);
13420 if(typeof(e) == 'undefined' && view == false){
13421 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13428 this.onSelect(r, index);
13430 if(doFocus !== false && !this.blockFocus){
13431 this.inputEl().focus();
13436 restrictHeight : function(){
13437 //this.innerList.dom.style.height = '';
13438 //var inner = this.innerList.dom;
13439 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13440 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13441 //this.list.beginUpdate();
13442 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13443 this.list.alignTo(this.inputEl(), this.listAlign);
13444 this.list.alignTo(this.inputEl(), this.listAlign);
13445 //this.list.endUpdate();
13449 onEmptyResults : function(){
13451 if(this.tickable && this.editable){
13452 this.restrictHeight();
13460 * Returns true if the dropdown list is expanded, else false.
13462 isExpanded : function(){
13463 return this.list.isVisible();
13467 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13468 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13469 * @param {String} value The data value of the item to select
13470 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13471 * selected item if it is not currently in view (defaults to true)
13472 * @return {Boolean} True if the value matched an item in the list, else false
13474 selectByValue : function(v, scrollIntoView){
13475 if(v !== undefined && v !== null){
13476 var r = this.findRecord(this.valueField || this.displayField, v);
13478 this.select(this.store.indexOf(r), scrollIntoView);
13486 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13487 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13488 * @param {Number} index The zero-based index of the list item to select
13489 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13490 * selected item if it is not currently in view (defaults to true)
13492 select : function(index, scrollIntoView){
13493 this.selectedIndex = index;
13494 this.view.select(index);
13495 if(scrollIntoView !== false){
13496 var el = this.view.getNode(index);
13498 * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13501 this.list.scrollChildIntoView(el, false);
13507 selectNext : function(){
13508 var ct = this.store.getCount();
13510 if(this.selectedIndex == -1){
13512 }else if(this.selectedIndex < ct-1){
13513 this.select(this.selectedIndex+1);
13519 selectPrev : function(){
13520 var ct = this.store.getCount();
13522 if(this.selectedIndex == -1){
13524 }else if(this.selectedIndex != 0){
13525 this.select(this.selectedIndex-1);
13531 onKeyUp : function(e){
13532 if(this.editable !== false && !e.isSpecialKey()){
13533 this.lastKey = e.getKey();
13534 this.dqTask.delay(this.queryDelay);
13539 validateBlur : function(){
13540 return !this.list || !this.list.isVisible();
13544 initQuery : function(){
13546 var v = this.getRawValue();
13548 if(this.tickable && this.editable){
13549 v = this.tickableInputEl().getValue();
13556 doForce : function(){
13557 if(this.inputEl().dom.value.length > 0){
13558 this.inputEl().dom.value =
13559 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13565 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
13566 * query allowing the query action to be canceled if needed.
13567 * @param {String} query The SQL query to execute
13568 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13569 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
13570 * saved in the current store (defaults to false)
13572 doQuery : function(q, forceAll){
13574 if(q === undefined || q === null){
13579 forceAll: forceAll,
13583 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13588 forceAll = qe.forceAll;
13589 if(forceAll === true || (q.length >= this.minChars)){
13591 this.hasQuery = true;
13593 if(this.lastQuery != q || this.alwaysQuery){
13594 this.lastQuery = q;
13595 if(this.mode == 'local'){
13596 this.selectedIndex = -1;
13598 this.store.clearFilter();
13601 if(this.specialFilter){
13602 this.fireEvent('specialfilter', this);
13607 this.store.filter(this.displayField, q);
13610 this.store.fireEvent("datachanged", this.store);
13617 this.store.baseParams[this.queryParam] = q;
13619 var options = {params : this.getParams(q)};
13622 options.add = true;
13623 options.params.start = this.page * this.pageSize;
13626 this.store.load(options);
13629 * this code will make the page width larger, at the beginning, the list not align correctly,
13630 * we should expand the list on onLoad
13631 * so command out it
13636 this.selectedIndex = -1;
13641 this.loadNext = false;
13645 getParams : function(q){
13647 //p[this.queryParam] = q;
13651 p.limit = this.pageSize;
13657 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13659 collapse : function(){
13660 if(!this.isExpanded()){
13666 this.hasFocus = false;
13670 this.cancelBtn.hide();
13671 this.trigger.show();
13674 this.tickableInputEl().dom.value = '';
13675 this.tickableInputEl().blur();
13680 Roo.get(document).un('mousedown', this.collapseIf, this);
13681 Roo.get(document).un('mousewheel', this.collapseIf, this);
13682 if (!this.editable) {
13683 Roo.get(document).un('keydown', this.listKeyPress, this);
13685 this.fireEvent('collapse', this);
13691 collapseIf : function(e){
13692 var in_combo = e.within(this.el);
13693 var in_list = e.within(this.list);
13694 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13696 if (in_combo || in_list || is_list) {
13697 //e.stopPropagation();
13702 this.onTickableFooterButtonClick(e, false, false);
13710 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13712 expand : function(){
13714 if(this.isExpanded() || !this.hasFocus){
13718 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13719 this.list.setWidth(lw);
13725 this.restrictHeight();
13729 this.tickItems = Roo.apply([], this.item);
13732 this.cancelBtn.show();
13733 this.trigger.hide();
13736 this.tickableInputEl().focus();
13741 Roo.get(document).on('mousedown', this.collapseIf, this);
13742 Roo.get(document).on('mousewheel', this.collapseIf, this);
13743 if (!this.editable) {
13744 Roo.get(document).on('keydown', this.listKeyPress, this);
13747 this.fireEvent('expand', this);
13751 // Implements the default empty TriggerField.onTriggerClick function
13752 onTriggerClick : function(e)
13754 Roo.log('trigger click');
13756 if(this.disabled || !this.triggerList){
13761 this.loadNext = false;
13763 if(this.isExpanded()){
13765 if (!this.blockFocus) {
13766 this.inputEl().focus();
13770 this.hasFocus = true;
13771 if(this.triggerAction == 'all') {
13772 this.doQuery(this.allQuery, true);
13774 this.doQuery(this.getRawValue());
13776 if (!this.blockFocus) {
13777 this.inputEl().focus();
13782 onTickableTriggerClick : function(e)
13789 this.loadNext = false;
13790 this.hasFocus = true;
13792 if(this.triggerAction == 'all') {
13793 this.doQuery(this.allQuery, true);
13795 this.doQuery(this.getRawValue());
13799 onSearchFieldClick : function(e)
13801 if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13802 this.onTickableFooterButtonClick(e, false, false);
13806 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13811 this.loadNext = false;
13812 this.hasFocus = true;
13814 if(this.triggerAction == 'all') {
13815 this.doQuery(this.allQuery, true);
13817 this.doQuery(this.getRawValue());
13821 listKeyPress : function(e)
13823 //Roo.log('listkeypress');
13824 // scroll to first matching element based on key pres..
13825 if (e.isSpecialKey()) {
13828 var k = String.fromCharCode(e.getKey()).toUpperCase();
13831 var csel = this.view.getSelectedNodes();
13832 var cselitem = false;
13834 var ix = this.view.indexOf(csel[0]);
13835 cselitem = this.store.getAt(ix);
13836 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13842 this.store.each(function(v) {
13844 // start at existing selection.
13845 if (cselitem.id == v.id) {
13851 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13852 match = this.store.indexOf(v);
13858 if (match === false) {
13859 return true; // no more action?
13862 this.view.select(match);
13863 var sn = Roo.get(this.view.getSelectedNodes()[0]);
13864 sn.scrollIntoView(sn.dom.parentNode, false);
13867 onViewScroll : function(e, t){
13869 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){
13873 this.hasQuery = true;
13875 this.loading = this.list.select('.loading', true).first();
13877 if(this.loading === null){
13878 this.list.createChild({
13880 cls: 'loading roo-select2-more-results roo-select2-active',
13881 html: 'Loading more results...'
13884 this.loading = this.list.select('.loading', true).first();
13886 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13888 this.loading.hide();
13891 this.loading.show();
13896 this.loadNext = true;
13898 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13903 addItem : function(o)
13905 var dv = ''; // display value
13907 if (this.displayField) {
13908 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13910 // this is an error condition!!!
13911 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
13918 var choice = this.choices.createChild({
13920 cls: 'roo-select2-search-choice',
13929 cls: 'roo-select2-search-choice-close',
13934 }, this.searchField);
13936 var close = choice.select('a.roo-select2-search-choice-close', true).first();
13938 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13946 this.inputEl().dom.value = '';
13951 onRemoveItem : function(e, _self, o)
13953 e.preventDefault();
13955 this.lastItem = Roo.apply([], this.item);
13957 var index = this.item.indexOf(o.data) * 1;
13960 Roo.log('not this item?!');
13964 this.item.splice(index, 1);
13969 this.fireEvent('remove', this, e);
13975 syncValue : function()
13977 if(!this.item.length){
13984 Roo.each(this.item, function(i){
13985 if(_this.valueField){
13986 value.push(i[_this.valueField]);
13993 this.value = value.join(',');
13995 if(this.hiddenField){
13996 this.hiddenField.dom.value = this.value;
13999 this.store.fireEvent("datachanged", this.store);
14004 clearItem : function()
14006 if(!this.multiple){
14012 Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14020 if(this.tickable && !Roo.isTouch){
14021 this.view.refresh();
14025 inputEl: function ()
14027 if(Roo.isIOS && this.useNativeIOS){
14028 return this.el.select('select.roo-ios-select', true).first();
14031 if(Roo.isTouch && this.mobileTouchView){
14032 return this.el.select('input.form-control',true).first();
14036 return this.searchField;
14039 return this.el.select('input.form-control',true).first();
14042 onTickableFooterButtonClick : function(e, btn, el)
14044 e.preventDefault();
14046 this.lastItem = Roo.apply([], this.item);
14048 if(btn && btn.name == 'cancel'){
14049 this.tickItems = Roo.apply([], this.item);
14058 Roo.each(this.tickItems, function(o){
14066 validate : function()
14068 var v = this.getRawValue();
14071 v = this.getValue();
14074 if(this.disabled || this.allowBlank || v.length){
14079 this.markInvalid();
14083 tickableInputEl : function()
14085 if(!this.tickable || !this.editable){
14086 return this.inputEl();
14089 return this.inputEl().select('.roo-select2-search-field-input', true).first();
14093 getAutoCreateTouchView : function()
14098 cls: 'form-group' //input-group
14104 type : this.inputType,
14105 cls : 'form-control x-combo-noedit',
14106 autocomplete: 'new-password',
14107 placeholder : this.placeholder || '',
14112 input.name = this.name;
14116 input.cls += ' input-' + this.size;
14119 if (this.disabled) {
14120 input.disabled = true;
14131 inputblock.cls += ' input-group';
14133 inputblock.cn.unshift({
14135 cls : 'input-group-addon',
14140 if(this.removable && !this.multiple){
14141 inputblock.cls += ' roo-removable';
14143 inputblock.cn.push({
14146 cls : 'roo-combo-removable-btn close'
14150 if(this.hasFeedback && !this.allowBlank){
14152 inputblock.cls += ' has-feedback';
14154 inputblock.cn.push({
14156 cls: 'glyphicon form-control-feedback'
14163 inputblock.cls += (this.before) ? '' : ' input-group';
14165 inputblock.cn.push({
14167 cls : 'input-group-addon',
14178 cls: 'form-hidden-field'
14192 cls: 'form-hidden-field'
14196 cls: 'roo-select2-choices',
14200 cls: 'roo-select2-search-field',
14213 cls: 'roo-select2-container input-group roo-touchview-combobox ',
14219 if(!this.multiple && this.showToggleBtn){
14226 if (this.caret != false) {
14229 cls: 'fa fa-' + this.caret
14236 cls : 'input-group-addon btn dropdown-toggle',
14241 cls: 'combobox-clear',
14255 combobox.cls += ' roo-select2-container-multi';
14258 var align = this.labelAlign || this.parentLabelAlign();
14262 if(this.fieldLabel.length && this.labelWidth){
14264 var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
14265 var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
14270 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14271 tooltip : 'This field is required'
14275 cls : 'control-label ' + lw,
14276 html : this.fieldLabel
14287 if(this.indicatorpos == 'right'){
14291 cls : 'control-label ' + lw,
14292 html : this.fieldLabel
14297 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14298 tooltip : 'This field is required'
14310 var settings = this;
14312 ['xs','sm','md','lg'].map(function(size){
14313 if (settings[size]) {
14314 cfg.cls += ' col-' + size + '-' + settings[size];
14321 initTouchView : function()
14323 this.renderTouchView();
14325 this.touchViewEl.on('scroll', function(){
14326 this.el.dom.scrollTop = 0;
14329 this.originalValue = this.getValue();
14331 this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14333 this.inputEl().on("click", this.showTouchView, this);
14334 if (this.triggerEl) {
14335 this.triggerEl.on("click", this.showTouchView, this);
14339 this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14340 this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14342 this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14344 this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14345 this.store.on('load', this.onTouchViewLoad, this);
14346 this.store.on('loadexception', this.onTouchViewLoadException, this);
14348 if(this.hiddenName){
14350 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14352 this.hiddenField.dom.value =
14353 this.hiddenValue !== undefined ? this.hiddenValue :
14354 this.value !== undefined ? this.value : '';
14356 this.el.dom.removeAttribute('name');
14357 this.hiddenField.dom.setAttribute('name', this.hiddenName);
14361 this.choices = this.el.select('ul.roo-select2-choices', true).first();
14362 this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14365 if(this.removable && !this.multiple){
14366 var close = this.closeTriggerEl();
14368 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14369 close.on('click', this.removeBtnClick, this, close);
14373 * fix the bug in Safari iOS8
14375 this.inputEl().on("focus", function(e){
14376 document.activeElement.blur();
14384 renderTouchView : function()
14386 this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14387 this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14389 this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14390 this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14392 this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14393 this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14394 this.touchViewBodyEl.setStyle('overflow', 'auto');
14396 this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14397 this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14399 this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14400 this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14404 showTouchView : function()
14410 this.touchViewHeaderEl.hide();
14412 if(this.modalTitle.length){
14413 this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14414 this.touchViewHeaderEl.show();
14417 this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14418 this.touchViewEl.show();
14420 this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14421 this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14422 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14424 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14426 if(this.modalTitle.length){
14427 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14430 this.touchViewBodyEl.setHeight(bodyHeight);
14434 (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14436 this.touchViewEl.addClass('in');
14439 this.doTouchViewQuery();
14443 hideTouchView : function()
14445 this.touchViewEl.removeClass('in');
14449 (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14451 this.touchViewEl.setStyle('display', 'none');
14456 setTouchViewValue : function()
14463 Roo.each(this.tickItems, function(o){
14468 this.hideTouchView();
14471 doTouchViewQuery : function()
14480 if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14484 if(!this.alwaysQuery || this.mode == 'local'){
14485 this.onTouchViewLoad();
14492 onTouchViewBeforeLoad : function(combo,opts)
14498 onTouchViewLoad : function()
14500 if(this.store.getCount() < 1){
14501 this.onTouchViewEmptyResults();
14505 this.clearTouchView();
14507 var rawValue = this.getRawValue();
14509 var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14511 this.tickItems = [];
14513 this.store.data.each(function(d, rowIndex){
14514 var row = this.touchViewListGroup.createChild(template);
14516 if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14517 row.addClass(d.data.cls);
14520 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14523 html : d.data[this.displayField]
14526 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14527 row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14530 row.removeClass('selected');
14531 if(!this.multiple && this.valueField &&
14532 typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14535 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14536 row.addClass('selected');
14539 if(this.multiple && this.valueField &&
14540 typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14544 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14545 this.tickItems.push(d.data);
14548 row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14552 var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14554 var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14556 if(this.modalTitle.length){
14557 bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14560 var listHeight = this.touchViewListGroup.getHeight();
14564 if(firstChecked && listHeight > bodyHeight){
14565 (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14570 onTouchViewLoadException : function()
14572 this.hideTouchView();
14575 onTouchViewEmptyResults : function()
14577 this.clearTouchView();
14579 this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14581 this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14585 clearTouchView : function()
14587 this.touchViewListGroup.dom.innerHTML = '';
14590 onTouchViewClick : function(e, el, o)
14592 e.preventDefault();
14595 var rowIndex = o.rowIndex;
14597 var r = this.store.getAt(rowIndex);
14599 if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14601 if(!this.multiple){
14602 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14603 c.dom.removeAttribute('checked');
14606 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14608 this.setFromData(r.data);
14610 var close = this.closeTriggerEl();
14616 this.hideTouchView();
14618 this.fireEvent('select', this, r, rowIndex);
14623 if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14624 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14625 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14629 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14630 this.addItem(r.data);
14631 this.tickItems.push(r.data);
14635 getAutoCreateNativeIOS : function()
14638 cls: 'form-group' //input-group,
14643 cls : 'roo-ios-select'
14647 combobox.name = this.name;
14650 if (this.disabled) {
14651 combobox.disabled = true;
14654 var settings = this;
14656 ['xs','sm','md','lg'].map(function(size){
14657 if (settings[size]) {
14658 cfg.cls += ' col-' + size + '-' + settings[size];
14668 initIOSView : function()
14670 this.store.on('load', this.onIOSViewLoad, this);
14675 onIOSViewLoad : function()
14677 if(this.store.getCount() < 1){
14681 this.clearIOSView();
14683 if(this.allowBlank) {
14685 var default_text = '-- SELECT --';
14687 var opt = this.inputEl().createChild({
14690 html : default_text
14694 o[this.valueField] = 0;
14695 o[this.displayField] = default_text;
14697 this.ios_options.push({
14704 this.store.data.each(function(d, rowIndex){
14708 if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14709 html = d.data[this.displayField];
14714 if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
14715 value = d.data[this.valueField];
14724 if(this.value == d.data[this.valueField]){
14725 option['selected'] = true;
14728 var opt = this.inputEl().createChild(option);
14730 this.ios_options.push({
14737 this.inputEl().on('change', function(){
14738 this.fireEvent('select', this);
14743 clearIOSView: function()
14745 this.inputEl().dom.innerHTML = '';
14747 this.ios_options = [];
14750 setIOSValue: function(v)
14754 if(!this.ios_options){
14758 Roo.each(this.ios_options, function(opts){
14760 opts.el.dom.removeAttribute('selected');
14762 if(opts.data[this.valueField] != v){
14766 opts.el.dom.setAttribute('selected', true);
14772 * @cfg {Boolean} grow
14776 * @cfg {Number} growMin
14780 * @cfg {Number} growMax
14789 Roo.apply(Roo.bootstrap.ComboBox, {
14793 cls: 'modal-header',
14815 cls: 'list-group-item',
14819 cls: 'roo-combobox-list-group-item-value'
14823 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14837 listItemCheckbox : {
14839 cls: 'list-group-item',
14843 cls: 'roo-combobox-list-group-item-value'
14847 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14863 cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14868 cls: 'modal-footer',
14876 cls: 'col-xs-6 text-left',
14879 cls: 'btn btn-danger roo-touch-view-cancel',
14885 cls: 'col-xs-6 text-right',
14888 cls: 'btn btn-success roo-touch-view-ok',
14899 Roo.apply(Roo.bootstrap.ComboBox, {
14901 touchViewTemplate : {
14903 cls: 'modal fade roo-combobox-touch-view',
14907 cls: 'modal-dialog',
14908 style : 'position:fixed', // we have to fix position....
14912 cls: 'modal-content',
14914 Roo.bootstrap.ComboBox.header,
14915 Roo.bootstrap.ComboBox.body,
14916 Roo.bootstrap.ComboBox.footer
14925 * Ext JS Library 1.1.1
14926 * Copyright(c) 2006-2007, Ext JS, LLC.
14928 * Originally Released Under LGPL - original licence link has changed is not relivant.
14931 * <script type="text/javascript">
14936 * @extends Roo.util.Observable
14937 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
14938 * This class also supports single and multi selection modes. <br>
14939 * Create a data model bound view:
14941 var store = new Roo.data.Store(...);
14943 var view = new Roo.View({
14945 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
14947 singleSelect: true,
14948 selectedClass: "ydataview-selected",
14952 // listen for node click?
14953 view.on("click", function(vw, index, node, e){
14954 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14958 dataModel.load("foobar.xml");
14960 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14962 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14963 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14965 * Note: old style constructor is still suported (container, template, config)
14968 * Create a new View
14969 * @param {Object} config The config object
14972 Roo.View = function(config, depreciated_tpl, depreciated_config){
14974 this.parent = false;
14976 if (typeof(depreciated_tpl) == 'undefined') {
14977 // new way.. - universal constructor.
14978 Roo.apply(this, config);
14979 this.el = Roo.get(this.el);
14982 this.el = Roo.get(config);
14983 this.tpl = depreciated_tpl;
14984 Roo.apply(this, depreciated_config);
14986 this.wrapEl = this.el.wrap().wrap();
14987 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14990 if(typeof(this.tpl) == "string"){
14991 this.tpl = new Roo.Template(this.tpl);
14993 // support xtype ctors..
14994 this.tpl = new Roo.factory(this.tpl, Roo);
14998 this.tpl.compile();
15003 * @event beforeclick
15004 * Fires before a click is processed. Returns false to cancel the default action.
15005 * @param {Roo.View} this
15006 * @param {Number} index The index of the target node
15007 * @param {HTMLElement} node The target node
15008 * @param {Roo.EventObject} e The raw event object
15010 "beforeclick" : true,
15013 * Fires when a template node is clicked.
15014 * @param {Roo.View} this
15015 * @param {Number} index The index of the target node
15016 * @param {HTMLElement} node The target node
15017 * @param {Roo.EventObject} e The raw event object
15022 * Fires when a template node is double clicked.
15023 * @param {Roo.View} this
15024 * @param {Number} index The index of the target node
15025 * @param {HTMLElement} node The target node
15026 * @param {Roo.EventObject} e The raw event object
15030 * @event contextmenu
15031 * Fires when a template node is right clicked.
15032 * @param {Roo.View} this
15033 * @param {Number} index The index of the target node
15034 * @param {HTMLElement} node The target node
15035 * @param {Roo.EventObject} e The raw event object
15037 "contextmenu" : true,
15039 * @event selectionchange
15040 * Fires when the selected nodes change.
15041 * @param {Roo.View} this
15042 * @param {Array} selections Array of the selected nodes
15044 "selectionchange" : true,
15047 * @event beforeselect
15048 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15049 * @param {Roo.View} this
15050 * @param {HTMLElement} node The node to be selected
15051 * @param {Array} selections Array of currently selected nodes
15053 "beforeselect" : true,
15055 * @event preparedata
15056 * Fires on every row to render, to allow you to change the data.
15057 * @param {Roo.View} this
15058 * @param {Object} data to be rendered (change this)
15060 "preparedata" : true
15068 "click": this.onClick,
15069 "dblclick": this.onDblClick,
15070 "contextmenu": this.onContextMenu,
15074 this.selections = [];
15076 this.cmp = new Roo.CompositeElementLite([]);
15078 this.store = Roo.factory(this.store, Roo.data);
15079 this.setStore(this.store, true);
15082 if ( this.footer && this.footer.xtype) {
15084 var fctr = this.wrapEl.appendChild(document.createElement("div"));
15086 this.footer.dataSource = this.store;
15087 this.footer.container = fctr;
15088 this.footer = Roo.factory(this.footer, Roo);
15089 fctr.insertFirst(this.el);
15091 // this is a bit insane - as the paging toolbar seems to detach the el..
15092 // dom.parentNode.parentNode.parentNode
15093 // they get detached?
15097 Roo.View.superclass.constructor.call(this);
15102 Roo.extend(Roo.View, Roo.util.Observable, {
15105 * @cfg {Roo.data.Store} store Data store to load data from.
15110 * @cfg {String|Roo.Element} el The container element.
15115 * @cfg {String|Roo.Template} tpl The template used by this View
15119 * @cfg {String} dataName the named area of the template to use as the data area
15120 * Works with domtemplates roo-name="name"
15124 * @cfg {String} selectedClass The css class to add to selected nodes
15126 selectedClass : "x-view-selected",
15128 * @cfg {String} emptyText The empty text to show when nothing is loaded.
15133 * @cfg {String} text to display on mask (default Loading)
15137 * @cfg {Boolean} multiSelect Allow multiple selection
15139 multiSelect : false,
15141 * @cfg {Boolean} singleSelect Allow single selection
15143 singleSelect: false,
15146 * @cfg {Boolean} toggleSelect - selecting
15148 toggleSelect : false,
15151 * @cfg {Boolean} tickable - selecting
15156 * Returns the element this view is bound to.
15157 * @return {Roo.Element}
15159 getEl : function(){
15160 return this.wrapEl;
15166 * Refreshes the view. - called by datachanged on the store. - do not call directly.
15168 refresh : function(){
15169 //Roo.log('refresh');
15172 // if we are using something like 'domtemplate', then
15173 // the what gets used is:
15174 // t.applySubtemplate(NAME, data, wrapping data..)
15175 // the outer template then get' applied with
15176 // the store 'extra data'
15177 // and the body get's added to the
15178 // roo-name="data" node?
15179 // <span class='roo-tpl-{name}'></span> ?????
15183 this.clearSelections();
15184 this.el.update("");
15186 var records = this.store.getRange();
15187 if(records.length < 1) {
15189 // is this valid?? = should it render a template??
15191 this.el.update(this.emptyText);
15195 if (this.dataName) {
15196 this.el.update(t.apply(this.store.meta)); //????
15197 el = this.el.child('.roo-tpl-' + this.dataName);
15200 for(var i = 0, len = records.length; i < len; i++){
15201 var data = this.prepareData(records[i].data, i, records[i]);
15202 this.fireEvent("preparedata", this, data, i, records[i]);
15204 var d = Roo.apply({}, data);
15207 Roo.apply(d, {'roo-id' : Roo.id()});
15211 Roo.each(this.parent.item, function(item){
15212 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15215 Roo.apply(d, {'roo-data-checked' : 'checked'});
15219 html[html.length] = Roo.util.Format.trim(
15221 t.applySubtemplate(this.dataName, d, this.store.meta) :
15228 el.update(html.join(""));
15229 this.nodes = el.dom.childNodes;
15230 this.updateIndexes(0);
15235 * Function to override to reformat the data that is sent to
15236 * the template for each node.
15237 * DEPRICATED - use the preparedata event handler.
15238 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15239 * a JSON object for an UpdateManager bound view).
15241 prepareData : function(data, index, record)
15243 this.fireEvent("preparedata", this, data, index, record);
15247 onUpdate : function(ds, record){
15248 // Roo.log('on update');
15249 this.clearSelections();
15250 var index = this.store.indexOf(record);
15251 var n = this.nodes[index];
15252 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15253 n.parentNode.removeChild(n);
15254 this.updateIndexes(index, index);
15260 onAdd : function(ds, records, index)
15262 //Roo.log(['on Add', ds, records, index] );
15263 this.clearSelections();
15264 if(this.nodes.length == 0){
15268 var n = this.nodes[index];
15269 for(var i = 0, len = records.length; i < len; i++){
15270 var d = this.prepareData(records[i].data, i, records[i]);
15272 this.tpl.insertBefore(n, d);
15275 this.tpl.append(this.el, d);
15278 this.updateIndexes(index);
15281 onRemove : function(ds, record, index){
15282 // Roo.log('onRemove');
15283 this.clearSelections();
15284 var el = this.dataName ?
15285 this.el.child('.roo-tpl-' + this.dataName) :
15288 el.dom.removeChild(this.nodes[index]);
15289 this.updateIndexes(index);
15293 * Refresh an individual node.
15294 * @param {Number} index
15296 refreshNode : function(index){
15297 this.onUpdate(this.store, this.store.getAt(index));
15300 updateIndexes : function(startIndex, endIndex){
15301 var ns = this.nodes;
15302 startIndex = startIndex || 0;
15303 endIndex = endIndex || ns.length - 1;
15304 for(var i = startIndex; i <= endIndex; i++){
15305 ns[i].nodeIndex = i;
15310 * Changes the data store this view uses and refresh the view.
15311 * @param {Store} store
15313 setStore : function(store, initial){
15314 if(!initial && this.store){
15315 this.store.un("datachanged", this.refresh);
15316 this.store.un("add", this.onAdd);
15317 this.store.un("remove", this.onRemove);
15318 this.store.un("update", this.onUpdate);
15319 this.store.un("clear", this.refresh);
15320 this.store.un("beforeload", this.onBeforeLoad);
15321 this.store.un("load", this.onLoad);
15322 this.store.un("loadexception", this.onLoad);
15326 store.on("datachanged", this.refresh, this);
15327 store.on("add", this.onAdd, this);
15328 store.on("remove", this.onRemove, this);
15329 store.on("update", this.onUpdate, this);
15330 store.on("clear", this.refresh, this);
15331 store.on("beforeload", this.onBeforeLoad, this);
15332 store.on("load", this.onLoad, this);
15333 store.on("loadexception", this.onLoad, this);
15341 * onbeforeLoad - masks the loading area.
15344 onBeforeLoad : function(store,opts)
15346 //Roo.log('onBeforeLoad');
15348 this.el.update("");
15350 this.el.mask(this.mask ? this.mask : "Loading" );
15352 onLoad : function ()
15359 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15360 * @param {HTMLElement} node
15361 * @return {HTMLElement} The template node
15363 findItemFromChild : function(node){
15364 var el = this.dataName ?
15365 this.el.child('.roo-tpl-' + this.dataName,true) :
15368 if(!node || node.parentNode == el){
15371 var p = node.parentNode;
15372 while(p && p != el){
15373 if(p.parentNode == el){
15382 onClick : function(e){
15383 var item = this.findItemFromChild(e.getTarget());
15385 var index = this.indexOf(item);
15386 if(this.onItemClick(item, index, e) !== false){
15387 this.fireEvent("click", this, index, item, e);
15390 this.clearSelections();
15395 onContextMenu : function(e){
15396 var item = this.findItemFromChild(e.getTarget());
15398 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15403 onDblClick : function(e){
15404 var item = this.findItemFromChild(e.getTarget());
15406 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15410 onItemClick : function(item, index, e)
15412 if(this.fireEvent("beforeclick", this, index, item, e) === false){
15415 if (this.toggleSelect) {
15416 var m = this.isSelected(item) ? 'unselect' : 'select';
15419 _t[m](item, true, false);
15422 if(this.multiSelect || this.singleSelect){
15423 if(this.multiSelect && e.shiftKey && this.lastSelection){
15424 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15426 this.select(item, this.multiSelect && e.ctrlKey);
15427 this.lastSelection = item;
15430 if(!this.tickable){
15431 e.preventDefault();
15439 * Get the number of selected nodes.
15442 getSelectionCount : function(){
15443 return this.selections.length;
15447 * Get the currently selected nodes.
15448 * @return {Array} An array of HTMLElements
15450 getSelectedNodes : function(){
15451 return this.selections;
15455 * Get the indexes of the selected nodes.
15458 getSelectedIndexes : function(){
15459 var indexes = [], s = this.selections;
15460 for(var i = 0, len = s.length; i < len; i++){
15461 indexes.push(s[i].nodeIndex);
15467 * Clear all selections
15468 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15470 clearSelections : function(suppressEvent){
15471 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15472 this.cmp.elements = this.selections;
15473 this.cmp.removeClass(this.selectedClass);
15474 this.selections = [];
15475 if(!suppressEvent){
15476 this.fireEvent("selectionchange", this, this.selections);
15482 * Returns true if the passed node is selected
15483 * @param {HTMLElement/Number} node The node or node index
15484 * @return {Boolean}
15486 isSelected : function(node){
15487 var s = this.selections;
15491 node = this.getNode(node);
15492 return s.indexOf(node) !== -1;
15497 * @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
15498 * @param {Boolean} keepExisting (optional) true to keep existing selections
15499 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15501 select : function(nodeInfo, keepExisting, suppressEvent){
15502 if(nodeInfo instanceof Array){
15504 this.clearSelections(true);
15506 for(var i = 0, len = nodeInfo.length; i < len; i++){
15507 this.select(nodeInfo[i], true, true);
15511 var node = this.getNode(nodeInfo);
15512 if(!node || this.isSelected(node)){
15513 return; // already selected.
15516 this.clearSelections(true);
15519 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15520 Roo.fly(node).addClass(this.selectedClass);
15521 this.selections.push(node);
15522 if(!suppressEvent){
15523 this.fireEvent("selectionchange", this, this.selections);
15531 * @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
15532 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15533 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15535 unselect : function(nodeInfo, keepExisting, suppressEvent)
15537 if(nodeInfo instanceof Array){
15538 Roo.each(this.selections, function(s) {
15539 this.unselect(s, nodeInfo);
15543 var node = this.getNode(nodeInfo);
15544 if(!node || !this.isSelected(node)){
15545 //Roo.log("not selected");
15546 return; // not selected.
15550 Roo.each(this.selections, function(s) {
15552 Roo.fly(node).removeClass(this.selectedClass);
15559 this.selections= ns;
15560 this.fireEvent("selectionchange", this, this.selections);
15564 * Gets a template node.
15565 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15566 * @return {HTMLElement} The node or null if it wasn't found
15568 getNode : function(nodeInfo){
15569 if(typeof nodeInfo == "string"){
15570 return document.getElementById(nodeInfo);
15571 }else if(typeof nodeInfo == "number"){
15572 return this.nodes[nodeInfo];
15578 * Gets a range template nodes.
15579 * @param {Number} startIndex
15580 * @param {Number} endIndex
15581 * @return {Array} An array of nodes
15583 getNodes : function(start, end){
15584 var ns = this.nodes;
15585 start = start || 0;
15586 end = typeof end == "undefined" ? ns.length - 1 : end;
15589 for(var i = start; i <= end; i++){
15593 for(var i = start; i >= end; i--){
15601 * Finds the index of the passed node
15602 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15603 * @return {Number} The index of the node or -1
15605 indexOf : function(node){
15606 node = this.getNode(node);
15607 if(typeof node.nodeIndex == "number"){
15608 return node.nodeIndex;
15610 var ns = this.nodes;
15611 for(var i = 0, len = ns.length; i < len; i++){
15622 * based on jquery fullcalendar
15626 Roo.bootstrap = Roo.bootstrap || {};
15628 * @class Roo.bootstrap.Calendar
15629 * @extends Roo.bootstrap.Component
15630 * Bootstrap Calendar class
15631 * @cfg {Boolean} loadMask (true|false) default false
15632 * @cfg {Object} header generate the user specific header of the calendar, default false
15635 * Create a new Container
15636 * @param {Object} config The config object
15641 Roo.bootstrap.Calendar = function(config){
15642 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15646 * Fires when a date is selected
15647 * @param {DatePicker} this
15648 * @param {Date} date The selected date
15652 * @event monthchange
15653 * Fires when the displayed month changes
15654 * @param {DatePicker} this
15655 * @param {Date} date The selected month
15657 'monthchange': true,
15659 * @event evententer
15660 * Fires when mouse over an event
15661 * @param {Calendar} this
15662 * @param {event} Event
15664 'evententer': true,
15666 * @event eventleave
15667 * Fires when the mouse leaves an
15668 * @param {Calendar} this
15671 'eventleave': true,
15673 * @event eventclick
15674 * Fires when the mouse click an
15675 * @param {Calendar} this
15684 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
15687 * @cfg {Number} startDay
15688 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15696 getAutoCreate : function(){
15699 var fc_button = function(name, corner, style, content ) {
15700 return Roo.apply({},{
15702 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
15704 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15707 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15718 style : 'width:100%',
15725 cls : 'fc-header-left',
15727 fc_button('prev', 'left', 'arrow', '‹' ),
15728 fc_button('next', 'right', 'arrow', '›' ),
15729 { tag: 'span', cls: 'fc-header-space' },
15730 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
15738 cls : 'fc-header-center',
15742 cls: 'fc-header-title',
15745 html : 'month / year'
15753 cls : 'fc-header-right',
15755 /* fc_button('month', 'left', '', 'month' ),
15756 fc_button('week', '', '', 'week' ),
15757 fc_button('day', 'right', '', 'day' )
15769 header = this.header;
15772 var cal_heads = function() {
15774 // fixme - handle this.
15776 for (var i =0; i < Date.dayNames.length; i++) {
15777 var d = Date.dayNames[i];
15780 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15781 html : d.substring(0,3)
15785 ret[0].cls += ' fc-first';
15786 ret[6].cls += ' fc-last';
15789 var cal_cell = function(n) {
15792 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15797 cls: 'fc-day-number',
15801 cls: 'fc-day-content',
15805 style: 'position: relative;' // height: 17px;
15817 var cal_rows = function() {
15820 for (var r = 0; r < 6; r++) {
15827 for (var i =0; i < Date.dayNames.length; i++) {
15828 var d = Date.dayNames[i];
15829 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15832 row.cn[0].cls+=' fc-first';
15833 row.cn[0].cn[0].style = 'min-height:90px';
15834 row.cn[6].cls+=' fc-last';
15838 ret[0].cls += ' fc-first';
15839 ret[4].cls += ' fc-prev-last';
15840 ret[5].cls += ' fc-last';
15847 cls: 'fc-border-separate',
15848 style : 'width:100%',
15856 cls : 'fc-first fc-last',
15874 cls : 'fc-content',
15875 style : "position: relative;",
15878 cls : 'fc-view fc-view-month fc-grid',
15879 style : 'position: relative',
15880 unselectable : 'on',
15883 cls : 'fc-event-container',
15884 style : 'position:absolute;z-index:8;top:0;left:0;'
15902 initEvents : function()
15905 throw "can not find store for calendar";
15911 style: "text-align:center",
15915 style: "background-color:white;width:50%;margin:250 auto",
15919 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
15930 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15932 var size = this.el.select('.fc-content', true).first().getSize();
15933 this.maskEl.setSize(size.width, size.height);
15934 this.maskEl.enableDisplayMode("block");
15935 if(!this.loadMask){
15936 this.maskEl.hide();
15939 this.store = Roo.factory(this.store, Roo.data);
15940 this.store.on('load', this.onLoad, this);
15941 this.store.on('beforeload', this.onBeforeLoad, this);
15945 this.cells = this.el.select('.fc-day',true);
15946 //Roo.log(this.cells);
15947 this.textNodes = this.el.query('.fc-day-number');
15948 this.cells.addClassOnOver('fc-state-hover');
15950 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15951 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15952 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15953 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15955 this.on('monthchange', this.onMonthChange, this);
15957 this.update(new Date().clearTime());
15960 resize : function() {
15961 var sz = this.el.getSize();
15963 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15964 this.el.select('.fc-day-content div',true).setHeight(34);
15969 showPrevMonth : function(e){
15970 this.update(this.activeDate.add("mo", -1));
15972 showToday : function(e){
15973 this.update(new Date().clearTime());
15976 showNextMonth : function(e){
15977 this.update(this.activeDate.add("mo", 1));
15981 showPrevYear : function(){
15982 this.update(this.activeDate.add("y", -1));
15986 showNextYear : function(){
15987 this.update(this.activeDate.add("y", 1));
15992 update : function(date)
15994 var vd = this.activeDate;
15995 this.activeDate = date;
15996 // if(vd && this.el){
15997 // var t = date.getTime();
15998 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15999 // Roo.log('using add remove');
16001 // this.fireEvent('monthchange', this, date);
16003 // this.cells.removeClass("fc-state-highlight");
16004 // this.cells.each(function(c){
16005 // if(c.dateValue == t){
16006 // c.addClass("fc-state-highlight");
16007 // setTimeout(function(){
16008 // try{c.dom.firstChild.focus();}catch(e){}
16018 var days = date.getDaysInMonth();
16020 var firstOfMonth = date.getFirstDateOfMonth();
16021 var startingPos = firstOfMonth.getDay()-this.startDay;
16023 if(startingPos < this.startDay){
16027 var pm = date.add(Date.MONTH, -1);
16028 var prevStart = pm.getDaysInMonth()-startingPos;
16030 this.cells = this.el.select('.fc-day',true);
16031 this.textNodes = this.el.query('.fc-day-number');
16032 this.cells.addClassOnOver('fc-state-hover');
16034 var cells = this.cells.elements;
16035 var textEls = this.textNodes;
16037 Roo.each(cells, function(cell){
16038 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16041 days += startingPos;
16043 // convert everything to numbers so it's fast
16044 var day = 86400000;
16045 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16048 //Roo.log(prevStart);
16050 var today = new Date().clearTime().getTime();
16051 var sel = date.clearTime().getTime();
16052 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16053 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16054 var ddMatch = this.disabledDatesRE;
16055 var ddText = this.disabledDatesText;
16056 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16057 var ddaysText = this.disabledDaysText;
16058 var format = this.format;
16060 var setCellClass = function(cal, cell){
16064 //Roo.log('set Cell Class');
16066 var t = d.getTime();
16070 cell.dateValue = t;
16072 cell.className += " fc-today";
16073 cell.className += " fc-state-highlight";
16074 cell.title = cal.todayText;
16077 // disable highlight in other month..
16078 //cell.className += " fc-state-highlight";
16083 cell.className = " fc-state-disabled";
16084 cell.title = cal.minText;
16088 cell.className = " fc-state-disabled";
16089 cell.title = cal.maxText;
16093 if(ddays.indexOf(d.getDay()) != -1){
16094 cell.title = ddaysText;
16095 cell.className = " fc-state-disabled";
16098 if(ddMatch && format){
16099 var fvalue = d.dateFormat(format);
16100 if(ddMatch.test(fvalue)){
16101 cell.title = ddText.replace("%0", fvalue);
16102 cell.className = " fc-state-disabled";
16106 if (!cell.initialClassName) {
16107 cell.initialClassName = cell.dom.className;
16110 cell.dom.className = cell.initialClassName + ' ' + cell.className;
16115 for(; i < startingPos; i++) {
16116 textEls[i].innerHTML = (++prevStart);
16117 d.setDate(d.getDate()+1);
16119 cells[i].className = "fc-past fc-other-month";
16120 setCellClass(this, cells[i]);
16125 for(; i < days; i++){
16126 intDay = i - startingPos + 1;
16127 textEls[i].innerHTML = (intDay);
16128 d.setDate(d.getDate()+1);
16130 cells[i].className = ''; // "x-date-active";
16131 setCellClass(this, cells[i]);
16135 for(; i < 42; i++) {
16136 textEls[i].innerHTML = (++extraDays);
16137 d.setDate(d.getDate()+1);
16139 cells[i].className = "fc-future fc-other-month";
16140 setCellClass(this, cells[i]);
16143 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16145 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16147 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16148 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16150 if(totalRows != 6){
16151 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16152 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16155 this.fireEvent('monthchange', this, date);
16159 if(!this.internalRender){
16160 var main = this.el.dom.firstChild;
16161 var w = main.offsetWidth;
16162 this.el.setWidth(w + this.el.getBorderWidth("lr"));
16163 Roo.fly(main).setWidth(w);
16164 this.internalRender = true;
16165 // opera does not respect the auto grow header center column
16166 // then, after it gets a width opera refuses to recalculate
16167 // without a second pass
16168 if(Roo.isOpera && !this.secondPass){
16169 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16170 this.secondPass = true;
16171 this.update.defer(10, this, [date]);
16178 findCell : function(dt) {
16179 dt = dt.clearTime().getTime();
16181 this.cells.each(function(c){
16182 //Roo.log("check " +c.dateValue + '?=' + dt);
16183 if(c.dateValue == dt){
16193 findCells : function(ev) {
16194 var s = ev.start.clone().clearTime().getTime();
16196 var e= ev.end.clone().clearTime().getTime();
16199 this.cells.each(function(c){
16200 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16202 if(c.dateValue > e){
16205 if(c.dateValue < s){
16214 // findBestRow: function(cells)
16218 // for (var i =0 ; i < cells.length;i++) {
16219 // ret = Math.max(cells[i].rows || 0,ret);
16226 addItem : function(ev)
16228 // look for vertical location slot in
16229 var cells = this.findCells(ev);
16231 // ev.row = this.findBestRow(cells);
16233 // work out the location.
16237 for(var i =0; i < cells.length; i++) {
16239 cells[i].row = cells[0].row;
16242 cells[i].row = cells[i].row + 1;
16252 if (crow.start.getY() == cells[i].getY()) {
16254 crow.end = cells[i];
16271 cells[0].events.push(ev);
16273 this.calevents.push(ev);
16276 clearEvents: function() {
16278 if(!this.calevents){
16282 Roo.each(this.cells.elements, function(c){
16288 Roo.each(this.calevents, function(e) {
16289 Roo.each(e.els, function(el) {
16290 el.un('mouseenter' ,this.onEventEnter, this);
16291 el.un('mouseleave' ,this.onEventLeave, this);
16296 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16302 renderEvents: function()
16306 this.cells.each(function(c) {
16315 if(c.row != c.events.length){
16316 r = 4 - (4 - (c.row - c.events.length));
16319 c.events = ev.slice(0, r);
16320 c.more = ev.slice(r);
16322 if(c.more.length && c.more.length == 1){
16323 c.events.push(c.more.pop());
16326 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16330 this.cells.each(function(c) {
16332 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16335 for (var e = 0; e < c.events.length; e++){
16336 var ev = c.events[e];
16337 var rows = ev.rows;
16339 for(var i = 0; i < rows.length; i++) {
16341 // how many rows should it span..
16344 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16345 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16347 unselectable : "on",
16350 cls: 'fc-event-inner',
16354 // cls: 'fc-event-time',
16355 // html : cells.length > 1 ? '' : ev.time
16359 cls: 'fc-event-title',
16360 html : String.format('{0}', ev.title)
16367 cls: 'ui-resizable-handle ui-resizable-e',
16368 html : '  '
16375 cfg.cls += ' fc-event-start';
16377 if ((i+1) == rows.length) {
16378 cfg.cls += ' fc-event-end';
16381 var ctr = _this.el.select('.fc-event-container',true).first();
16382 var cg = ctr.createChild(cfg);
16384 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16385 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16387 var r = (c.more.length) ? 1 : 0;
16388 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
16389 cg.setWidth(ebox.right - sbox.x -2);
16391 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16392 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16393 cg.on('click', _this.onEventClick, _this, ev);
16404 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16405 style : 'position: absolute',
16406 unselectable : "on",
16409 cls: 'fc-event-inner',
16413 cls: 'fc-event-title',
16421 cls: 'ui-resizable-handle ui-resizable-e',
16422 html : '  '
16428 var ctr = _this.el.select('.fc-event-container',true).first();
16429 var cg = ctr.createChild(cfg);
16431 var sbox = c.select('.fc-day-content',true).first().getBox();
16432 var ebox = c.select('.fc-day-content',true).first().getBox();
16434 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
16435 cg.setWidth(ebox.right - sbox.x -2);
16437 cg.on('click', _this.onMoreEventClick, _this, c.more);
16447 onEventEnter: function (e, el,event,d) {
16448 this.fireEvent('evententer', this, el, event);
16451 onEventLeave: function (e, el,event,d) {
16452 this.fireEvent('eventleave', this, el, event);
16455 onEventClick: function (e, el,event,d) {
16456 this.fireEvent('eventclick', this, el, event);
16459 onMonthChange: function () {
16463 onMoreEventClick: function(e, el, more)
16467 this.calpopover.placement = 'right';
16468 this.calpopover.setTitle('More');
16470 this.calpopover.setContent('');
16472 var ctr = this.calpopover.el.select('.popover-content', true).first();
16474 Roo.each(more, function(m){
16476 cls : 'fc-event-hori fc-event-draggable',
16479 var cg = ctr.createChild(cfg);
16481 cg.on('click', _this.onEventClick, _this, m);
16484 this.calpopover.show(el);
16489 onLoad: function ()
16491 this.calevents = [];
16494 if(this.store.getCount() > 0){
16495 this.store.data.each(function(d){
16498 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16499 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16500 time : d.data.start_time,
16501 title : d.data.title,
16502 description : d.data.description,
16503 venue : d.data.venue
16508 this.renderEvents();
16510 if(this.calevents.length && this.loadMask){
16511 this.maskEl.hide();
16515 onBeforeLoad: function()
16517 this.clearEvents();
16519 this.maskEl.show();
16533 * @class Roo.bootstrap.Popover
16534 * @extends Roo.bootstrap.Component
16535 * Bootstrap Popover class
16536 * @cfg {String} html contents of the popover (or false to use children..)
16537 * @cfg {String} title of popover (or false to hide)
16538 * @cfg {String} placement how it is placed
16539 * @cfg {String} trigger click || hover (or false to trigger manually)
16540 * @cfg {String} over what (parent or false to trigger manually.)
16541 * @cfg {Number} delay - delay before showing
16544 * Create a new Popover
16545 * @param {Object} config The config object
16548 Roo.bootstrap.Popover = function(config){
16549 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16555 * After the popover show
16557 * @param {Roo.bootstrap.Popover} this
16562 * After the popover hide
16564 * @param {Roo.bootstrap.Popover} this
16570 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
16572 title: 'Fill in a title',
16575 placement : 'right',
16576 trigger : 'hover', // hover
16582 can_build_overlaid : false,
16584 getChildContainer : function()
16586 return this.el.select('.popover-content',true).first();
16589 getAutoCreate : function(){
16592 cls : 'popover roo-dynamic',
16593 style: 'display:block',
16599 cls : 'popover-inner',
16603 cls: 'popover-title',
16607 cls : 'popover-content',
16618 setTitle: function(str)
16621 this.el.select('.popover-title',true).first().dom.innerHTML = str;
16623 setContent: function(str)
16626 this.el.select('.popover-content',true).first().dom.innerHTML = str;
16628 // as it get's added to the bottom of the page.
16629 onRender : function(ct, position)
16631 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16633 var cfg = Roo.apply({}, this.getAutoCreate());
16637 cfg.cls += ' ' + this.cls;
16640 cfg.style = this.style;
16642 //Roo.log("adding to ");
16643 this.el = Roo.get(document.body).createChild(cfg, position);
16644 // Roo.log(this.el);
16649 initEvents : function()
16651 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16652 this.el.enableDisplayMode('block');
16654 if (this.over === false) {
16657 if (this.triggers === false) {
16660 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16661 var triggers = this.trigger ? this.trigger.split(' ') : [];
16662 Roo.each(triggers, function(trigger) {
16664 if (trigger == 'click') {
16665 on_el.on('click', this.toggle, this);
16666 } else if (trigger != 'manual') {
16667 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin';
16668 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16670 on_el.on(eventIn ,this.enter, this);
16671 on_el.on(eventOut, this.leave, this);
16682 toggle : function () {
16683 this.hoverState == 'in' ? this.leave() : this.enter();
16686 enter : function () {
16688 clearTimeout(this.timeout);
16690 this.hoverState = 'in';
16692 if (!this.delay || !this.delay.show) {
16697 this.timeout = setTimeout(function () {
16698 if (_t.hoverState == 'in') {
16701 }, this.delay.show)
16704 leave : function() {
16705 clearTimeout(this.timeout);
16707 this.hoverState = 'out';
16709 if (!this.delay || !this.delay.hide) {
16714 this.timeout = setTimeout(function () {
16715 if (_t.hoverState == 'out') {
16718 }, this.delay.hide)
16721 show : function (on_el)
16724 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16728 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16729 if (this.html !== false) {
16730 this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16732 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16733 if (!this.title.length) {
16734 this.el.select('.popover-title',true).hide();
16737 var placement = typeof this.placement == 'function' ?
16738 this.placement.call(this, this.el, on_el) :
16741 var autoToken = /\s?auto?\s?/i;
16742 var autoPlace = autoToken.test(placement);
16744 placement = placement.replace(autoToken, '') || 'top';
16748 //this.el.setXY([0,0]);
16750 this.el.dom.style.display='block';
16751 this.el.addClass(placement);
16753 //this.el.appendTo(on_el);
16755 var p = this.getPosition();
16756 var box = this.el.getBox();
16761 var align = Roo.bootstrap.Popover.alignment[placement];
16762 this.el.alignTo(on_el, align[0],align[1]);
16763 //var arrow = this.el.select('.arrow',true).first();
16764 //arrow.set(align[2],
16766 this.el.addClass('in');
16769 if (this.el.hasClass('fade')) {
16773 this.hoverState = 'in';
16775 this.fireEvent('show', this);
16780 this.el.setXY([0,0]);
16781 this.el.removeClass('in');
16783 this.hoverState = null;
16785 this.fireEvent('hide', this);
16790 Roo.bootstrap.Popover.alignment = {
16791 'left' : ['r-l', [-10,0], 'right'],
16792 'right' : ['l-r', [10,0], 'left'],
16793 'bottom' : ['t-b', [0,10], 'top'],
16794 'top' : [ 'b-t', [0,-10], 'bottom']
16805 * @class Roo.bootstrap.Progress
16806 * @extends Roo.bootstrap.Component
16807 * Bootstrap Progress class
16808 * @cfg {Boolean} striped striped of the progress bar
16809 * @cfg {Boolean} active animated of the progress bar
16813 * Create a new Progress
16814 * @param {Object} config The config object
16817 Roo.bootstrap.Progress = function(config){
16818 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16821 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
16826 getAutoCreate : function(){
16834 cfg.cls += ' progress-striped';
16838 cfg.cls += ' active';
16857 * @class Roo.bootstrap.ProgressBar
16858 * @extends Roo.bootstrap.Component
16859 * Bootstrap ProgressBar class
16860 * @cfg {Number} aria_valuenow aria-value now
16861 * @cfg {Number} aria_valuemin aria-value min
16862 * @cfg {Number} aria_valuemax aria-value max
16863 * @cfg {String} label label for the progress bar
16864 * @cfg {String} panel (success | info | warning | danger )
16865 * @cfg {String} role role of the progress bar
16866 * @cfg {String} sr_only text
16870 * Create a new ProgressBar
16871 * @param {Object} config The config object
16874 Roo.bootstrap.ProgressBar = function(config){
16875 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16878 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
16882 aria_valuemax : 100,
16888 getAutoCreate : function()
16893 cls: 'progress-bar',
16894 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16906 cfg.role = this.role;
16909 if(this.aria_valuenow){
16910 cfg['aria-valuenow'] = this.aria_valuenow;
16913 if(this.aria_valuemin){
16914 cfg['aria-valuemin'] = this.aria_valuemin;
16917 if(this.aria_valuemax){
16918 cfg['aria-valuemax'] = this.aria_valuemax;
16921 if(this.label && !this.sr_only){
16922 cfg.html = this.label;
16926 cfg.cls += ' progress-bar-' + this.panel;
16932 update : function(aria_valuenow)
16934 this.aria_valuenow = aria_valuenow;
16936 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16951 * @class Roo.bootstrap.TabGroup
16952 * @extends Roo.bootstrap.Column
16953 * Bootstrap Column class
16954 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16955 * @cfg {Boolean} carousel true to make the group behave like a carousel
16956 * @cfg {Boolean} bullets show bullets for the panels
16957 * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16958 * @cfg {Number} timer auto slide timer .. default 0 millisecond
16959 * @cfg {Boolean} showarrow (true|false) show arrow default true
16962 * Create a new TabGroup
16963 * @param {Object} config The config object
16966 Roo.bootstrap.TabGroup = function(config){
16967 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16969 this.navId = Roo.id();
16972 Roo.bootstrap.TabGroup.register(this);
16976 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
16979 transition : false,
16984 slideOnTouch : false,
16987 getAutoCreate : function()
16989 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16991 cfg.cls += ' tab-content';
16993 if (this.carousel) {
16994 cfg.cls += ' carousel slide';
16997 cls : 'carousel-inner',
17001 if(this.bullets && !Roo.isTouch){
17004 cls : 'carousel-bullets',
17008 if(this.bullets_cls){
17009 bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17016 cfg.cn[0].cn.push(bullets);
17019 if(this.showarrow){
17020 cfg.cn[0].cn.push({
17022 class : 'carousel-arrow',
17026 class : 'carousel-prev',
17030 class : 'fa fa-chevron-left'
17036 class : 'carousel-next',
17040 class : 'fa fa-chevron-right'
17053 initEvents: function()
17055 // if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17056 // this.el.on("touchstart", this.onTouchStart, this);
17059 if(this.autoslide){
17062 this.slideFn = window.setInterval(function() {
17063 _this.showPanelNext();
17067 if(this.showarrow){
17068 this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17069 this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17075 // onTouchStart : function(e, el, o)
17077 // if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17081 // this.showPanelNext();
17085 getChildContainer : function()
17087 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17091 * register a Navigation item
17092 * @param {Roo.bootstrap.NavItem} the navitem to add
17094 register : function(item)
17096 this.tabs.push( item);
17097 item.navId = this.navId; // not really needed..
17102 getActivePanel : function()
17105 Roo.each(this.tabs, function(t) {
17115 getPanelByName : function(n)
17118 Roo.each(this.tabs, function(t) {
17119 if (t.tabId == n) {
17127 indexOfPanel : function(p)
17130 Roo.each(this.tabs, function(t,i) {
17131 if (t.tabId == p.tabId) {
17140 * show a specific panel
17141 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17142 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17144 showPanel : function (pan)
17146 if(this.transition || typeof(pan) == 'undefined'){
17147 Roo.log("waiting for the transitionend");
17151 if (typeof(pan) == 'number') {
17152 pan = this.tabs[pan];
17155 if (typeof(pan) == 'string') {
17156 pan = this.getPanelByName(pan);
17159 var cur = this.getActivePanel();
17162 Roo.log('pan or acitve pan is undefined');
17166 if (pan.tabId == this.getActivePanel().tabId) {
17170 if (false === cur.fireEvent('beforedeactivate')) {
17174 if(this.bullets > 0 && !Roo.isTouch){
17175 this.setActiveBullet(this.indexOfPanel(pan));
17178 if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17180 this.transition = true;
17181 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
17182 var lr = dir == 'next' ? 'left' : 'right';
17183 pan.el.addClass(dir); // or prev
17184 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17185 cur.el.addClass(lr); // or right
17186 pan.el.addClass(lr);
17189 cur.el.on('transitionend', function() {
17190 Roo.log("trans end?");
17192 pan.el.removeClass([lr,dir]);
17193 pan.setActive(true);
17195 cur.el.removeClass([lr]);
17196 cur.setActive(false);
17198 _this.transition = false;
17200 }, this, { single: true } );
17205 cur.setActive(false);
17206 pan.setActive(true);
17211 showPanelNext : function()
17213 var i = this.indexOfPanel(this.getActivePanel());
17215 if (i >= this.tabs.length - 1 && !this.autoslide) {
17219 if (i >= this.tabs.length - 1 && this.autoslide) {
17223 this.showPanel(this.tabs[i+1]);
17226 showPanelPrev : function()
17228 var i = this.indexOfPanel(this.getActivePanel());
17230 if (i < 1 && !this.autoslide) {
17234 if (i < 1 && this.autoslide) {
17235 i = this.tabs.length;
17238 this.showPanel(this.tabs[i-1]);
17242 addBullet: function()
17244 if(!this.bullets || Roo.isTouch){
17247 var ctr = this.el.select('.carousel-bullets',true).first();
17248 var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17249 var bullet = ctr.createChild({
17250 cls : 'bullet bullet-' + i
17251 },ctr.dom.lastChild);
17256 bullet.on('click', (function(e, el, o, ii, t){
17258 e.preventDefault();
17260 this.showPanel(ii);
17262 if(this.autoslide && this.slideFn){
17263 clearInterval(this.slideFn);
17264 this.slideFn = window.setInterval(function() {
17265 _this.showPanelNext();
17269 }).createDelegate(this, [i, bullet], true));
17274 setActiveBullet : function(i)
17280 Roo.each(this.el.select('.bullet', true).elements, function(el){
17281 el.removeClass('selected');
17284 var bullet = this.el.select('.bullet-' + i, true).first();
17290 bullet.addClass('selected');
17301 Roo.apply(Roo.bootstrap.TabGroup, {
17305 * register a Navigation Group
17306 * @param {Roo.bootstrap.NavGroup} the navgroup to add
17308 register : function(navgrp)
17310 this.groups[navgrp.navId] = navgrp;
17314 * fetch a Navigation Group based on the navigation ID
17315 * if one does not exist , it will get created.
17316 * @param {string} the navgroup to add
17317 * @returns {Roo.bootstrap.NavGroup} the navgroup
17319 get: function(navId) {
17320 if (typeof(this.groups[navId]) == 'undefined') {
17321 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17323 return this.groups[navId] ;
17338 * @class Roo.bootstrap.TabPanel
17339 * @extends Roo.bootstrap.Component
17340 * Bootstrap TabPanel class
17341 * @cfg {Boolean} active panel active
17342 * @cfg {String} html panel content
17343 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17344 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17345 * @cfg {String} href click to link..
17349 * Create a new TabPanel
17350 * @param {Object} config The config object
17353 Roo.bootstrap.TabPanel = function(config){
17354 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17358 * Fires when the active status changes
17359 * @param {Roo.bootstrap.TabPanel} this
17360 * @param {Boolean} state the new state
17365 * @event beforedeactivate
17366 * Fires before a tab is de-activated - can be used to do validation on a form.
17367 * @param {Roo.bootstrap.TabPanel} this
17368 * @return {Boolean} false if there is an error
17371 'beforedeactivate': true
17374 this.tabId = this.tabId || Roo.id();
17378 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
17386 getAutoCreate : function(){
17389 // item is needed for carousel - not sure if it has any effect otherwise
17390 cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17391 html: this.html || ''
17395 cfg.cls += ' active';
17399 cfg.tabId = this.tabId;
17406 initEvents: function()
17408 var p = this.parent();
17410 this.navId = this.navId || p.navId;
17412 if (typeof(this.navId) != 'undefined') {
17413 // not really needed.. but just in case.. parent should be a NavGroup.
17414 var tg = Roo.bootstrap.TabGroup.get(this.navId);
17418 var i = tg.tabs.length - 1;
17420 if(this.active && tg.bullets > 0 && i < tg.bullets){
17421 tg.setActiveBullet(i);
17425 this.el.on('click', this.onClick, this);
17428 this.el.on("touchstart", this.onTouchStart, this);
17429 this.el.on("touchmove", this.onTouchMove, this);
17430 this.el.on("touchend", this.onTouchEnd, this);
17435 onRender : function(ct, position)
17437 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17440 setActive : function(state)
17442 Roo.log("panel - set active " + this.tabId + "=" + state);
17444 this.active = state;
17446 this.el.removeClass('active');
17448 } else if (!this.el.hasClass('active')) {
17449 this.el.addClass('active');
17452 this.fireEvent('changed', this, state);
17455 onClick : function(e)
17457 e.preventDefault();
17459 if(!this.href.length){
17463 window.location.href = this.href;
17472 onTouchStart : function(e)
17474 this.swiping = false;
17476 this.startX = e.browserEvent.touches[0].clientX;
17477 this.startY = e.browserEvent.touches[0].clientY;
17480 onTouchMove : function(e)
17482 this.swiping = true;
17484 this.endX = e.browserEvent.touches[0].clientX;
17485 this.endY = e.browserEvent.touches[0].clientY;
17488 onTouchEnd : function(e)
17495 var tabGroup = this.parent();
17497 if(this.endX > this.startX){ // swiping right
17498 tabGroup.showPanelPrev();
17502 if(this.startX > this.endX){ // swiping left
17503 tabGroup.showPanelNext();
17522 * @class Roo.bootstrap.DateField
17523 * @extends Roo.bootstrap.Input
17524 * Bootstrap DateField class
17525 * @cfg {Number} weekStart default 0
17526 * @cfg {String} viewMode default empty, (months|years)
17527 * @cfg {String} minViewMode default empty, (months|years)
17528 * @cfg {Number} startDate default -Infinity
17529 * @cfg {Number} endDate default Infinity
17530 * @cfg {Boolean} todayHighlight default false
17531 * @cfg {Boolean} todayBtn default false
17532 * @cfg {Boolean} calendarWeeks default false
17533 * @cfg {Object} daysOfWeekDisabled default empty
17534 * @cfg {Boolean} singleMode default false (true | false)
17536 * @cfg {Boolean} keyboardNavigation default true
17537 * @cfg {String} language default en
17540 * Create a new DateField
17541 * @param {Object} config The config object
17544 Roo.bootstrap.DateField = function(config){
17545 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
17549 * Fires when this field show.
17550 * @param {Roo.bootstrap.DateField} this
17551 * @param {Mixed} date The date value
17556 * Fires when this field hide.
17557 * @param {Roo.bootstrap.DateField} this
17558 * @param {Mixed} date The date value
17563 * Fires when select a date.
17564 * @param {Roo.bootstrap.DateField} this
17565 * @param {Mixed} date The date value
17569 * @event beforeselect
17570 * Fires when before select a date.
17571 * @param {Roo.bootstrap.DateField} this
17572 * @param {Mixed} date The date value
17574 beforeselect : true
17578 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
17581 * @cfg {String} format
17582 * The default date format string which can be overriden for localization support. The format must be
17583 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
17587 * @cfg {String} altFormats
17588 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
17589 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
17591 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
17599 todayHighlight : false,
17605 keyboardNavigation: true,
17607 calendarWeeks: false,
17609 startDate: -Infinity,
17613 daysOfWeekDisabled: [],
17617 singleMode : false,
17619 UTCDate: function()
17621 return new Date(Date.UTC.apply(Date, arguments));
17624 UTCToday: function()
17626 var today = new Date();
17627 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17630 getDate: function() {
17631 var d = this.getUTCDate();
17632 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17635 getUTCDate: function() {
17639 setDate: function(d) {
17640 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17643 setUTCDate: function(d) {
17645 this.setValue(this.formatDate(this.date));
17648 onRender: function(ct, position)
17651 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17653 this.language = this.language || 'en';
17654 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17655 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17657 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17658 this.format = this.format || 'm/d/y';
17659 this.isInline = false;
17660 this.isInput = true;
17661 this.component = this.el.select('.add-on', true).first() || false;
17662 this.component = (this.component && this.component.length === 0) ? false : this.component;
17663 this.hasInput = this.component && this.inputEl().length;
17665 if (typeof(this.minViewMode === 'string')) {
17666 switch (this.minViewMode) {
17668 this.minViewMode = 1;
17671 this.minViewMode = 2;
17674 this.minViewMode = 0;
17679 if (typeof(this.viewMode === 'string')) {
17680 switch (this.viewMode) {
17693 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17695 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17697 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17699 this.picker().on('mousedown', this.onMousedown, this);
17700 this.picker().on('click', this.onClick, this);
17702 this.picker().addClass('datepicker-dropdown');
17704 this.startViewMode = this.viewMode;
17706 if(this.singleMode){
17707 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17708 v.setVisibilityMode(Roo.Element.DISPLAY);
17712 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17713 v.setStyle('width', '189px');
17717 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17718 if(!this.calendarWeeks){
17723 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17724 v.attr('colspan', function(i, val){
17725 return parseInt(val) + 1;
17730 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17732 this.setStartDate(this.startDate);
17733 this.setEndDate(this.endDate);
17735 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17742 if(this.isInline) {
17747 picker : function()
17749 return this.pickerEl;
17750 // return this.el.select('.datepicker', true).first();
17753 fillDow: function()
17755 var dowCnt = this.weekStart;
17764 if(this.calendarWeeks){
17772 while (dowCnt < this.weekStart + 7) {
17776 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17780 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17783 fillMonths: function()
17786 var months = this.picker().select('>.datepicker-months td', true).first();
17788 months.dom.innerHTML = '';
17794 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17797 months.createChild(month);
17804 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;
17806 if (this.date < this.startDate) {
17807 this.viewDate = new Date(this.startDate);
17808 } else if (this.date > this.endDate) {
17809 this.viewDate = new Date(this.endDate);
17811 this.viewDate = new Date(this.date);
17819 var d = new Date(this.viewDate),
17820 year = d.getUTCFullYear(),
17821 month = d.getUTCMonth(),
17822 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17823 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17824 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17825 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17826 currentDate = this.date && this.date.valueOf(),
17827 today = this.UTCToday();
17829 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17831 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17833 // this.picker.select('>tfoot th.today').
17834 // .text(dates[this.language].today)
17835 // .toggle(this.todayBtn !== false);
17837 this.updateNavArrows();
17840 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17842 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17844 prevMonth.setUTCDate(day);
17846 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17848 var nextMonth = new Date(prevMonth);
17850 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17852 nextMonth = nextMonth.valueOf();
17854 var fillMonths = false;
17856 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17858 while(prevMonth.valueOf() < nextMonth) {
17861 if (prevMonth.getUTCDay() === this.weekStart) {
17863 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17871 if(this.calendarWeeks){
17872 // ISO 8601: First week contains first thursday.
17873 // ISO also states week starts on Monday, but we can be more abstract here.
17875 // Start of current week: based on weekstart/current date
17876 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17877 // Thursday of this week
17878 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17879 // First Thursday of year, year from thursday
17880 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17881 // Calendar week: ms between thursdays, div ms per day, div 7 days
17882 calWeek = (th - yth) / 864e5 / 7 + 1;
17884 fillMonths.cn.push({
17892 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17894 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17897 if (this.todayHighlight &&
17898 prevMonth.getUTCFullYear() == today.getFullYear() &&
17899 prevMonth.getUTCMonth() == today.getMonth() &&
17900 prevMonth.getUTCDate() == today.getDate()) {
17901 clsName += ' today';
17904 if (currentDate && prevMonth.valueOf() === currentDate) {
17905 clsName += ' active';
17908 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17909 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17910 clsName += ' disabled';
17913 fillMonths.cn.push({
17915 cls: 'day ' + clsName,
17916 html: prevMonth.getDate()
17919 prevMonth.setDate(prevMonth.getDate()+1);
17922 var currentYear = this.date && this.date.getUTCFullYear();
17923 var currentMonth = this.date && this.date.getUTCMonth();
17925 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17927 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17928 v.removeClass('active');
17930 if(currentYear === year && k === currentMonth){
17931 v.addClass('active');
17934 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17935 v.addClass('disabled');
17941 year = parseInt(year/10, 10) * 10;
17943 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17945 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17948 for (var i = -1; i < 11; i++) {
17949 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17951 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17959 showMode: function(dir)
17962 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17965 Roo.each(this.picker().select('>div',true).elements, function(v){
17966 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17969 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17974 if(this.isInline) {
17978 this.picker().removeClass(['bottom', 'top']);
17980 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17982 * place to the top of element!
17986 this.picker().addClass('top');
17987 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17992 this.picker().addClass('bottom');
17994 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17997 parseDate : function(value)
17999 if(!value || value instanceof Date){
18002 var v = Date.parseDate(value, this.format);
18003 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18004 v = Date.parseDate(value, 'Y-m-d');
18006 if(!v && this.altFormats){
18007 if(!this.altFormatsArray){
18008 this.altFormatsArray = this.altFormats.split("|");
18010 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18011 v = Date.parseDate(value, this.altFormatsArray[i]);
18017 formatDate : function(date, fmt)
18019 return (!date || !(date instanceof Date)) ?
18020 date : date.dateFormat(fmt || this.format);
18023 onFocus : function()
18025 Roo.bootstrap.DateField.superclass.onFocus.call(this);
18029 onBlur : function()
18031 Roo.bootstrap.DateField.superclass.onBlur.call(this);
18033 var d = this.inputEl().getValue();
18042 this.picker().show();
18046 this.fireEvent('show', this, this.date);
18051 if(this.isInline) {
18054 this.picker().hide();
18055 this.viewMode = this.startViewMode;
18058 this.fireEvent('hide', this, this.date);
18062 onMousedown: function(e)
18064 e.stopPropagation();
18065 e.preventDefault();
18070 Roo.bootstrap.DateField.superclass.keyup.call(this);
18074 setValue: function(v)
18076 if(this.fireEvent('beforeselect', this, v) !== false){
18077 var d = new Date(this.parseDate(v) ).clearTime();
18079 if(isNaN(d.getTime())){
18080 this.date = this.viewDate = '';
18081 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18085 v = this.formatDate(d);
18087 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18089 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18093 this.fireEvent('select', this, this.date);
18097 getValue: function()
18099 return this.formatDate(this.date);
18102 fireKey: function(e)
18104 if (!this.picker().isVisible()){
18105 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18111 var dateChanged = false,
18113 newDate, newViewDate;
18118 e.preventDefault();
18122 if (!this.keyboardNavigation) {
18125 dir = e.keyCode == 37 ? -1 : 1;
18128 newDate = this.moveYear(this.date, dir);
18129 newViewDate = this.moveYear(this.viewDate, dir);
18130 } else if (e.shiftKey){
18131 newDate = this.moveMonth(this.date, dir);
18132 newViewDate = this.moveMonth(this.viewDate, dir);
18134 newDate = new Date(this.date);
18135 newDate.setUTCDate(this.date.getUTCDate() + dir);
18136 newViewDate = new Date(this.viewDate);
18137 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18139 if (this.dateWithinRange(newDate)){
18140 this.date = newDate;
18141 this.viewDate = newViewDate;
18142 this.setValue(this.formatDate(this.date));
18144 e.preventDefault();
18145 dateChanged = true;
18150 if (!this.keyboardNavigation) {
18153 dir = e.keyCode == 38 ? -1 : 1;
18155 newDate = this.moveYear(this.date, dir);
18156 newViewDate = this.moveYear(this.viewDate, dir);
18157 } else if (e.shiftKey){
18158 newDate = this.moveMonth(this.date, dir);
18159 newViewDate = this.moveMonth(this.viewDate, dir);
18161 newDate = new Date(this.date);
18162 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18163 newViewDate = new Date(this.viewDate);
18164 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18166 if (this.dateWithinRange(newDate)){
18167 this.date = newDate;
18168 this.viewDate = newViewDate;
18169 this.setValue(this.formatDate(this.date));
18171 e.preventDefault();
18172 dateChanged = true;
18176 this.setValue(this.formatDate(this.date));
18178 e.preventDefault();
18181 this.setValue(this.formatDate(this.date));
18195 onClick: function(e)
18197 e.stopPropagation();
18198 e.preventDefault();
18200 var target = e.getTarget();
18202 if(target.nodeName.toLowerCase() === 'i'){
18203 target = Roo.get(target).dom.parentNode;
18206 var nodeName = target.nodeName;
18207 var className = target.className;
18208 var html = target.innerHTML;
18209 //Roo.log(nodeName);
18211 switch(nodeName.toLowerCase()) {
18213 switch(className) {
18219 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18220 switch(this.viewMode){
18222 this.viewDate = this.moveMonth(this.viewDate, dir);
18226 this.viewDate = this.moveYear(this.viewDate, dir);
18232 var date = new Date();
18233 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18235 this.setValue(this.formatDate(this.date));
18242 if (className.indexOf('disabled') < 0) {
18243 this.viewDate.setUTCDate(1);
18244 if (className.indexOf('month') > -1) {
18245 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18247 var year = parseInt(html, 10) || 0;
18248 this.viewDate.setUTCFullYear(year);
18252 if(this.singleMode){
18253 this.setValue(this.formatDate(this.viewDate));
18264 //Roo.log(className);
18265 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18266 var day = parseInt(html, 10) || 1;
18267 var year = this.viewDate.getUTCFullYear(),
18268 month = this.viewDate.getUTCMonth();
18270 if (className.indexOf('old') > -1) {
18277 } else if (className.indexOf('new') > -1) {
18285 //Roo.log([year,month,day]);
18286 this.date = this.UTCDate(year, month, day,0,0,0,0);
18287 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18289 //Roo.log(this.formatDate(this.date));
18290 this.setValue(this.formatDate(this.date));
18297 setStartDate: function(startDate)
18299 this.startDate = startDate || -Infinity;
18300 if (this.startDate !== -Infinity) {
18301 this.startDate = this.parseDate(this.startDate);
18304 this.updateNavArrows();
18307 setEndDate: function(endDate)
18309 this.endDate = endDate || Infinity;
18310 if (this.endDate !== Infinity) {
18311 this.endDate = this.parseDate(this.endDate);
18314 this.updateNavArrows();
18317 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18319 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18320 if (typeof(this.daysOfWeekDisabled) !== 'object') {
18321 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18323 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18324 return parseInt(d, 10);
18327 this.updateNavArrows();
18330 updateNavArrows: function()
18332 if(this.singleMode){
18336 var d = new Date(this.viewDate),
18337 year = d.getUTCFullYear(),
18338 month = d.getUTCMonth();
18340 Roo.each(this.picker().select('.prev', true).elements, function(v){
18342 switch (this.viewMode) {
18345 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18351 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18358 Roo.each(this.picker().select('.next', true).elements, function(v){
18360 switch (this.viewMode) {
18363 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18369 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18377 moveMonth: function(date, dir)
18382 var new_date = new Date(date.valueOf()),
18383 day = new_date.getUTCDate(),
18384 month = new_date.getUTCMonth(),
18385 mag = Math.abs(dir),
18387 dir = dir > 0 ? 1 : -1;
18390 // If going back one month, make sure month is not current month
18391 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18393 return new_date.getUTCMonth() == month;
18395 // If going forward one month, make sure month is as expected
18396 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18398 return new_date.getUTCMonth() != new_month;
18400 new_month = month + dir;
18401 new_date.setUTCMonth(new_month);
18402 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18403 if (new_month < 0 || new_month > 11) {
18404 new_month = (new_month + 12) % 12;
18407 // For magnitudes >1, move one month at a time...
18408 for (var i=0; i<mag; i++) {
18409 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18410 new_date = this.moveMonth(new_date, dir);
18412 // ...then reset the day, keeping it in the new month
18413 new_month = new_date.getUTCMonth();
18414 new_date.setUTCDate(day);
18416 return new_month != new_date.getUTCMonth();
18419 // Common date-resetting loop -- if date is beyond end of month, make it
18422 new_date.setUTCDate(--day);
18423 new_date.setUTCMonth(new_month);
18428 moveYear: function(date, dir)
18430 return this.moveMonth(date, dir*12);
18433 dateWithinRange: function(date)
18435 return date >= this.startDate && date <= this.endDate;
18441 this.picker().remove();
18444 validateValue : function(value)
18446 if(value.length < 1) {
18447 if(this.allowBlank){
18453 if(value.length < this.minLength){
18456 if(value.length > this.maxLength){
18460 var vt = Roo.form.VTypes;
18461 if(!vt[this.vtype](value, this)){
18465 if(typeof this.validator == "function"){
18466 var msg = this.validator(value);
18472 if(this.regex && !this.regex.test(value)){
18476 if(typeof(this.parseDate(value)) == 'undefined'){
18480 if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18484 if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18494 Roo.apply(Roo.bootstrap.DateField, {
18505 html: '<i class="fa fa-arrow-left"/>'
18515 html: '<i class="fa fa-arrow-right"/>'
18557 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
18558 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
18559 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
18560 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18561 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
18574 navFnc: 'FullYear',
18579 navFnc: 'FullYear',
18584 Roo.apply(Roo.bootstrap.DateField, {
18588 cls: 'datepicker dropdown-menu roo-dynamic',
18592 cls: 'datepicker-days',
18596 cls: 'table-condensed',
18598 Roo.bootstrap.DateField.head,
18602 Roo.bootstrap.DateField.footer
18609 cls: 'datepicker-months',
18613 cls: 'table-condensed',
18615 Roo.bootstrap.DateField.head,
18616 Roo.bootstrap.DateField.content,
18617 Roo.bootstrap.DateField.footer
18624 cls: 'datepicker-years',
18628 cls: 'table-condensed',
18630 Roo.bootstrap.DateField.head,
18631 Roo.bootstrap.DateField.content,
18632 Roo.bootstrap.DateField.footer
18651 * @class Roo.bootstrap.TimeField
18652 * @extends Roo.bootstrap.Input
18653 * Bootstrap DateField class
18657 * Create a new TimeField
18658 * @param {Object} config The config object
18661 Roo.bootstrap.TimeField = function(config){
18662 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18666 * Fires when this field show.
18667 * @param {Roo.bootstrap.DateField} thisthis
18668 * @param {Mixed} date The date value
18673 * Fires when this field hide.
18674 * @param {Roo.bootstrap.DateField} this
18675 * @param {Mixed} date The date value
18680 * Fires when select a date.
18681 * @param {Roo.bootstrap.DateField} this
18682 * @param {Mixed} date The date value
18688 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
18691 * @cfg {String} format
18692 * The default time format string which can be overriden for localization support. The format must be
18693 * valid according to {@link Date#parseDate} (defaults to 'H:i').
18697 onRender: function(ct, position)
18700 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18702 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18704 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18706 this.pop = this.picker().select('>.datepicker-time',true).first();
18707 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18709 this.picker().on('mousedown', this.onMousedown, this);
18710 this.picker().on('click', this.onClick, this);
18712 this.picker().addClass('datepicker-dropdown');
18717 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18718 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18719 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18720 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18721 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18722 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18726 fireKey: function(e){
18727 if (!this.picker().isVisible()){
18728 if (e.keyCode == 27) { // allow escape to hide and re-show picker
18734 e.preventDefault();
18742 this.onTogglePeriod();
18745 this.onIncrementMinutes();
18748 this.onDecrementMinutes();
18757 onClick: function(e) {
18758 e.stopPropagation();
18759 e.preventDefault();
18762 picker : function()
18764 return this.el.select('.datepicker', true).first();
18767 fillTime: function()
18769 var time = this.pop.select('tbody', true).first();
18771 time.dom.innerHTML = '';
18786 cls: 'hours-up glyphicon glyphicon-chevron-up'
18806 cls: 'minutes-up glyphicon glyphicon-chevron-up'
18827 cls: 'timepicker-hour',
18842 cls: 'timepicker-minute',
18857 cls: 'btn btn-primary period',
18879 cls: 'hours-down glyphicon glyphicon-chevron-down'
18899 cls: 'minutes-down glyphicon glyphicon-chevron-down'
18917 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18924 var hours = this.time.getHours();
18925 var minutes = this.time.getMinutes();
18938 hours = hours - 12;
18942 hours = '0' + hours;
18946 minutes = '0' + minutes;
18949 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18950 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18951 this.pop.select('button', true).first().dom.innerHTML = period;
18957 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18959 var cls = ['bottom'];
18961 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18968 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18973 this.picker().addClass(cls.join('-'));
18977 Roo.each(cls, function(c){
18979 _this.picker().setTop(_this.inputEl().getHeight());
18983 _this.picker().setTop(0 - _this.picker().getHeight());
18988 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18992 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18999 onFocus : function()
19001 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19005 onBlur : function()
19007 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19013 this.picker().show();
19018 this.fireEvent('show', this, this.date);
19023 this.picker().hide();
19026 this.fireEvent('hide', this, this.date);
19029 setTime : function()
19032 this.setValue(this.time.format(this.format));
19034 this.fireEvent('select', this, this.date);
19039 onMousedown: function(e){
19040 e.stopPropagation();
19041 e.preventDefault();
19044 onIncrementHours: function()
19046 Roo.log('onIncrementHours');
19047 this.time = this.time.add(Date.HOUR, 1);
19052 onDecrementHours: function()
19054 Roo.log('onDecrementHours');
19055 this.time = this.time.add(Date.HOUR, -1);
19059 onIncrementMinutes: function()
19061 Roo.log('onIncrementMinutes');
19062 this.time = this.time.add(Date.MINUTE, 1);
19066 onDecrementMinutes: function()
19068 Roo.log('onDecrementMinutes');
19069 this.time = this.time.add(Date.MINUTE, -1);
19073 onTogglePeriod: function()
19075 Roo.log('onTogglePeriod');
19076 this.time = this.time.add(Date.HOUR, 12);
19083 Roo.apply(Roo.bootstrap.TimeField, {
19113 cls: 'btn btn-info ok',
19125 Roo.apply(Roo.bootstrap.TimeField, {
19129 cls: 'datepicker dropdown-menu',
19133 cls: 'datepicker-time',
19137 cls: 'table-condensed',
19139 Roo.bootstrap.TimeField.content,
19140 Roo.bootstrap.TimeField.footer
19159 * @class Roo.bootstrap.MonthField
19160 * @extends Roo.bootstrap.Input
19161 * Bootstrap MonthField class
19163 * @cfg {String} language default en
19166 * Create a new MonthField
19167 * @param {Object} config The config object
19170 Roo.bootstrap.MonthField = function(config){
19171 Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19176 * Fires when this field show.
19177 * @param {Roo.bootstrap.MonthField} this
19178 * @param {Mixed} date The date value
19183 * Fires when this field hide.
19184 * @param {Roo.bootstrap.MonthField} this
19185 * @param {Mixed} date The date value
19190 * Fires when select a date.
19191 * @param {Roo.bootstrap.MonthField} this
19192 * @param {String} oldvalue The old value
19193 * @param {String} newvalue The new value
19199 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input, {
19201 onRender: function(ct, position)
19204 Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19206 this.language = this.language || 'en';
19207 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19208 this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19210 this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19211 this.isInline = false;
19212 this.isInput = true;
19213 this.component = this.el.select('.add-on', true).first() || false;
19214 this.component = (this.component && this.component.length === 0) ? false : this.component;
19215 this.hasInput = this.component && this.inputEL().length;
19217 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19219 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19221 this.picker().on('mousedown', this.onMousedown, this);
19222 this.picker().on('click', this.onClick, this);
19224 this.picker().addClass('datepicker-dropdown');
19226 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19227 v.setStyle('width', '189px');
19234 if(this.isInline) {
19240 setValue: function(v, suppressEvent)
19242 var o = this.getValue();
19244 Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19248 if(suppressEvent !== true){
19249 this.fireEvent('select', this, o, v);
19254 getValue: function()
19259 onClick: function(e)
19261 e.stopPropagation();
19262 e.preventDefault();
19264 var target = e.getTarget();
19266 if(target.nodeName.toLowerCase() === 'i'){
19267 target = Roo.get(target).dom.parentNode;
19270 var nodeName = target.nodeName;
19271 var className = target.className;
19272 var html = target.innerHTML;
19274 if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19278 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19280 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19286 picker : function()
19288 return this.pickerEl;
19291 fillMonths: function()
19294 var months = this.picker().select('>.datepicker-months td', true).first();
19296 months.dom.innerHTML = '';
19302 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19305 months.createChild(month);
19314 if(typeof(this.vIndex) == 'undefined' && this.value.length){
19315 this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19318 Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19319 e.removeClass('active');
19321 if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19322 e.addClass('active');
19329 if(this.isInline) {
19333 this.picker().removeClass(['bottom', 'top']);
19335 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19337 * place to the top of element!
19341 this.picker().addClass('top');
19342 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19347 this.picker().addClass('bottom');
19349 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19352 onFocus : function()
19354 Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19358 onBlur : function()
19360 Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19362 var d = this.inputEl().getValue();
19371 this.picker().show();
19372 this.picker().select('>.datepicker-months', true).first().show();
19376 this.fireEvent('show', this, this.date);
19381 if(this.isInline) {
19384 this.picker().hide();
19385 this.fireEvent('hide', this, this.date);
19389 onMousedown: function(e)
19391 e.stopPropagation();
19392 e.preventDefault();
19397 Roo.bootstrap.MonthField.superclass.keyup.call(this);
19401 fireKey: function(e)
19403 if (!this.picker().isVisible()){
19404 if (e.keyCode == 27) {// allow escape to hide and re-show picker
19415 e.preventDefault();
19419 dir = e.keyCode == 37 ? -1 : 1;
19421 this.vIndex = this.vIndex + dir;
19423 if(this.vIndex < 0){
19427 if(this.vIndex > 11){
19431 if(isNaN(this.vIndex)){
19435 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19441 dir = e.keyCode == 38 ? -1 : 1;
19443 this.vIndex = this.vIndex + dir * 4;
19445 if(this.vIndex < 0){
19449 if(this.vIndex > 11){
19453 if(isNaN(this.vIndex)){
19457 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19462 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19463 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19467 e.preventDefault();
19470 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19471 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19487 this.picker().remove();
19492 Roo.apply(Roo.bootstrap.MonthField, {
19511 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19512 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19517 Roo.apply(Roo.bootstrap.MonthField, {
19521 cls: 'datepicker dropdown-menu roo-dynamic',
19525 cls: 'datepicker-months',
19529 cls: 'table-condensed',
19531 Roo.bootstrap.DateField.content
19551 * @class Roo.bootstrap.CheckBox
19552 * @extends Roo.bootstrap.Input
19553 * Bootstrap CheckBox class
19555 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
19556 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
19557 * @cfg {String} boxLabel The text that appears beside the checkbox
19558 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
19559 * @cfg {Boolean} checked initnal the element
19560 * @cfg {Boolean} inline inline the element (default false)
19561 * @cfg {String} groupId the checkbox group id // normal just use for checkbox
19564 * Create a new CheckBox
19565 * @param {Object} config The config object
19568 Roo.bootstrap.CheckBox = function(config){
19569 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
19574 * Fires when the element is checked or unchecked.
19575 * @param {Roo.bootstrap.CheckBox} this This input
19576 * @param {Boolean} checked The new checked value
19583 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
19585 inputType: 'checkbox',
19593 getAutoCreate : function()
19595 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19601 cfg.cls = 'form-group ' + this.inputType; //input-group
19604 cfg.cls += ' ' + this.inputType + '-inline';
19610 type : this.inputType,
19611 value : this.inputValue,
19612 cls : 'roo-' + this.inputType, //'form-box',
19613 placeholder : this.placeholder || ''
19617 if(this.inputType != 'radio'){
19621 cls : 'roo-hidden-value',
19622 value : this.checked ? this.valueOff : this.inputValue
19627 if (this.weight) { // Validity check?
19628 cfg.cls += " " + this.inputType + "-" + this.weight;
19631 if (this.disabled) {
19632 input.disabled=true;
19636 input.checked = this.checked;
19643 input.name = this.name;
19645 if(this.inputType != 'radio'){
19646 hidden.name = this.name;
19647 input.name = '_hidden_' + this.name;
19652 input.cls += ' input-' + this.size;
19657 ['xs','sm','md','lg'].map(function(size){
19658 if (settings[size]) {
19659 cfg.cls += ' col-' + size + '-' + settings[size];
19663 var inputblock = input;
19665 if (this.before || this.after) {
19668 cls : 'input-group',
19673 inputblock.cn.push({
19675 cls : 'input-group-addon',
19680 inputblock.cn.push(input);
19682 if(this.inputType != 'radio'){
19683 inputblock.cn.push(hidden);
19687 inputblock.cn.push({
19689 cls : 'input-group-addon',
19696 if (align ==='left' && this.fieldLabel.length) {
19697 // Roo.log("left and has label");
19703 cls : 'control-label col-md-' + this.labelWidth,
19704 html : this.fieldLabel
19708 cls : "col-md-" + (12 - this.labelWidth),
19715 } else if ( this.fieldLabel.length) {
19716 // Roo.log(" label");
19720 tag: this.boxLabel ? 'span' : 'label',
19722 cls: 'control-label box-input-label',
19723 //cls : 'input-group-addon',
19724 html : this.fieldLabel
19734 // Roo.log(" no label && no align");
19735 cfg.cn = [ inputblock ] ;
19741 var boxLabelCfg = {
19743 //'for': id, // box label is handled by onclick - so no for...
19745 html: this.boxLabel
19749 boxLabelCfg.tooltip = this.tooltip;
19752 cfg.cn.push(boxLabelCfg);
19755 if(this.inputType != 'radio'){
19756 cfg.cn.push(hidden);
19764 * return the real input element.
19766 inputEl: function ()
19768 return this.el.select('input.roo-' + this.inputType,true).first();
19770 hiddenEl: function ()
19772 return this.el.select('input.roo-hidden-value',true).first();
19775 labelEl: function()
19777 return this.el.select('label.control-label',true).first();
19779 /* depricated... */
19783 return this.labelEl();
19786 boxLabelEl: function()
19788 return this.el.select('label.box-label',true).first();
19791 initEvents : function()
19793 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19795 this.inputEl().on('click', this.onClick, this);
19797 if (this.boxLabel) {
19798 this.el.select('label.box-label',true).first().on('click', this.onClick, this);
19801 this.startValue = this.getValue();
19804 Roo.bootstrap.CheckBox.register(this);
19808 onClick : function()
19810 this.setChecked(!this.checked);
19813 setChecked : function(state,suppressEvent)
19815 this.startValue = this.getValue();
19817 if(this.inputType == 'radio'){
19819 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19820 e.dom.checked = false;
19823 this.inputEl().dom.checked = true;
19825 this.inputEl().dom.value = this.inputValue;
19827 if(suppressEvent !== true){
19828 this.fireEvent('check', this, true);
19836 this.checked = state;
19838 this.inputEl().dom.checked = state;
19841 this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
19843 if(suppressEvent !== true){
19844 this.fireEvent('check', this, state);
19850 getValue : function()
19852 if(this.inputType == 'radio'){
19853 return this.getGroupValue();
19856 return this.hiddenEl().dom.value;
19860 getGroupValue : function()
19862 if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19866 return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19869 setValue : function(v,suppressEvent)
19871 if(this.inputType == 'radio'){
19872 this.setGroupValue(v, suppressEvent);
19876 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19881 setGroupValue : function(v, suppressEvent)
19883 this.startValue = this.getValue();
19885 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19886 e.dom.checked = false;
19888 if(e.dom.value == v){
19889 e.dom.checked = true;
19893 if(suppressEvent !== true){
19894 this.fireEvent('check', this, true);
19902 validate : function()
19906 (this.inputType == 'radio' && this.validateRadio()) ||
19907 (this.inputType == 'checkbox' && this.validateCheckbox())
19913 this.markInvalid();
19917 validateRadio : function()
19919 if(this.allowBlank){
19925 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19926 if(!e.dom.checked){
19938 validateCheckbox : function()
19941 return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19944 var group = Roo.bootstrap.CheckBox.get(this.groupId);
19952 for(var i in group){
19957 r = (group[i].getValue() == group[i].inputValue) ? true : false;
19964 * Mark this field as valid
19966 markValid : function()
19970 this.fireEvent('valid', this);
19972 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19975 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19982 if(this.inputType == 'radio'){
19983 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19984 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19985 e.findParent('.form-group', false, true).addClass(_this.validClass);
19992 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19993 this.el.findParent('.form-group', false, true).addClass(this.validClass);
19997 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20003 for(var i in group){
20004 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20005 group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20010 * Mark this field as invalid
20011 * @param {String} msg The validation message
20013 markInvalid : function(msg)
20015 if(this.allowBlank){
20021 this.fireEvent('invalid', this, msg);
20023 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20026 label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20030 label.markInvalid();
20033 if(this.inputType == 'radio'){
20034 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20035 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20036 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20043 this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20044 this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20048 var group = Roo.bootstrap.CheckBox.get(this.groupId);
20054 for(var i in group){
20055 group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20056 group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20061 clearInvalid : function()
20063 Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20065 var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20068 label.iconEl.removeClass(label.validClass);
20069 label.iconEl.removeClass(label.invalidClass);
20073 disable : function()
20075 if(this.inputType != 'radio'){
20076 Roo.bootstrap.CheckBox.superclass.disable.call(this);
20083 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20084 _this.getActionEl().addClass(this.disabledClass);
20085 e.dom.disabled = true;
20089 this.disabled = true;
20090 this.fireEvent("disable", this);
20094 enable : function()
20096 if(this.inputType != 'radio'){
20097 Roo.bootstrap.CheckBox.superclass.enable.call(this);
20104 Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20105 _this.getActionEl().removeClass(this.disabledClass);
20106 e.dom.disabled = false;
20110 this.disabled = false;
20111 this.fireEvent("enable", this);
20117 Roo.apply(Roo.bootstrap.CheckBox, {
20122 * register a CheckBox Group
20123 * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20125 register : function(checkbox)
20127 if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20128 this.groups[checkbox.groupId] = {};
20131 if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20135 this.groups[checkbox.groupId][checkbox.name] = checkbox;
20139 * fetch a CheckBox Group based on the group ID
20140 * @param {string} the group ID
20141 * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20143 get: function(groupId) {
20144 if (typeof(this.groups[groupId]) == 'undefined') {
20148 return this.groups[groupId] ;
20160 *<div class="radio">
20162 <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
20163 Option one is this and that—be sure to include why it's great
20170 *<label class="radio-inline">fieldLabel</label>
20171 *<label class="radio-inline">
20172 <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
20180 * @class Roo.bootstrap.Radio
20181 * @extends Roo.bootstrap.CheckBox
20182 * Bootstrap Radio class
20185 * Create a new Radio
20186 * @param {Object} config The config object
20189 Roo.bootstrap.Radio = function(config){
20190 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20194 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
20196 inputType: 'radio',
20200 getAutoCreate : function()
20202 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20203 align = align || 'left'; // default...
20210 tag : this.inline ? 'span' : 'div',
20211 cls : 'form-group',
20215 var inline = this.inline ? ' radio-inline' : '';
20219 // does not need for, as we wrap the input with it..
20221 cls : 'control-label box-label' + inline,
20224 var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
20228 //cls : 'control-label' + inline,
20229 html : this.fieldLabel,
20230 style : 'width:' + labelWidth + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
20236 type : this.inputType,
20237 //value : (!this.checked) ? this.valueOff : this.inputValue,
20238 value : this.inputValue,
20240 placeholder : this.placeholder || '' // ?? needed????
20243 if (this.weight) { // Validity check?
20244 input.cls += " radio-" + this.weight;
20246 if (this.disabled) {
20247 input.disabled=true;
20251 input.checked = this.checked;
20255 input.name = this.name;
20259 input.cls += ' input-' + this.size;
20262 //?? can span's inline have a width??
20265 ['xs','sm','md','lg'].map(function(size){
20266 if (settings[size]) {
20267 cfg.cls += ' col-' + size + '-' + settings[size];
20271 var inputblock = input;
20273 if (this.before || this.after) {
20276 cls : 'input-group',
20281 inputblock.cn.push({
20283 cls : 'input-group-addon',
20287 inputblock.cn.push(input);
20289 inputblock.cn.push({
20291 cls : 'input-group-addon',
20299 if (this.fieldLabel && this.fieldLabel.length) {
20300 cfg.cn.push(fieldLabel);
20303 // normal bootstrap puts the input inside the label.
20304 // however with our styled version - it has to go after the input.
20306 //lbl.cn.push(inputblock);
20310 cls: 'radio' + inline,
20317 cfg.cn.push( lblwrap);
20322 html: this.boxLabel
20331 initEvents : function()
20333 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20335 this.inputEl().on('click', this.onClick, this);
20336 if (this.boxLabel) {
20337 //Roo.log('find label');
20338 this.el.select('span.radio label span',true).first().on('click', this.onClick, this);
20343 inputEl: function ()
20345 return this.el.select('input.roo-radio',true).first();
20347 onClick : function()
20350 this.setChecked(true);
20353 setChecked : function(state,suppressEvent)
20356 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20357 v.dom.checked = false;
20360 this.checked = state;
20361 this.inputEl().dom.checked = state;
20363 if(suppressEvent !== true){
20364 this.fireEvent('check', this, state);
20366 //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
20370 getGroupValue : function()
20373 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
20374 if(v.dom.checked == true){
20375 value = v.dom.value;
20383 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
20384 * @return {Mixed} value The field value
20386 getValue : function(){
20387 return this.getGroupValue();
20391 //<script type="text/javascript">
20394 * Based Ext JS Library 1.1.1
20395 * Copyright(c) 2006-2007, Ext JS, LLC.
20401 * @class Roo.HtmlEditorCore
20402 * @extends Roo.Component
20403 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
20405 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
20408 Roo.HtmlEditorCore = function(config){
20411 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
20416 * @event initialize
20417 * Fires when the editor is fully initialized (including the iframe)
20418 * @param {Roo.HtmlEditorCore} this
20423 * Fires when the editor is first receives the focus. Any insertion must wait
20424 * until after this event.
20425 * @param {Roo.HtmlEditorCore} this
20429 * @event beforesync
20430 * Fires before the textarea is updated with content from the editor iframe. Return false
20431 * to cancel the sync.
20432 * @param {Roo.HtmlEditorCore} this
20433 * @param {String} html
20437 * @event beforepush
20438 * Fires before the iframe editor is updated with content from the textarea. Return false
20439 * to cancel the push.
20440 * @param {Roo.HtmlEditorCore} this
20441 * @param {String} html
20446 * Fires when the textarea is updated with content from the editor iframe.
20447 * @param {Roo.HtmlEditorCore} this
20448 * @param {String} html
20453 * Fires when the iframe editor is updated with content from the textarea.
20454 * @param {Roo.HtmlEditorCore} this
20455 * @param {String} html
20460 * @event editorevent
20461 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
20462 * @param {Roo.HtmlEditorCore} this
20468 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
20470 // defaults : white / black...
20471 this.applyBlacklists();
20478 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
20482 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
20488 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
20493 * @cfg {Number} height (in pixels)
20497 * @cfg {Number} width (in pixels)
20502 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
20505 stylesheets: false,
20510 // private properties
20511 validationEvent : false,
20513 initialized : false,
20515 sourceEditMode : false,
20516 onFocus : Roo.emptyFn,
20518 hideMode:'offsets',
20522 // blacklist + whitelisted elements..
20529 * Protected method that will not generally be called directly. It
20530 * is called when the editor initializes the iframe with HTML contents. Override this method if you
20531 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
20533 getDocMarkup : function(){
20537 // inherit styels from page...??
20538 if (this.stylesheets === false) {
20540 Roo.get(document.head).select('style').each(function(node) {
20541 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20544 Roo.get(document.head).select('link').each(function(node) {
20545 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
20548 } else if (!this.stylesheets.length) {
20550 st = '<style type="text/css">' +
20551 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20557 st += '<style type="text/css">' +
20558 'IMG { cursor: pointer } ' +
20562 return '<html><head>' + st +
20563 //<style type="text/css">' +
20564 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
20566 ' </head><body class="roo-htmleditor-body"></body></html>';
20570 onRender : function(ct, position)
20573 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
20574 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
20577 this.el.dom.style.border = '0 none';
20578 this.el.dom.setAttribute('tabIndex', -1);
20579 this.el.addClass('x-hidden hide');
20583 if(Roo.isIE){ // fix IE 1px bogus margin
20584 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
20588 this.frameId = Roo.id();
20592 var iframe = this.owner.wrap.createChild({
20594 cls: 'form-control', // bootstrap..
20596 name: this.frameId,
20597 frameBorder : 'no',
20598 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
20603 this.iframe = iframe.dom;
20605 this.assignDocWin();
20607 this.doc.designMode = 'on';
20610 this.doc.write(this.getDocMarkup());
20614 var task = { // must defer to wait for browser to be ready
20616 //console.log("run task?" + this.doc.readyState);
20617 this.assignDocWin();
20618 if(this.doc.body || this.doc.readyState == 'complete'){
20620 this.doc.designMode="on";
20624 Roo.TaskMgr.stop(task);
20625 this.initEditor.defer(10, this);
20632 Roo.TaskMgr.start(task);
20637 onResize : function(w, h)
20639 Roo.log('resize: ' +w + ',' + h );
20640 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
20644 if(typeof w == 'number'){
20646 this.iframe.style.width = w + 'px';
20648 if(typeof h == 'number'){
20650 this.iframe.style.height = h + 'px';
20652 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
20659 * Toggles the editor between standard and source edit mode.
20660 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
20662 toggleSourceEdit : function(sourceEditMode){
20664 this.sourceEditMode = sourceEditMode === true;
20666 if(this.sourceEditMode){
20668 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
20671 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
20672 //this.iframe.className = '';
20675 //this.setSize(this.owner.wrap.getSize());
20676 //this.fireEvent('editmodechange', this, this.sourceEditMode);
20683 * Protected method that will not generally be called directly. If you need/want
20684 * custom HTML cleanup, this is the method you should override.
20685 * @param {String} html The HTML to be cleaned
20686 * return {String} The cleaned HTML
20688 cleanHtml : function(html){
20689 html = String(html);
20690 if(html.length > 5){
20691 if(Roo.isSafari){ // strip safari nonsense
20692 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
20695 if(html == ' '){
20702 * HTML Editor -> Textarea
20703 * Protected method that will not generally be called directly. Syncs the contents
20704 * of the editor iframe with the textarea.
20706 syncValue : function(){
20707 if(this.initialized){
20708 var bd = (this.doc.body || this.doc.documentElement);
20709 //this.cleanUpPaste(); -- this is done else where and causes havoc..
20710 var html = bd.innerHTML;
20712 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
20713 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
20715 html = '<div style="'+m[0]+'">' + html + '</div>';
20718 html = this.cleanHtml(html);
20719 // fix up the special chars.. normaly like back quotes in word...
20720 // however we do not want to do this with chinese..
20721 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20722 var cc = b.charCodeAt();
20724 (cc >= 0x4E00 && cc < 0xA000 ) ||
20725 (cc >= 0x3400 && cc < 0x4E00 ) ||
20726 (cc >= 0xf900 && cc < 0xfb00 )
20732 if(this.owner.fireEvent('beforesync', this, html) !== false){
20733 this.el.dom.value = html;
20734 this.owner.fireEvent('sync', this, html);
20740 * Protected method that will not generally be called directly. Pushes the value of the textarea
20741 * into the iframe editor.
20743 pushValue : function(){
20744 if(this.initialized){
20745 var v = this.el.dom.value.trim();
20747 // if(v.length < 1){
20751 if(this.owner.fireEvent('beforepush', this, v) !== false){
20752 var d = (this.doc.body || this.doc.documentElement);
20754 this.cleanUpPaste();
20755 this.el.dom.value = d.innerHTML;
20756 this.owner.fireEvent('push', this, v);
20762 deferFocus : function(){
20763 this.focus.defer(10, this);
20767 focus : function(){
20768 if(this.win && !this.sourceEditMode){
20775 assignDocWin: function()
20777 var iframe = this.iframe;
20780 this.doc = iframe.contentWindow.document;
20781 this.win = iframe.contentWindow;
20783 // if (!Roo.get(this.frameId)) {
20786 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20787 // this.win = Roo.get(this.frameId).dom.contentWindow;
20789 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20793 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20794 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20799 initEditor : function(){
20800 //console.log("INIT EDITOR");
20801 this.assignDocWin();
20805 this.doc.designMode="on";
20807 this.doc.write(this.getDocMarkup());
20810 var dbody = (this.doc.body || this.doc.documentElement);
20811 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20812 // this copies styles from the containing element into thsi one..
20813 // not sure why we need all of this..
20814 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20816 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20817 //ss['background-attachment'] = 'fixed'; // w3c
20818 dbody.bgProperties = 'fixed'; // ie
20819 //Roo.DomHelper.applyStyles(dbody, ss);
20820 Roo.EventManager.on(this.doc, {
20821 //'mousedown': this.onEditorEvent,
20822 'mouseup': this.onEditorEvent,
20823 'dblclick': this.onEditorEvent,
20824 'click': this.onEditorEvent,
20825 'keyup': this.onEditorEvent,
20830 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20832 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20833 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20835 this.initialized = true;
20837 this.owner.fireEvent('initialize', this);
20842 onDestroy : function(){
20848 //for (var i =0; i < this.toolbars.length;i++) {
20849 // // fixme - ask toolbars for heights?
20850 // this.toolbars[i].onDestroy();
20853 //this.wrap.dom.innerHTML = '';
20854 //this.wrap.remove();
20859 onFirstFocus : function(){
20861 this.assignDocWin();
20864 this.activated = true;
20867 if(Roo.isGecko){ // prevent silly gecko errors
20869 var s = this.win.getSelection();
20870 if(!s.focusNode || s.focusNode.nodeType != 3){
20871 var r = s.getRangeAt(0);
20872 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20877 this.execCmd('useCSS', true);
20878 this.execCmd('styleWithCSS', false);
20881 this.owner.fireEvent('activate', this);
20885 adjustFont: function(btn){
20886 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20887 //if(Roo.isSafari){ // safari
20890 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20891 if(Roo.isSafari){ // safari
20892 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20893 v = (v < 10) ? 10 : v;
20894 v = (v > 48) ? 48 : v;
20895 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20900 v = Math.max(1, v+adjust);
20902 this.execCmd('FontSize', v );
20905 onEditorEvent : function(e)
20907 this.owner.fireEvent('editorevent', this, e);
20908 // this.updateToolbar();
20909 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20912 insertTag : function(tg)
20914 // could be a bit smarter... -> wrap the current selected tRoo..
20915 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20917 range = this.createRange(this.getSelection());
20918 var wrappingNode = this.doc.createElement(tg.toLowerCase());
20919 wrappingNode.appendChild(range.extractContents());
20920 range.insertNode(wrappingNode);
20927 this.execCmd("formatblock", tg);
20931 insertText : function(txt)
20935 var range = this.createRange();
20936 range.deleteContents();
20937 //alert(Sender.getAttribute('label'));
20939 range.insertNode(this.doc.createTextNode(txt));
20945 * Executes a Midas editor command on the editor document and performs necessary focus and
20946 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20947 * @param {String} cmd The Midas command
20948 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20950 relayCmd : function(cmd, value){
20952 this.execCmd(cmd, value);
20953 this.owner.fireEvent('editorevent', this);
20954 //this.updateToolbar();
20955 this.owner.deferFocus();
20959 * Executes a Midas editor command directly on the editor document.
20960 * For visual commands, you should use {@link #relayCmd} instead.
20961 * <b>This should only be called after the editor is initialized.</b>
20962 * @param {String} cmd The Midas command
20963 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20965 execCmd : function(cmd, value){
20966 this.doc.execCommand(cmd, false, value === undefined ? null : value);
20973 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20975 * @param {String} text | dom node..
20977 insertAtCursor : function(text)
20982 if(!this.activated){
20988 var r = this.doc.selection.createRange();
20999 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21003 // from jquery ui (MIT licenced)
21005 var win = this.win;
21007 if (win.getSelection && win.getSelection().getRangeAt) {
21008 range = win.getSelection().getRangeAt(0);
21009 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21010 range.insertNode(node);
21011 } else if (win.document.selection && win.document.selection.createRange) {
21012 // no firefox support
21013 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21014 win.document.selection.createRange().pasteHTML(txt);
21016 // no firefox support
21017 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21018 this.execCmd('InsertHTML', txt);
21027 mozKeyPress : function(e){
21029 var c = e.getCharCode(), cmd;
21032 c = String.fromCharCode(c).toLowerCase();
21046 this.cleanUpPaste.defer(100, this);
21054 e.preventDefault();
21062 fixKeys : function(){ // load time branching for fastest keydown performance
21064 return function(e){
21065 var k = e.getKey(), r;
21068 r = this.doc.selection.createRange();
21071 r.pasteHTML('    ');
21078 r = this.doc.selection.createRange();
21080 var target = r.parentElement();
21081 if(!target || target.tagName.toLowerCase() != 'li'){
21083 r.pasteHTML('<br />');
21089 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21090 this.cleanUpPaste.defer(100, this);
21096 }else if(Roo.isOpera){
21097 return function(e){
21098 var k = e.getKey();
21102 this.execCmd('InsertHTML','    ');
21105 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21106 this.cleanUpPaste.defer(100, this);
21111 }else if(Roo.isSafari){
21112 return function(e){
21113 var k = e.getKey();
21117 this.execCmd('InsertText','\t');
21121 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21122 this.cleanUpPaste.defer(100, this);
21130 getAllAncestors: function()
21132 var p = this.getSelectedNode();
21135 a.push(p); // push blank onto stack..
21136 p = this.getParentElement();
21140 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21144 a.push(this.doc.body);
21148 lastSelNode : false,
21151 getSelection : function()
21153 this.assignDocWin();
21154 return Roo.isIE ? this.doc.selection : this.win.getSelection();
21157 getSelectedNode: function()
21159 // this may only work on Gecko!!!
21161 // should we cache this!!!!
21166 var range = this.createRange(this.getSelection()).cloneRange();
21169 var parent = range.parentElement();
21171 var testRange = range.duplicate();
21172 testRange.moveToElementText(parent);
21173 if (testRange.inRange(range)) {
21176 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21179 parent = parent.parentElement;
21184 // is ancestor a text element.
21185 var ac = range.commonAncestorContainer;
21186 if (ac.nodeType == 3) {
21187 ac = ac.parentNode;
21190 var ar = ac.childNodes;
21193 var other_nodes = [];
21194 var has_other_nodes = false;
21195 for (var i=0;i<ar.length;i++) {
21196 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
21199 // fullly contained node.
21201 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21206 // probably selected..
21207 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21208 other_nodes.push(ar[i]);
21212 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
21217 has_other_nodes = true;
21219 if (!nodes.length && other_nodes.length) {
21220 nodes= other_nodes;
21222 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21228 createRange: function(sel)
21230 // this has strange effects when using with
21231 // top toolbar - not sure if it's a great idea.
21232 //this.editor.contentWindow.focus();
21233 if (typeof sel != "undefined") {
21235 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21237 return this.doc.createRange();
21240 return this.doc.createRange();
21243 getParentElement: function()
21246 this.assignDocWin();
21247 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21249 var range = this.createRange(sel);
21252 var p = range.commonAncestorContainer;
21253 while (p.nodeType == 3) { // text node
21264 * Range intersection.. the hard stuff...
21268 * [ -- selected range --- ]
21272 * if end is before start or hits it. fail.
21273 * if start is after end or hits it fail.
21275 * if either hits (but other is outside. - then it's not
21281 // @see http://www.thismuchiknow.co.uk/?p=64.
21282 rangeIntersectsNode : function(range, node)
21284 var nodeRange = node.ownerDocument.createRange();
21286 nodeRange.selectNode(node);
21288 nodeRange.selectNodeContents(node);
21291 var rangeStartRange = range.cloneRange();
21292 rangeStartRange.collapse(true);
21294 var rangeEndRange = range.cloneRange();
21295 rangeEndRange.collapse(false);
21297 var nodeStartRange = nodeRange.cloneRange();
21298 nodeStartRange.collapse(true);
21300 var nodeEndRange = nodeRange.cloneRange();
21301 nodeEndRange.collapse(false);
21303 return rangeStartRange.compareBoundaryPoints(
21304 Range.START_TO_START, nodeEndRange) == -1 &&
21305 rangeEndRange.compareBoundaryPoints(
21306 Range.START_TO_START, nodeStartRange) == 1;
21310 rangeCompareNode : function(range, node)
21312 var nodeRange = node.ownerDocument.createRange();
21314 nodeRange.selectNode(node);
21316 nodeRange.selectNodeContents(node);
21320 range.collapse(true);
21322 nodeRange.collapse(true);
21324 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
21325 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
21327 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
21329 var nodeIsBefore = ss == 1;
21330 var nodeIsAfter = ee == -1;
21332 if (nodeIsBefore && nodeIsAfter) {
21335 if (!nodeIsBefore && nodeIsAfter) {
21336 return 1; //right trailed.
21339 if (nodeIsBefore && !nodeIsAfter) {
21340 return 2; // left trailed.
21346 // private? - in a new class?
21347 cleanUpPaste : function()
21349 // cleans up the whole document..
21350 Roo.log('cleanuppaste');
21352 this.cleanUpChildren(this.doc.body);
21353 var clean = this.cleanWordChars(this.doc.body.innerHTML);
21354 if (clean != this.doc.body.innerHTML) {
21355 this.doc.body.innerHTML = clean;
21360 cleanWordChars : function(input) {// change the chars to hex code
21361 var he = Roo.HtmlEditorCore;
21363 var output = input;
21364 Roo.each(he.swapCodes, function(sw) {
21365 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
21367 output = output.replace(swapper, sw[1]);
21374 cleanUpChildren : function (n)
21376 if (!n.childNodes.length) {
21379 for (var i = n.childNodes.length-1; i > -1 ; i--) {
21380 this.cleanUpChild(n.childNodes[i]);
21387 cleanUpChild : function (node)
21390 //console.log(node);
21391 if (node.nodeName == "#text") {
21392 // clean up silly Windows -- stuff?
21395 if (node.nodeName == "#comment") {
21396 node.parentNode.removeChild(node);
21397 // clean up silly Windows -- stuff?
21400 var lcname = node.tagName.toLowerCase();
21401 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
21402 // whitelist of tags..
21404 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
21406 node.parentNode.removeChild(node);
21411 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
21413 // remove <a name=....> as rendering on yahoo mailer is borked with this.
21414 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
21416 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
21417 // remove_keep_children = true;
21420 if (remove_keep_children) {
21421 this.cleanUpChildren(node);
21422 // inserts everything just before this node...
21423 while (node.childNodes.length) {
21424 var cn = node.childNodes[0];
21425 node.removeChild(cn);
21426 node.parentNode.insertBefore(cn, node);
21428 node.parentNode.removeChild(node);
21432 if (!node.attributes || !node.attributes.length) {
21433 this.cleanUpChildren(node);
21437 function cleanAttr(n,v)
21440 if (v.match(/^\./) || v.match(/^\//)) {
21443 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
21446 if (v.match(/^#/)) {
21449 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
21450 node.removeAttribute(n);
21454 var cwhite = this.cwhite;
21455 var cblack = this.cblack;
21457 function cleanStyle(n,v)
21459 if (v.match(/expression/)) { //XSS?? should we even bother..
21460 node.removeAttribute(n);
21464 var parts = v.split(/;/);
21467 Roo.each(parts, function(p) {
21468 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
21472 var l = p.split(':').shift().replace(/\s+/g,'');
21473 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
21475 if ( cwhite.length && cblack.indexOf(l) > -1) {
21476 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21477 //node.removeAttribute(n);
21481 // only allow 'c whitelisted system attributes'
21482 if ( cwhite.length && cwhite.indexOf(l) < 0) {
21483 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
21484 //node.removeAttribute(n);
21494 if (clean.length) {
21495 node.setAttribute(n, clean.join(';'));
21497 node.removeAttribute(n);
21503 for (var i = node.attributes.length-1; i > -1 ; i--) {
21504 var a = node.attributes[i];
21507 if (a.name.toLowerCase().substr(0,2)=='on') {
21508 node.removeAttribute(a.name);
21511 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
21512 node.removeAttribute(a.name);
21515 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
21516 cleanAttr(a.name,a.value); // fixme..
21519 if (a.name == 'style') {
21520 cleanStyle(a.name,a.value);
21523 /// clean up MS crap..
21524 // tecnically this should be a list of valid class'es..
21527 if (a.name == 'class') {
21528 if (a.value.match(/^Mso/)) {
21529 node.className = '';
21532 if (a.value.match(/body/)) {
21533 node.className = '';
21544 this.cleanUpChildren(node);
21550 * Clean up MS wordisms...
21552 cleanWord : function(node)
21557 this.cleanWord(this.doc.body);
21560 if (node.nodeName == "#text") {
21561 // clean up silly Windows -- stuff?
21564 if (node.nodeName == "#comment") {
21565 node.parentNode.removeChild(node);
21566 // clean up silly Windows -- stuff?
21570 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
21571 node.parentNode.removeChild(node);
21575 // remove - but keep children..
21576 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
21577 while (node.childNodes.length) {
21578 var cn = node.childNodes[0];
21579 node.removeChild(cn);
21580 node.parentNode.insertBefore(cn, node);
21582 node.parentNode.removeChild(node);
21583 this.iterateChildren(node, this.cleanWord);
21587 if (node.className.length) {
21589 var cn = node.className.split(/\W+/);
21591 Roo.each(cn, function(cls) {
21592 if (cls.match(/Mso[a-zA-Z]+/)) {
21597 node.className = cna.length ? cna.join(' ') : '';
21599 node.removeAttribute("class");
21603 if (node.hasAttribute("lang")) {
21604 node.removeAttribute("lang");
21607 if (node.hasAttribute("style")) {
21609 var styles = node.getAttribute("style").split(";");
21611 Roo.each(styles, function(s) {
21612 if (!s.match(/:/)) {
21615 var kv = s.split(":");
21616 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
21619 // what ever is left... we allow.
21622 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21623 if (!nstyle.length) {
21624 node.removeAttribute('style');
21627 this.iterateChildren(node, this.cleanWord);
21633 * iterateChildren of a Node, calling fn each time, using this as the scole..
21634 * @param {DomNode} node node to iterate children of.
21635 * @param {Function} fn method of this class to call on each item.
21637 iterateChildren : function(node, fn)
21639 if (!node.childNodes.length) {
21642 for (var i = node.childNodes.length-1; i > -1 ; i--) {
21643 fn.call(this, node.childNodes[i])
21649 * cleanTableWidths.
21651 * Quite often pasting from word etc.. results in tables with column and widths.
21652 * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
21655 cleanTableWidths : function(node)
21660 this.cleanTableWidths(this.doc.body);
21665 if (node.nodeName == "#text" || node.nodeName == "#comment") {
21668 Roo.log(node.tagName);
21669 if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
21670 this.iterateChildren(node, this.cleanTableWidths);
21673 if (node.hasAttribute('width')) {
21674 node.removeAttribute('width');
21678 if (node.hasAttribute("style")) {
21681 var styles = node.getAttribute("style").split(";");
21683 Roo.each(styles, function(s) {
21684 if (!s.match(/:/)) {
21687 var kv = s.split(":");
21688 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
21691 // what ever is left... we allow.
21694 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
21695 if (!nstyle.length) {
21696 node.removeAttribute('style');
21700 this.iterateChildren(node, this.cleanTableWidths);
21708 domToHTML : function(currentElement, depth, nopadtext) {
21710 depth = depth || 0;
21711 nopadtext = nopadtext || false;
21713 if (!currentElement) {
21714 return this.domToHTML(this.doc.body);
21717 //Roo.log(currentElement);
21719 var allText = false;
21720 var nodeName = currentElement.nodeName;
21721 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21723 if (nodeName == '#text') {
21725 return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21730 if (nodeName != 'BODY') {
21733 // Prints the node tagName, such as <A>, <IMG>, etc
21736 for(i = 0; i < currentElement.attributes.length;i++) {
21738 var aname = currentElement.attributes.item(i).name;
21739 if (!currentElement.attributes.item(i).value.length) {
21742 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21745 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21754 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21757 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21762 // Traverse the tree
21764 var currentElementChild = currentElement.childNodes.item(i);
21765 var allText = true;
21766 var innerHTML = '';
21768 while (currentElementChild) {
21769 // Formatting code (indent the tree so it looks nice on the screen)
21770 var nopad = nopadtext;
21771 if (lastnode == 'SPAN') {
21775 if (currentElementChild.nodeName == '#text') {
21776 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21777 toadd = nopadtext ? toadd : toadd.trim();
21778 if (!nopad && toadd.length > 80) {
21779 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
21781 innerHTML += toadd;
21784 currentElementChild = currentElement.childNodes.item(i);
21790 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
21792 // Recursively traverse the tree structure of the child node
21793 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
21794 lastnode = currentElementChild.nodeName;
21796 currentElementChild=currentElement.childNodes.item(i);
21802 // The remaining code is mostly for formatting the tree
21803 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
21808 ret+= "</"+tagName+">";
21814 applyBlacklists : function()
21816 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
21817 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
21821 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21822 if (b.indexOf(tag) > -1) {
21825 this.white.push(tag);
21829 Roo.each(w, function(tag) {
21830 if (b.indexOf(tag) > -1) {
21833 if (this.white.indexOf(tag) > -1) {
21836 this.white.push(tag);
21841 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21842 if (w.indexOf(tag) > -1) {
21845 this.black.push(tag);
21849 Roo.each(b, function(tag) {
21850 if (w.indexOf(tag) > -1) {
21853 if (this.black.indexOf(tag) > -1) {
21856 this.black.push(tag);
21861 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
21862 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
21866 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21867 if (b.indexOf(tag) > -1) {
21870 this.cwhite.push(tag);
21874 Roo.each(w, function(tag) {
21875 if (b.indexOf(tag) > -1) {
21878 if (this.cwhite.indexOf(tag) > -1) {
21881 this.cwhite.push(tag);
21886 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21887 if (w.indexOf(tag) > -1) {
21890 this.cblack.push(tag);
21894 Roo.each(b, function(tag) {
21895 if (w.indexOf(tag) > -1) {
21898 if (this.cblack.indexOf(tag) > -1) {
21901 this.cblack.push(tag);
21906 setStylesheets : function(stylesheets)
21908 if(typeof(stylesheets) == 'string'){
21909 Roo.get(this.iframe.contentDocument.head).createChild({
21911 rel : 'stylesheet',
21920 Roo.each(stylesheets, function(s) {
21925 Roo.get(_this.iframe.contentDocument.head).createChild({
21927 rel : 'stylesheet',
21936 removeStylesheets : function()
21940 Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21945 // hide stuff that is not compatible
21959 * @event specialkey
21963 * @cfg {String} fieldClass @hide
21966 * @cfg {String} focusClass @hide
21969 * @cfg {String} autoCreate @hide
21972 * @cfg {String} inputType @hide
21975 * @cfg {String} invalidClass @hide
21978 * @cfg {String} invalidText @hide
21981 * @cfg {String} msgFx @hide
21984 * @cfg {String} validateOnBlur @hide
21988 Roo.HtmlEditorCore.white = [
21989 'area', 'br', 'img', 'input', 'hr', 'wbr',
21991 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
21992 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
21993 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
21994 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
21995 'table', 'ul', 'xmp',
21997 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
22000 'dir', 'menu', 'ol', 'ul', 'dl',
22006 Roo.HtmlEditorCore.black = [
22007 // 'embed', 'object', // enable - backend responsiblity to clean thiese
22009 'base', 'basefont', 'bgsound', 'blink', 'body',
22010 'frame', 'frameset', 'head', 'html', 'ilayer',
22011 'iframe', 'layer', 'link', 'meta', 'object',
22012 'script', 'style' ,'title', 'xml' // clean later..
22014 Roo.HtmlEditorCore.clean = [
22015 'script', 'style', 'title', 'xml'
22017 Roo.HtmlEditorCore.remove = [
22022 Roo.HtmlEditorCore.ablack = [
22026 Roo.HtmlEditorCore.aclean = [
22027 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
22031 Roo.HtmlEditorCore.pwhite= [
22032 'http', 'https', 'mailto'
22035 // white listed style attributes.
22036 Roo.HtmlEditorCore.cwhite= [
22037 // 'text-align', /// default is to allow most things..
22043 // black listed style attributes.
22044 Roo.HtmlEditorCore.cblack= [
22045 // 'font-size' -- this can be set by the project
22049 Roo.HtmlEditorCore.swapCodes =[
22068 * @class Roo.bootstrap.HtmlEditor
22069 * @extends Roo.bootstrap.TextArea
22070 * Bootstrap HtmlEditor class
22073 * Create a new HtmlEditor
22074 * @param {Object} config The config object
22077 Roo.bootstrap.HtmlEditor = function(config){
22078 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22079 if (!this.toolbars) {
22080 this.toolbars = [];
22082 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22085 * @event initialize
22086 * Fires when the editor is fully initialized (including the iframe)
22087 * @param {HtmlEditor} this
22092 * Fires when the editor is first receives the focus. Any insertion must wait
22093 * until after this event.
22094 * @param {HtmlEditor} this
22098 * @event beforesync
22099 * Fires before the textarea is updated with content from the editor iframe. Return false
22100 * to cancel the sync.
22101 * @param {HtmlEditor} this
22102 * @param {String} html
22106 * @event beforepush
22107 * Fires before the iframe editor is updated with content from the textarea. Return false
22108 * to cancel the push.
22109 * @param {HtmlEditor} this
22110 * @param {String} html
22115 * Fires when the textarea is updated with content from the editor iframe.
22116 * @param {HtmlEditor} this
22117 * @param {String} html
22122 * Fires when the iframe editor is updated with content from the textarea.
22123 * @param {HtmlEditor} this
22124 * @param {String} html
22128 * @event editmodechange
22129 * Fires when the editor switches edit modes
22130 * @param {HtmlEditor} this
22131 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22133 editmodechange: true,
22135 * @event editorevent
22136 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22137 * @param {HtmlEditor} this
22141 * @event firstfocus
22142 * Fires when on first focus - needed by toolbars..
22143 * @param {HtmlEditor} this
22148 * Auto save the htmlEditor value as a file into Events
22149 * @param {HtmlEditor} this
22153 * @event savedpreview
22154 * preview the saved version of htmlEditor
22155 * @param {HtmlEditor} this
22162 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
22166 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22171 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
22176 * @cfg {Number} height (in pixels)
22180 * @cfg {Number} width (in pixels)
22185 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22188 stylesheets: false,
22193 // private properties
22194 validationEvent : false,
22196 initialized : false,
22199 onFocus : Roo.emptyFn,
22201 hideMode:'offsets',
22204 tbContainer : false,
22206 toolbarContainer :function() {
22207 return this.wrap.select('.x-html-editor-tb',true).first();
22211 * Protected method that will not generally be called directly. It
22212 * is called when the editor creates its toolbar. Override this method if you need to
22213 * add custom toolbar buttons.
22214 * @param {HtmlEditor} editor
22216 createToolbar : function(){
22218 Roo.log("create toolbars");
22220 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22221 this.toolbars[0].render(this.toolbarContainer());
22225 // if (!editor.toolbars || !editor.toolbars.length) {
22226 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22229 // for (var i =0 ; i < editor.toolbars.length;i++) {
22230 // editor.toolbars[i] = Roo.factory(
22231 // typeof(editor.toolbars[i]) == 'string' ?
22232 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
22233 // Roo.bootstrap.HtmlEditor);
22234 // editor.toolbars[i].init(editor);
22240 onRender : function(ct, position)
22242 // Roo.log("Call onRender: " + this.xtype);
22244 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22246 this.wrap = this.inputEl().wrap({
22247 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22250 this.editorcore.onRender(ct, position);
22252 if (this.resizable) {
22253 this.resizeEl = new Roo.Resizable(this.wrap, {
22257 minHeight : this.height,
22258 height: this.height,
22259 handles : this.resizable,
22262 resize : function(r, w, h) {
22263 _t.onResize(w,h); // -something
22269 this.createToolbar(this);
22272 if(!this.width && this.resizable){
22273 this.setSize(this.wrap.getSize());
22275 if (this.resizeEl) {
22276 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22277 // should trigger onReize..
22283 onResize : function(w, h)
22285 Roo.log('resize: ' +w + ',' + h );
22286 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22290 if(this.inputEl() ){
22291 if(typeof w == 'number'){
22292 var aw = w - this.wrap.getFrameWidth('lr');
22293 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22296 if(typeof h == 'number'){
22297 var tbh = -11; // fixme it needs to tool bar size!
22298 for (var i =0; i < this.toolbars.length;i++) {
22299 // fixme - ask toolbars for heights?
22300 tbh += this.toolbars[i].el.getHeight();
22301 //if (this.toolbars[i].footer) {
22302 // tbh += this.toolbars[i].footer.el.getHeight();
22310 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
22311 ah -= 5; // knock a few pixes off for look..
22312 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
22316 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
22317 this.editorcore.onResize(ew,eh);
22322 * Toggles the editor between standard and source edit mode.
22323 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
22325 toggleSourceEdit : function(sourceEditMode)
22327 this.editorcore.toggleSourceEdit(sourceEditMode);
22329 if(this.editorcore.sourceEditMode){
22330 Roo.log('editor - showing textarea');
22333 // Roo.log(this.syncValue());
22335 this.inputEl().removeClass(['hide', 'x-hidden']);
22336 this.inputEl().dom.removeAttribute('tabIndex');
22337 this.inputEl().focus();
22339 Roo.log('editor - hiding textarea');
22341 // Roo.log(this.pushValue());
22344 this.inputEl().addClass(['hide', 'x-hidden']);
22345 this.inputEl().dom.setAttribute('tabIndex', -1);
22346 //this.deferFocus();
22349 if(this.resizable){
22350 this.setSize(this.wrap.getSize());
22353 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
22356 // private (for BoxComponent)
22357 adjustSize : Roo.BoxComponent.prototype.adjustSize,
22359 // private (for BoxComponent)
22360 getResizeEl : function(){
22364 // private (for BoxComponent)
22365 getPositionEl : function(){
22370 initEvents : function(){
22371 this.originalValue = this.getValue();
22375 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22378 // markInvalid : Roo.emptyFn,
22380 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
22383 // clearInvalid : Roo.emptyFn,
22385 setValue : function(v){
22386 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
22387 this.editorcore.pushValue();
22392 deferFocus : function(){
22393 this.focus.defer(10, this);
22397 focus : function(){
22398 this.editorcore.focus();
22404 onDestroy : function(){
22410 for (var i =0; i < this.toolbars.length;i++) {
22411 // fixme - ask toolbars for heights?
22412 this.toolbars[i].onDestroy();
22415 this.wrap.dom.innerHTML = '';
22416 this.wrap.remove();
22421 onFirstFocus : function(){
22422 //Roo.log("onFirstFocus");
22423 this.editorcore.onFirstFocus();
22424 for (var i =0; i < this.toolbars.length;i++) {
22425 this.toolbars[i].onFirstFocus();
22431 syncValue : function()
22433 this.editorcore.syncValue();
22436 pushValue : function()
22438 this.editorcore.pushValue();
22442 // hide stuff that is not compatible
22456 * @event specialkey
22460 * @cfg {String} fieldClass @hide
22463 * @cfg {String} focusClass @hide
22466 * @cfg {String} autoCreate @hide
22469 * @cfg {String} inputType @hide
22472 * @cfg {String} invalidClass @hide
22475 * @cfg {String} invalidText @hide
22478 * @cfg {String} msgFx @hide
22481 * @cfg {String} validateOnBlur @hide
22490 Roo.namespace('Roo.bootstrap.htmleditor');
22492 * @class Roo.bootstrap.HtmlEditorToolbar1
22497 new Roo.bootstrap.HtmlEditor({
22500 new Roo.bootstrap.HtmlEditorToolbar1({
22501 disable : { fonts: 1 , format: 1, ..., ... , ...],
22507 * @cfg {Object} disable List of elements to disable..
22508 * @cfg {Array} btns List of additional buttons.
22512 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
22515 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
22518 Roo.apply(this, config);
22520 // default disabled, based on 'good practice'..
22521 this.disable = this.disable || {};
22522 Roo.applyIf(this.disable, {
22525 specialElements : true
22527 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
22529 this.editor = config.editor;
22530 this.editorcore = config.editor.editorcore;
22532 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
22534 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
22535 // dont call parent... till later.
22537 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
22542 editorcore : false,
22547 "h1","h2","h3","h4","h5","h6",
22549 "abbr", "acronym", "address", "cite", "samp", "var",
22553 onRender : function(ct, position)
22555 // Roo.log("Call onRender: " + this.xtype);
22557 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
22559 this.el.dom.style.marginBottom = '0';
22561 var editorcore = this.editorcore;
22562 var editor= this.editor;
22565 var btn = function(id,cmd , toggle, handler){
22567 var event = toggle ? 'toggle' : 'click';
22572 xns: Roo.bootstrap,
22575 enableToggle:toggle !== false,
22577 pressed : toggle ? false : null,
22580 a.listeners[toggle ? 'toggle' : 'click'] = function() {
22581 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
22590 xns: Roo.bootstrap,
22591 glyphicon : 'font',
22595 xns: Roo.bootstrap,
22599 Roo.each(this.formats, function(f) {
22600 style.menu.items.push({
22602 xns: Roo.bootstrap,
22603 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
22608 editorcore.insertTag(this.tagname);
22615 children.push(style);
22618 btn('bold',false,true);
22619 btn('italic',false,true);
22620 btn('align-left', 'justifyleft',true);
22621 btn('align-center', 'justifycenter',true);
22622 btn('align-right' , 'justifyright',true);
22623 btn('link', false, false, function(btn) {
22624 //Roo.log("create link?");
22625 var url = prompt(this.createLinkText, this.defaultLinkValue);
22626 if(url && url != 'http:/'+'/'){
22627 this.editorcore.relayCmd('createlink', url);
22630 btn('list','insertunorderedlist',true);
22631 btn('pencil', false,true, function(btn){
22634 this.toggleSourceEdit(btn.pressed);
22640 xns: Roo.bootstrap,
22645 xns: Roo.bootstrap,
22650 cog.menu.items.push({
22652 xns: Roo.bootstrap,
22653 html : Clean styles,
22658 editorcore.insertTag(this.tagname);
22667 this.xtype = 'NavSimplebar';
22669 for(var i=0;i< children.length;i++) {
22671 this.buttons.add(this.addxtypeChild(children[i]));
22675 editor.on('editorevent', this.updateToolbar, this);
22677 onBtnClick : function(id)
22679 this.editorcore.relayCmd(id);
22680 this.editorcore.focus();
22684 * Protected method that will not generally be called directly. It triggers
22685 * a toolbar update by reading the markup state of the current selection in the editor.
22687 updateToolbar: function(){
22689 if(!this.editorcore.activated){
22690 this.editor.onFirstFocus(); // is this neeed?
22694 var btns = this.buttons;
22695 var doc = this.editorcore.doc;
22696 btns.get('bold').setActive(doc.queryCommandState('bold'));
22697 btns.get('italic').setActive(doc.queryCommandState('italic'));
22698 //btns.get('underline').setActive(doc.queryCommandState('underline'));
22700 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
22701 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
22702 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
22704 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
22705 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
22708 var ans = this.editorcore.getAllAncestors();
22709 if (this.formatCombo) {
22712 var store = this.formatCombo.store;
22713 this.formatCombo.setValue("");
22714 for (var i =0; i < ans.length;i++) {
22715 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
22717 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
22725 // hides menus... - so this cant be on a menu...
22726 Roo.bootstrap.MenuMgr.hideAll();
22728 Roo.bootstrap.MenuMgr.hideAll();
22729 //this.editorsyncValue();
22731 onFirstFocus: function() {
22732 this.buttons.each(function(item){
22736 toggleSourceEdit : function(sourceEditMode){
22739 if(sourceEditMode){
22740 Roo.log("disabling buttons");
22741 this.buttons.each( function(item){
22742 if(item.cmd != 'pencil'){
22748 Roo.log("enabling buttons");
22749 if(this.editorcore.initialized){
22750 this.buttons.each( function(item){
22756 Roo.log("calling toggole on editor");
22757 // tell the editor that it's been pressed..
22758 this.editor.toggleSourceEdit(sourceEditMode);
22768 * @class Roo.bootstrap.Table.AbstractSelectionModel
22769 * @extends Roo.util.Observable
22770 * Abstract base class for grid SelectionModels. It provides the interface that should be
22771 * implemented by descendant classes. This class should not be directly instantiated.
22774 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22775 this.locked = false;
22776 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22780 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
22781 /** @ignore Called by the grid automatically. Do not call directly. */
22782 init : function(grid){
22788 * Locks the selections.
22791 this.locked = true;
22795 * Unlocks the selections.
22797 unlock : function(){
22798 this.locked = false;
22802 * Returns true if the selections are locked.
22803 * @return {Boolean}
22805 isLocked : function(){
22806 return this.locked;
22810 * @extends Roo.bootstrap.Table.AbstractSelectionModel
22811 * @class Roo.bootstrap.Table.RowSelectionModel
22812 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22813 * It supports multiple selections and keyboard selection/navigation.
22815 * @param {Object} config
22818 Roo.bootstrap.Table.RowSelectionModel = function(config){
22819 Roo.apply(this, config);
22820 this.selections = new Roo.util.MixedCollection(false, function(o){
22825 this.lastActive = false;
22829 * @event selectionchange
22830 * Fires when the selection changes
22831 * @param {SelectionModel} this
22833 "selectionchange" : true,
22835 * @event afterselectionchange
22836 * Fires after the selection changes (eg. by key press or clicking)
22837 * @param {SelectionModel} this
22839 "afterselectionchange" : true,
22841 * @event beforerowselect
22842 * Fires when a row is selected being selected, return false to cancel.
22843 * @param {SelectionModel} this
22844 * @param {Number} rowIndex The selected index
22845 * @param {Boolean} keepExisting False if other selections will be cleared
22847 "beforerowselect" : true,
22850 * Fires when a row is selected.
22851 * @param {SelectionModel} this
22852 * @param {Number} rowIndex The selected index
22853 * @param {Roo.data.Record} r The record
22855 "rowselect" : true,
22857 * @event rowdeselect
22858 * Fires when a row is deselected.
22859 * @param {SelectionModel} this
22860 * @param {Number} rowIndex The selected index
22862 "rowdeselect" : true
22864 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22865 this.locked = false;
22868 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
22870 * @cfg {Boolean} singleSelect
22871 * True to allow selection of only one row at a time (defaults to false)
22873 singleSelect : false,
22876 initEvents : function()
22879 //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22880 // this.growclickrid.on("mousedown", this.handleMouseDown, this);
22881 //}else{ // allow click to work like normal
22882 // this.grid.on("rowclick", this.handleDragableRowClick, this);
22884 //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
22885 this.grid.on("rowclick", this.handleMouseDown, this);
22887 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22888 "up" : function(e){
22890 this.selectPrevious(e.shiftKey);
22891 }else if(this.last !== false && this.lastActive !== false){
22892 var last = this.last;
22893 this.selectRange(this.last, this.lastActive-1);
22894 this.grid.getView().focusRow(this.lastActive);
22895 if(last !== false){
22899 this.selectFirstRow();
22901 this.fireEvent("afterselectionchange", this);
22903 "down" : function(e){
22905 this.selectNext(e.shiftKey);
22906 }else if(this.last !== false && this.lastActive !== false){
22907 var last = this.last;
22908 this.selectRange(this.last, this.lastActive+1);
22909 this.grid.getView().focusRow(this.lastActive);
22910 if(last !== false){
22914 this.selectFirstRow();
22916 this.fireEvent("afterselectionchange", this);
22920 this.grid.store.on('load', function(){
22921 this.selections.clear();
22924 var view = this.grid.view;
22925 view.on("refresh", this.onRefresh, this);
22926 view.on("rowupdated", this.onRowUpdated, this);
22927 view.on("rowremoved", this.onRemove, this);
22932 onRefresh : function()
22934 var ds = this.grid.store, i, v = this.grid.view;
22935 var s = this.selections;
22936 s.each(function(r){
22937 if((i = ds.indexOfId(r.id)) != -1){
22946 onRemove : function(v, index, r){
22947 this.selections.remove(r);
22951 onRowUpdated : function(v, index, r){
22952 if(this.isSelected(r)){
22953 v.onRowSelect(index);
22959 * @param {Array} records The records to select
22960 * @param {Boolean} keepExisting (optional) True to keep existing selections
22962 selectRecords : function(records, keepExisting)
22965 this.clearSelections();
22967 var ds = this.grid.store;
22968 for(var i = 0, len = records.length; i < len; i++){
22969 this.selectRow(ds.indexOf(records[i]), true);
22974 * Gets the number of selected rows.
22977 getCount : function(){
22978 return this.selections.length;
22982 * Selects the first row in the grid.
22984 selectFirstRow : function(){
22989 * Select the last row.
22990 * @param {Boolean} keepExisting (optional) True to keep existing selections
22992 selectLastRow : function(keepExisting){
22993 //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22994 this.selectRow(this.grid.store.getCount() - 1, keepExisting);
22998 * Selects the row immediately following the last selected row.
22999 * @param {Boolean} keepExisting (optional) True to keep existing selections
23001 selectNext : function(keepExisting)
23003 if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23004 this.selectRow(this.last+1, keepExisting);
23005 this.grid.getView().focusRow(this.last);
23010 * Selects the row that precedes the last selected row.
23011 * @param {Boolean} keepExisting (optional) True to keep existing selections
23013 selectPrevious : function(keepExisting){
23015 this.selectRow(this.last-1, keepExisting);
23016 this.grid.getView().focusRow(this.last);
23021 * Returns the selected records
23022 * @return {Array} Array of selected records
23024 getSelections : function(){
23025 return [].concat(this.selections.items);
23029 * Returns the first selected record.
23032 getSelected : function(){
23033 return this.selections.itemAt(0);
23038 * Clears all selections.
23040 clearSelections : function(fast)
23046 var ds = this.grid.store;
23047 var s = this.selections;
23048 s.each(function(r){
23049 this.deselectRow(ds.indexOfId(r.id));
23053 this.selections.clear();
23060 * Selects all rows.
23062 selectAll : function(){
23066 this.selections.clear();
23067 for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23068 this.selectRow(i, true);
23073 * Returns True if there is a selection.
23074 * @return {Boolean}
23076 hasSelection : function(){
23077 return this.selections.length > 0;
23081 * Returns True if the specified row is selected.
23082 * @param {Number/Record} record The record or index of the record to check
23083 * @return {Boolean}
23085 isSelected : function(index){
23086 var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23087 return (r && this.selections.key(r.id) ? true : false);
23091 * Returns True if the specified record id is selected.
23092 * @param {String} id The id of record to check
23093 * @return {Boolean}
23095 isIdSelected : function(id){
23096 return (this.selections.key(id) ? true : false);
23101 handleMouseDBClick : function(e, t){
23105 handleMouseDown : function(e, t)
23107 var rowIndex = this.grid.headerShow ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23108 if(this.isLocked() || rowIndex < 0 ){
23111 if(e.shiftKey && this.last !== false){
23112 var last = this.last;
23113 this.selectRange(last, rowIndex, e.ctrlKey);
23114 this.last = last; // reset the last
23118 var isSelected = this.isSelected(rowIndex);
23119 //Roo.log("select row:" + rowIndex);
23121 this.deselectRow(rowIndex);
23123 this.selectRow(rowIndex, true);
23127 if(e.button !== 0 && isSelected){
23128 alert('rowIndex 2: ' + rowIndex);
23129 view.focusRow(rowIndex);
23130 }else if(e.ctrlKey && isSelected){
23131 this.deselectRow(rowIndex);
23132 }else if(!isSelected){
23133 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23134 view.focusRow(rowIndex);
23138 this.fireEvent("afterselectionchange", this);
23141 handleDragableRowClick : function(grid, rowIndex, e)
23143 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23144 this.selectRow(rowIndex, false);
23145 grid.view.focusRow(rowIndex);
23146 this.fireEvent("afterselectionchange", this);
23151 * Selects multiple rows.
23152 * @param {Array} rows Array of the indexes of the row to select
23153 * @param {Boolean} keepExisting (optional) True to keep existing selections
23155 selectRows : function(rows, keepExisting){
23157 this.clearSelections();
23159 for(var i = 0, len = rows.length; i < len; i++){
23160 this.selectRow(rows[i], true);
23165 * Selects a range of rows. All rows in between startRow and endRow are also selected.
23166 * @param {Number} startRow The index of the first row in the range
23167 * @param {Number} endRow The index of the last row in the range
23168 * @param {Boolean} keepExisting (optional) True to retain existing selections
23170 selectRange : function(startRow, endRow, keepExisting){
23175 this.clearSelections();
23177 if(startRow <= endRow){
23178 for(var i = startRow; i <= endRow; i++){
23179 this.selectRow(i, true);
23182 for(var i = startRow; i >= endRow; i--){
23183 this.selectRow(i, true);
23189 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23190 * @param {Number} startRow The index of the first row in the range
23191 * @param {Number} endRow The index of the last row in the range
23193 deselectRange : function(startRow, endRow, preventViewNotify){
23197 for(var i = startRow; i <= endRow; i++){
23198 this.deselectRow(i, preventViewNotify);
23204 * @param {Number} row The index of the row to select
23205 * @param {Boolean} keepExisting (optional) True to keep existing selections
23207 selectRow : function(index, keepExisting, preventViewNotify)
23209 if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23212 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23213 if(!keepExisting || this.singleSelect){
23214 this.clearSelections();
23217 var r = this.grid.store.getAt(index);
23218 //console.log('selectRow - record id :' + r.id);
23220 this.selections.add(r);
23221 this.last = this.lastActive = index;
23222 if(!preventViewNotify){
23223 var proxy = new Roo.Element(
23224 this.grid.getRowDom(index)
23226 proxy.addClass('bg-info info');
23228 this.fireEvent("rowselect", this, index, r);
23229 this.fireEvent("selectionchange", this);
23235 * @param {Number} row The index of the row to deselect
23237 deselectRow : function(index, preventViewNotify)
23242 if(this.last == index){
23245 if(this.lastActive == index){
23246 this.lastActive = false;
23249 var r = this.grid.store.getAt(index);
23254 this.selections.remove(r);
23255 //.console.log('deselectRow - record id :' + r.id);
23256 if(!preventViewNotify){
23258 var proxy = new Roo.Element(
23259 this.grid.getRowDom(index)
23261 proxy.removeClass('bg-info info');
23263 this.fireEvent("rowdeselect", this, index);
23264 this.fireEvent("selectionchange", this);
23268 restoreLast : function(){
23270 this.last = this._last;
23275 acceptsNav : function(row, col, cm){
23276 return !cm.isHidden(col) && cm.isCellEditable(col, row);
23280 onEditorKey : function(field, e){
23281 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23286 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23288 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23290 }else if(k == e.ENTER && !e.ctrlKey){
23294 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23296 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23298 }else if(k == e.ESC){
23302 g.startEditing(newCell[0], newCell[1]);
23308 * Ext JS Library 1.1.1
23309 * Copyright(c) 2006-2007, Ext JS, LLC.
23311 * Originally Released Under LGPL - original licence link has changed is not relivant.
23314 * <script type="text/javascript">
23318 * @class Roo.bootstrap.PagingToolbar
23319 * @extends Roo.bootstrap.NavSimplebar
23320 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
23322 * Create a new PagingToolbar
23323 * @param {Object} config The config object
23324 * @param {Roo.data.Store} store
23326 Roo.bootstrap.PagingToolbar = function(config)
23328 // old args format still supported... - xtype is prefered..
23329 // created from xtype...
23331 this.ds = config.dataSource;
23333 if (config.store && !this.ds) {
23334 this.store= Roo.factory(config.store, Roo.data);
23335 this.ds = this.store;
23336 this.ds.xmodule = this.xmodule || false;
23339 this.toolbarItems = [];
23340 if (config.items) {
23341 this.toolbarItems = config.items;
23344 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
23349 this.bind(this.ds);
23352 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
23356 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
23358 * @cfg {Roo.data.Store} dataSource
23359 * The underlying data store providing the paged data
23362 * @cfg {String/HTMLElement/Element} container
23363 * container The id or element that will contain the toolbar
23366 * @cfg {Boolean} displayInfo
23367 * True to display the displayMsg (defaults to false)
23370 * @cfg {Number} pageSize
23371 * The number of records to display per page (defaults to 20)
23375 * @cfg {String} displayMsg
23376 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
23378 displayMsg : 'Displaying {0} - {1} of {2}',
23380 * @cfg {String} emptyMsg
23381 * The message to display when no records are found (defaults to "No data to display")
23383 emptyMsg : 'No data to display',
23385 * Customizable piece of the default paging text (defaults to "Page")
23388 beforePageText : "Page",
23390 * Customizable piece of the default paging text (defaults to "of %0")
23393 afterPageText : "of {0}",
23395 * Customizable piece of the default paging text (defaults to "First Page")
23398 firstText : "First Page",
23400 * Customizable piece of the default paging text (defaults to "Previous Page")
23403 prevText : "Previous Page",
23405 * Customizable piece of the default paging text (defaults to "Next Page")
23408 nextText : "Next Page",
23410 * Customizable piece of the default paging text (defaults to "Last Page")
23413 lastText : "Last Page",
23415 * Customizable piece of the default paging text (defaults to "Refresh")
23418 refreshText : "Refresh",
23422 onRender : function(ct, position)
23424 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
23425 this.navgroup.parentId = this.id;
23426 this.navgroup.onRender(this.el, null);
23427 // add the buttons to the navgroup
23429 if(this.displayInfo){
23430 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
23431 this.displayEl = this.el.select('.x-paging-info', true).first();
23432 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
23433 // this.displayEl = navel.el.select('span',true).first();
23439 Roo.each(_this.buttons, function(e){ // this might need to use render????
23440 Roo.factory(e).onRender(_this.el, null);
23444 Roo.each(_this.toolbarItems, function(e) {
23445 _this.navgroup.addItem(e);
23449 this.first = this.navgroup.addItem({
23450 tooltip: this.firstText,
23452 icon : 'fa fa-backward',
23454 preventDefault: true,
23455 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
23458 this.prev = this.navgroup.addItem({
23459 tooltip: this.prevText,
23461 icon : 'fa fa-step-backward',
23463 preventDefault: true,
23464 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
23466 //this.addSeparator();
23469 var field = this.navgroup.addItem( {
23471 cls : 'x-paging-position',
23473 html : this.beforePageText +
23474 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
23475 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
23478 this.field = field.el.select('input', true).first();
23479 this.field.on("keydown", this.onPagingKeydown, this);
23480 this.field.on("focus", function(){this.dom.select();});
23483 this.afterTextEl = field.el.select('.x-paging-after',true).first();
23484 //this.field.setHeight(18);
23485 //this.addSeparator();
23486 this.next = this.navgroup.addItem({
23487 tooltip: this.nextText,
23489 html : ' <i class="fa fa-step-forward">',
23491 preventDefault: true,
23492 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
23494 this.last = this.navgroup.addItem({
23495 tooltip: this.lastText,
23496 icon : 'fa fa-forward',
23499 preventDefault: true,
23500 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
23502 //this.addSeparator();
23503 this.loading = this.navgroup.addItem({
23504 tooltip: this.refreshText,
23505 icon: 'fa fa-refresh',
23506 preventDefault: true,
23507 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
23513 updateInfo : function(){
23514 if(this.displayEl){
23515 var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
23516 var msg = count == 0 ?
23520 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
23522 this.displayEl.update(msg);
23527 onLoad : function(ds, r, o){
23528 this.cursor = o.params ? o.params.start : 0;
23529 var d = this.getPageData(),
23533 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
23534 this.field.dom.value = ap;
23535 this.first.setDisabled(ap == 1);
23536 this.prev.setDisabled(ap == 1);
23537 this.next.setDisabled(ap == ps);
23538 this.last.setDisabled(ap == ps);
23539 this.loading.enable();
23544 getPageData : function(){
23545 var total = this.ds.getTotalCount();
23548 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
23549 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
23554 onLoadError : function(){
23555 this.loading.enable();
23559 onPagingKeydown : function(e){
23560 var k = e.getKey();
23561 var d = this.getPageData();
23563 var v = this.field.dom.value, pageNum;
23564 if(!v || isNaN(pageNum = parseInt(v, 10))){
23565 this.field.dom.value = d.activePage;
23568 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
23569 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23572 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))
23574 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
23575 this.field.dom.value = pageNum;
23576 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
23579 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
23581 var v = this.field.dom.value, pageNum;
23582 var increment = (e.shiftKey) ? 10 : 1;
23583 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
23586 if(!v || isNaN(pageNum = parseInt(v, 10))) {
23587 this.field.dom.value = d.activePage;
23590 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
23592 this.field.dom.value = parseInt(v, 10) + increment;
23593 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
23594 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
23601 beforeLoad : function(){
23603 this.loading.disable();
23608 onClick : function(which){
23617 ds.load({params:{start: 0, limit: this.pageSize}});
23620 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
23623 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
23626 var total = ds.getTotalCount();
23627 var extra = total % this.pageSize;
23628 var lastStart = extra ? (total - extra) : total-this.pageSize;
23629 ds.load({params:{start: lastStart, limit: this.pageSize}});
23632 ds.load({params:{start: this.cursor, limit: this.pageSize}});
23638 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
23639 * @param {Roo.data.Store} store The data store to unbind
23641 unbind : function(ds){
23642 ds.un("beforeload", this.beforeLoad, this);
23643 ds.un("load", this.onLoad, this);
23644 ds.un("loadexception", this.onLoadError, this);
23645 ds.un("remove", this.updateInfo, this);
23646 ds.un("add", this.updateInfo, this);
23647 this.ds = undefined;
23651 * Binds the paging toolbar to the specified {@link Roo.data.Store}
23652 * @param {Roo.data.Store} store The data store to bind
23654 bind : function(ds){
23655 ds.on("beforeload", this.beforeLoad, this);
23656 ds.on("load", this.onLoad, this);
23657 ds.on("loadexception", this.onLoadError, this);
23658 ds.on("remove", this.updateInfo, this);
23659 ds.on("add", this.updateInfo, this);
23670 * @class Roo.bootstrap.MessageBar
23671 * @extends Roo.bootstrap.Component
23672 * Bootstrap MessageBar class
23673 * @cfg {String} html contents of the MessageBar
23674 * @cfg {String} weight (info | success | warning | danger) default info
23675 * @cfg {String} beforeClass insert the bar before the given class
23676 * @cfg {Boolean} closable (true | false) default false
23677 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
23680 * Create a new Element
23681 * @param {Object} config The config object
23684 Roo.bootstrap.MessageBar = function(config){
23685 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
23688 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
23694 beforeClass: 'bootstrap-sticky-wrap',
23696 getAutoCreate : function(){
23700 cls: 'alert alert-dismissable alert-' + this.weight,
23705 html: this.html || ''
23711 cfg.cls += ' alert-messages-fixed';
23725 onRender : function(ct, position)
23727 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
23730 var cfg = Roo.apply({}, this.getAutoCreate());
23734 cfg.cls += ' ' + this.cls;
23737 cfg.style = this.style;
23739 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
23741 this.el.setVisibilityMode(Roo.Element.DISPLAY);
23744 this.el.select('>button.close').on('click', this.hide, this);
23750 if (!this.rendered) {
23756 this.fireEvent('show', this);
23762 if (!this.rendered) {
23768 this.fireEvent('hide', this);
23771 update : function()
23773 // var e = this.el.dom.firstChild;
23775 // if(this.closable){
23776 // e = e.nextSibling;
23779 // e.data = this.html || '';
23781 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23797 * @class Roo.bootstrap.Graph
23798 * @extends Roo.bootstrap.Component
23799 * Bootstrap Graph class
23803 @cfg {String} graphtype bar | vbar | pie
23804 @cfg {number} g_x coodinator | centre x (pie)
23805 @cfg {number} g_y coodinator | centre y (pie)
23806 @cfg {number} g_r radius (pie)
23807 @cfg {number} g_height height of the chart (respected by all elements in the set)
23808 @cfg {number} g_width width of the chart (respected by all elements in the set)
23809 @cfg {Object} title The title of the chart
23812 -opts (object) options for the chart
23814 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23815 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23817 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.
23818 o stacked (boolean) whether or not to tread values as in a stacked bar chart
23820 o stretch (boolean)
23822 -opts (object) options for the pie
23825 o startAngle (number)
23826 o endAngle (number)
23830 * Create a new Input
23831 * @param {Object} config The config object
23834 Roo.bootstrap.Graph = function(config){
23835 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23841 * The img click event for the img.
23842 * @param {Roo.EventObject} e
23848 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
23859 //g_colors: this.colors,
23866 getAutoCreate : function(){
23877 onRender : function(ct,position){
23880 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23882 if (typeof(Raphael) == 'undefined') {
23883 Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23887 this.raphael = Raphael(this.el.dom);
23889 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23890 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23891 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23892 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23894 r.text(160, 10, "Single Series Chart").attr(txtattr);
23895 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23896 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23897 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23899 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23900 r.barchart(330, 10, 300, 220, data1);
23901 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23902 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23905 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23906 // r.barchart(30, 30, 560, 250, xdata, {
23907 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23908 // axis : "0 0 1 1",
23909 // axisxlabels : xdata
23910 // //yvalues : cols,
23913 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23915 // this.load(null,xdata,{
23916 // axis : "0 0 1 1",
23917 // axisxlabels : xdata
23922 load : function(graphtype,xdata,opts)
23924 this.raphael.clear();
23926 graphtype = this.graphtype;
23931 var r = this.raphael,
23932 fin = function () {
23933 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23935 fout = function () {
23936 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23938 pfin = function() {
23939 this.sector.stop();
23940 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23943 this.label[0].stop();
23944 this.label[0].attr({ r: 7.5 });
23945 this.label[1].attr({ "font-weight": 800 });
23948 pfout = function() {
23949 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23952 this.label[0].animate({ r: 5 }, 500, "bounce");
23953 this.label[1].attr({ "font-weight": 400 });
23959 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23962 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23965 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
23966 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23968 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23975 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23980 setTitle: function(o)
23985 initEvents: function() {
23988 this.el.on('click', this.onClick, this);
23992 onClick : function(e)
23994 Roo.log('img onclick');
23995 this.fireEvent('click', this, e);
24007 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24010 * @class Roo.bootstrap.dash.NumberBox
24011 * @extends Roo.bootstrap.Component
24012 * Bootstrap NumberBox class
24013 * @cfg {String} headline Box headline
24014 * @cfg {String} content Box content
24015 * @cfg {String} icon Box icon
24016 * @cfg {String} footer Footer text
24017 * @cfg {String} fhref Footer href
24020 * Create a new NumberBox
24021 * @param {Object} config The config object
24025 Roo.bootstrap.dash.NumberBox = function(config){
24026 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24030 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
24039 getAutoCreate : function(){
24043 cls : 'small-box ',
24051 cls : 'roo-headline',
24052 html : this.headline
24056 cls : 'roo-content',
24057 html : this.content
24071 cls : 'ion ' + this.icon
24080 cls : 'small-box-footer',
24081 href : this.fhref || '#',
24085 cfg.cn.push(footer);
24092 onRender : function(ct,position){
24093 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24100 setHeadline: function (value)
24102 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24105 setFooter: function (value, href)
24107 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24110 this.el.select('a.small-box-footer',true).first().attr('href', href);
24115 setContent: function (value)
24117 this.el.select('.roo-content',true).first().dom.innerHTML = value;
24120 initEvents: function()
24134 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24137 * @class Roo.bootstrap.dash.TabBox
24138 * @extends Roo.bootstrap.Component
24139 * Bootstrap TabBox class
24140 * @cfg {String} title Title of the TabBox
24141 * @cfg {String} icon Icon of the TabBox
24142 * @cfg {Boolean} showtabs (true|false) show the tabs default true
24143 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24146 * Create a new TabBox
24147 * @param {Object} config The config object
24151 Roo.bootstrap.dash.TabBox = function(config){
24152 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24157 * When a pane is added
24158 * @param {Roo.bootstrap.dash.TabPane} pane
24162 * @event activatepane
24163 * When a pane is activated
24164 * @param {Roo.bootstrap.dash.TabPane} pane
24166 "activatepane" : true
24174 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
24179 tabScrollable : false,
24181 getChildContainer : function()
24183 return this.el.select('.tab-content', true).first();
24186 getAutoCreate : function(){
24190 cls: 'pull-left header',
24198 cls: 'fa ' + this.icon
24204 cls: 'nav nav-tabs pull-right',
24210 if(this.tabScrollable){
24217 cls: 'nav nav-tabs pull-right',
24228 cls: 'nav-tabs-custom',
24233 cls: 'tab-content no-padding',
24241 initEvents : function()
24243 //Roo.log('add add pane handler');
24244 this.on('addpane', this.onAddPane, this);
24247 * Updates the box title
24248 * @param {String} html to set the title to.
24250 setTitle : function(value)
24252 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24254 onAddPane : function(pane)
24256 this.panes.push(pane);
24257 //Roo.log('addpane');
24259 // tabs are rendere left to right..
24260 if(!this.showtabs){
24264 var ctr = this.el.select('.nav-tabs', true).first();
24267 var existing = ctr.select('.nav-tab',true);
24268 var qty = existing.getCount();;
24271 var tab = ctr.createChild({
24273 cls : 'nav-tab' + (qty ? '' : ' active'),
24281 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24284 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24286 pane.el.addClass('active');
24291 onTabClick : function(ev,un,ob,pane)
24293 //Roo.log('tab - prev default');
24294 ev.preventDefault();
24297 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24298 pane.tab.addClass('active');
24299 //Roo.log(pane.title);
24300 this.getChildContainer().select('.tab-pane',true).removeClass('active');
24301 // technically we should have a deactivate event.. but maybe add later.
24302 // and it should not de-activate the selected tab...
24303 this.fireEvent('activatepane', pane);
24304 pane.el.addClass('active');
24305 pane.fireEvent('activate');
24310 getActivePane : function()
24313 Roo.each(this.panes, function(p) {
24314 if(p.el.hasClass('active')){
24335 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24337 * @class Roo.bootstrap.TabPane
24338 * @extends Roo.bootstrap.Component
24339 * Bootstrap TabPane class
24340 * @cfg {Boolean} active (false | true) Default false
24341 * @cfg {String} title title of panel
24345 * Create a new TabPane
24346 * @param {Object} config The config object
24349 Roo.bootstrap.dash.TabPane = function(config){
24350 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
24356 * When a pane is activated
24357 * @param {Roo.bootstrap.dash.TabPane} pane
24364 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
24369 // the tabBox that this is attached to.
24372 getAutoCreate : function()
24380 cfg.cls += ' active';
24385 initEvents : function()
24387 //Roo.log('trigger add pane handler');
24388 this.parent().fireEvent('addpane', this)
24392 * Updates the tab title
24393 * @param {String} html to set the title to.
24395 setTitle: function(str)
24401 this.tab.select('a', true).first().dom.innerHTML = str;
24418 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24421 * @class Roo.bootstrap.menu.Menu
24422 * @extends Roo.bootstrap.Component
24423 * Bootstrap Menu class - container for Menu
24424 * @cfg {String} html Text of the menu
24425 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
24426 * @cfg {String} icon Font awesome icon
24427 * @cfg {String} pos Menu align to (top | bottom) default bottom
24431 * Create a new Menu
24432 * @param {Object} config The config object
24436 Roo.bootstrap.menu.Menu = function(config){
24437 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
24441 * @event beforeshow
24442 * Fires before this menu is displayed
24443 * @param {Roo.bootstrap.menu.Menu} this
24447 * @event beforehide
24448 * Fires before this menu is hidden
24449 * @param {Roo.bootstrap.menu.Menu} this
24454 * Fires after this menu is displayed
24455 * @param {Roo.bootstrap.menu.Menu} this
24460 * Fires after this menu is hidden
24461 * @param {Roo.bootstrap.menu.Menu} this
24466 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
24467 * @param {Roo.bootstrap.menu.Menu} this
24468 * @param {Roo.EventObject} e
24475 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
24479 weight : 'default',
24484 getChildContainer : function() {
24485 if(this.isSubMenu){
24489 return this.el.select('ul.dropdown-menu', true).first();
24492 getAutoCreate : function()
24497 cls : 'roo-menu-text',
24505 cls : 'fa ' + this.icon
24516 cls : 'dropdown-button btn btn-' + this.weight,
24521 cls : 'dropdown-toggle btn btn-' + this.weight,
24531 cls : 'dropdown-menu'
24537 if(this.pos == 'top'){
24538 cfg.cls += ' dropup';
24541 if(this.isSubMenu){
24544 cls : 'dropdown-menu'
24551 onRender : function(ct, position)
24553 this.isSubMenu = ct.hasClass('dropdown-submenu');
24555 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
24558 initEvents : function()
24560 if(this.isSubMenu){
24564 this.hidden = true;
24566 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
24567 this.triggerEl.on('click', this.onTriggerPress, this);
24569 this.buttonEl = this.el.select('button.dropdown-button', true).first();
24570 this.buttonEl.on('click', this.onClick, this);
24576 if(this.isSubMenu){
24580 return this.el.select('ul.dropdown-menu', true).first();
24583 onClick : function(e)
24585 this.fireEvent("click", this, e);
24588 onTriggerPress : function(e)
24590 if (this.isVisible()) {
24597 isVisible : function(){
24598 return !this.hidden;
24603 this.fireEvent("beforeshow", this);
24605 this.hidden = false;
24606 this.el.addClass('open');
24608 Roo.get(document).on("mouseup", this.onMouseUp, this);
24610 this.fireEvent("show", this);
24617 this.fireEvent("beforehide", this);
24619 this.hidden = true;
24620 this.el.removeClass('open');
24622 Roo.get(document).un("mouseup", this.onMouseUp);
24624 this.fireEvent("hide", this);
24627 onMouseUp : function()
24641 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24644 * @class Roo.bootstrap.menu.Item
24645 * @extends Roo.bootstrap.Component
24646 * Bootstrap MenuItem class
24647 * @cfg {Boolean} submenu (true | false) default false
24648 * @cfg {String} html text of the item
24649 * @cfg {String} href the link
24650 * @cfg {Boolean} disable (true | false) default false
24651 * @cfg {Boolean} preventDefault (true | false) default true
24652 * @cfg {String} icon Font awesome icon
24653 * @cfg {String} pos Submenu align to (left | right) default right
24657 * Create a new Item
24658 * @param {Object} config The config object
24662 Roo.bootstrap.menu.Item = function(config){
24663 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
24667 * Fires when the mouse is hovering over this menu
24668 * @param {Roo.bootstrap.menu.Item} this
24669 * @param {Roo.EventObject} e
24674 * Fires when the mouse exits this menu
24675 * @param {Roo.bootstrap.menu.Item} this
24676 * @param {Roo.EventObject} e
24682 * The raw click event for the entire grid.
24683 * @param {Roo.EventObject} e
24689 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
24694 preventDefault: true,
24699 getAutoCreate : function()
24704 cls : 'roo-menu-item-text',
24712 cls : 'fa ' + this.icon
24721 href : this.href || '#',
24728 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
24732 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
24734 if(this.pos == 'left'){
24735 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
24742 initEvents : function()
24744 this.el.on('mouseover', this.onMouseOver, this);
24745 this.el.on('mouseout', this.onMouseOut, this);
24747 this.el.select('a', true).first().on('click', this.onClick, this);
24751 onClick : function(e)
24753 if(this.preventDefault){
24754 e.preventDefault();
24757 this.fireEvent("click", this, e);
24760 onMouseOver : function(e)
24762 if(this.submenu && this.pos == 'left'){
24763 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
24766 this.fireEvent("mouseover", this, e);
24769 onMouseOut : function(e)
24771 this.fireEvent("mouseout", this, e);
24783 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24786 * @class Roo.bootstrap.menu.Separator
24787 * @extends Roo.bootstrap.Component
24788 * Bootstrap Separator class
24791 * Create a new Separator
24792 * @param {Object} config The config object
24796 Roo.bootstrap.menu.Separator = function(config){
24797 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24800 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
24802 getAutoCreate : function(){
24823 * @class Roo.bootstrap.Tooltip
24824 * Bootstrap Tooltip class
24825 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24826 * to determine which dom element triggers the tooltip.
24828 * It needs to add support for additional attributes like tooltip-position
24831 * Create a new Toolti
24832 * @param {Object} config The config object
24835 Roo.bootstrap.Tooltip = function(config){
24836 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24839 Roo.apply(Roo.bootstrap.Tooltip, {
24841 * @function init initialize tooltip monitoring.
24845 currentTip : false,
24846 currentRegion : false,
24852 Roo.get(document).on('mouseover', this.enter ,this);
24853 Roo.get(document).on('mouseout', this.leave, this);
24856 this.currentTip = new Roo.bootstrap.Tooltip();
24859 enter : function(ev)
24861 var dom = ev.getTarget();
24863 //Roo.log(['enter',dom]);
24864 var el = Roo.fly(dom);
24865 if (this.currentEl) {
24867 //Roo.log(this.currentEl);
24868 //Roo.log(this.currentEl.contains(dom));
24869 if (this.currentEl == el) {
24872 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24878 if (this.currentTip.el) {
24879 this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24883 if(!el || el.dom == document){
24889 // you can not look for children, as if el is the body.. then everythign is the child..
24890 if (!el.attr('tooltip')) { //
24891 if (!el.select("[tooltip]").elements.length) {
24894 // is the mouse over this child...?
24895 bindEl = el.select("[tooltip]").first();
24896 var xy = ev.getXY();
24897 if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24898 //Roo.log("not in region.");
24901 //Roo.log("child element over..");
24904 this.currentEl = bindEl;
24905 this.currentTip.bind(bindEl);
24906 this.currentRegion = Roo.lib.Region.getRegion(dom);
24907 this.currentTip.enter();
24910 leave : function(ev)
24912 var dom = ev.getTarget();
24913 //Roo.log(['leave',dom]);
24914 if (!this.currentEl) {
24919 if (dom != this.currentEl.dom) {
24922 var xy = ev.getXY();
24923 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
24926 // only activate leave if mouse cursor is outside... bounding box..
24931 if (this.currentTip) {
24932 this.currentTip.leave();
24934 //Roo.log('clear currentEl');
24935 this.currentEl = false;
24940 'left' : ['r-l', [-2,0], 'right'],
24941 'right' : ['l-r', [2,0], 'left'],
24942 'bottom' : ['t-b', [0,2], 'top'],
24943 'top' : [ 'b-t', [0,-2], 'bottom']
24949 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
24954 delay : null, // can be { show : 300 , hide: 500}
24958 hoverState : null, //???
24960 placement : 'bottom',
24962 getAutoCreate : function(){
24969 cls : 'tooltip-arrow'
24972 cls : 'tooltip-inner'
24979 bind : function(el)
24985 enter : function () {
24987 if (this.timeout != null) {
24988 clearTimeout(this.timeout);
24991 this.hoverState = 'in';
24992 //Roo.log("enter - show");
24993 if (!this.delay || !this.delay.show) {
24998 this.timeout = setTimeout(function () {
24999 if (_t.hoverState == 'in') {
25002 }, this.delay.show);
25006 clearTimeout(this.timeout);
25008 this.hoverState = 'out';
25009 if (!this.delay || !this.delay.hide) {
25015 this.timeout = setTimeout(function () {
25016 //Roo.log("leave - timeout");
25018 if (_t.hoverState == 'out') {
25020 Roo.bootstrap.Tooltip.currentEl = false;
25028 this.render(document.body);
25031 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25033 var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25035 this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25037 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25039 var placement = typeof this.placement == 'function' ?
25040 this.placement.call(this, this.el, on_el) :
25043 var autoToken = /\s?auto?\s?/i;
25044 var autoPlace = autoToken.test(placement);
25046 placement = placement.replace(autoToken, '') || 'top';
25050 //this.el.setXY([0,0]);
25052 //this.el.dom.style.display='block';
25054 //this.el.appendTo(on_el);
25056 var p = this.getPosition();
25057 var box = this.el.getBox();
25063 var align = Roo.bootstrap.Tooltip.alignment[placement];
25065 var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25067 if(placement == 'top' || placement == 'bottom'){
25069 placement = 'right';
25072 if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25073 placement = 'left';
25076 var scroll = Roo.select('body', true).first().getScroll();
25078 if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25084 align = Roo.bootstrap.Tooltip.alignment[placement];
25086 this.el.alignTo(this.bindEl, align[0],align[1]);
25087 //var arrow = this.el.select('.arrow',true).first();
25088 //arrow.set(align[2],
25090 this.el.addClass(placement);
25092 this.el.addClass('in fade');
25094 this.hoverState = null;
25096 if (this.el.hasClass('fade')) {
25107 //this.el.setXY([0,0]);
25108 this.el.removeClass('in');
25124 * @class Roo.bootstrap.LocationPicker
25125 * @extends Roo.bootstrap.Component
25126 * Bootstrap LocationPicker class
25127 * @cfg {Number} latitude Position when init default 0
25128 * @cfg {Number} longitude Position when init default 0
25129 * @cfg {Number} zoom default 15
25130 * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25131 * @cfg {Boolean} mapTypeControl default false
25132 * @cfg {Boolean} disableDoubleClickZoom default false
25133 * @cfg {Boolean} scrollwheel default true
25134 * @cfg {Boolean} streetViewControl default false
25135 * @cfg {Number} radius default 0
25136 * @cfg {String} locationName
25137 * @cfg {Boolean} draggable default true
25138 * @cfg {Boolean} enableAutocomplete default false
25139 * @cfg {Boolean} enableReverseGeocode default true
25140 * @cfg {String} markerTitle
25143 * Create a new LocationPicker
25144 * @param {Object} config The config object
25148 Roo.bootstrap.LocationPicker = function(config){
25150 Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25155 * Fires when the picker initialized.
25156 * @param {Roo.bootstrap.LocationPicker} this
25157 * @param {Google Location} location
25161 * @event positionchanged
25162 * Fires when the picker position changed.
25163 * @param {Roo.bootstrap.LocationPicker} this
25164 * @param {Google Location} location
25166 positionchanged : true,
25169 * Fires when the map resize.
25170 * @param {Roo.bootstrap.LocationPicker} this
25175 * Fires when the map show.
25176 * @param {Roo.bootstrap.LocationPicker} this
25181 * Fires when the map hide.
25182 * @param {Roo.bootstrap.LocationPicker} this
25187 * Fires when click the map.
25188 * @param {Roo.bootstrap.LocationPicker} this
25189 * @param {Map event} e
25193 * @event mapRightClick
25194 * Fires when right click the map.
25195 * @param {Roo.bootstrap.LocationPicker} this
25196 * @param {Map event} e
25198 mapRightClick : true,
25200 * @event markerClick
25201 * Fires when click the marker.
25202 * @param {Roo.bootstrap.LocationPicker} this
25203 * @param {Map event} e
25205 markerClick : true,
25207 * @event markerRightClick
25208 * Fires when right click the marker.
25209 * @param {Roo.bootstrap.LocationPicker} this
25210 * @param {Map event} e
25212 markerRightClick : true,
25214 * @event OverlayViewDraw
25215 * Fires when OverlayView Draw
25216 * @param {Roo.bootstrap.LocationPicker} this
25218 OverlayViewDraw : true,
25220 * @event OverlayViewOnAdd
25221 * Fires when OverlayView Draw
25222 * @param {Roo.bootstrap.LocationPicker} this
25224 OverlayViewOnAdd : true,
25226 * @event OverlayViewOnRemove
25227 * Fires when OverlayView Draw
25228 * @param {Roo.bootstrap.LocationPicker} this
25230 OverlayViewOnRemove : true,
25232 * @event OverlayViewShow
25233 * Fires when OverlayView Draw
25234 * @param {Roo.bootstrap.LocationPicker} this
25235 * @param {Pixel} cpx
25237 OverlayViewShow : true,
25239 * @event OverlayViewHide
25240 * Fires when OverlayView Draw
25241 * @param {Roo.bootstrap.LocationPicker} this
25243 OverlayViewHide : true,
25245 * @event loadexception
25246 * Fires when load google lib failed.
25247 * @param {Roo.bootstrap.LocationPicker} this
25249 loadexception : true
25254 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component, {
25256 gMapContext: false,
25262 mapTypeControl: false,
25263 disableDoubleClickZoom: false,
25265 streetViewControl: false,
25269 enableAutocomplete: false,
25270 enableReverseGeocode: true,
25273 getAutoCreate: function()
25278 cls: 'roo-location-picker'
25284 initEvents: function(ct, position)
25286 if(!this.el.getWidth() || this.isApplied()){
25290 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25295 initial: function()
25297 if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
25298 this.fireEvent('loadexception', this);
25302 if(!this.mapTypeId){
25303 this.mapTypeId = google.maps.MapTypeId.ROADMAP;
25306 this.gMapContext = this.GMapContext();
25308 this.initOverlayView();
25310 this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
25314 google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
25315 _this.setPosition(_this.gMapContext.marker.position);
25318 google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
25319 _this.fireEvent('mapClick', this, event);
25323 google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
25324 _this.fireEvent('mapRightClick', this, event);
25328 google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
25329 _this.fireEvent('markerClick', this, event);
25333 google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
25334 _this.fireEvent('markerRightClick', this, event);
25338 this.setPosition(this.gMapContext.location);
25340 this.fireEvent('initial', this, this.gMapContext.location);
25343 initOverlayView: function()
25347 Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
25351 _this.fireEvent('OverlayViewDraw', _this);
25356 _this.fireEvent('OverlayViewOnAdd', _this);
25359 onRemove: function()
25361 _this.fireEvent('OverlayViewOnRemove', _this);
25364 show: function(cpx)
25366 _this.fireEvent('OverlayViewShow', _this, cpx);
25371 _this.fireEvent('OverlayViewHide', _this);
25377 fromLatLngToContainerPixel: function(event)
25379 return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
25382 isApplied: function()
25384 return this.getGmapContext() == false ? false : true;
25387 getGmapContext: function()
25389 return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
25392 GMapContext: function()
25394 var position = new google.maps.LatLng(this.latitude, this.longitude);
25396 var _map = new google.maps.Map(this.el.dom, {
25399 mapTypeId: this.mapTypeId,
25400 mapTypeControl: this.mapTypeControl,
25401 disableDoubleClickZoom: this.disableDoubleClickZoom,
25402 scrollwheel: this.scrollwheel,
25403 streetViewControl: this.streetViewControl,
25404 locationName: this.locationName,
25405 draggable: this.draggable,
25406 enableAutocomplete: this.enableAutocomplete,
25407 enableReverseGeocode: this.enableReverseGeocode
25410 var _marker = new google.maps.Marker({
25411 position: position,
25413 title: this.markerTitle,
25414 draggable: this.draggable
25421 location: position,
25422 radius: this.radius,
25423 locationName: this.locationName,
25424 addressComponents: {
25425 formatted_address: null,
25426 addressLine1: null,
25427 addressLine2: null,
25429 streetNumber: null,
25433 stateOrProvince: null
25436 domContainer: this.el.dom,
25437 geodecoder: new google.maps.Geocoder()
25441 drawCircle: function(center, radius, options)
25443 if (this.gMapContext.circle != null) {
25444 this.gMapContext.circle.setMap(null);
25448 options = Roo.apply({}, options, {
25449 strokeColor: "#0000FF",
25450 strokeOpacity: .35,
25452 fillColor: "#0000FF",
25456 options.map = this.gMapContext.map;
25457 options.radius = radius;
25458 options.center = center;
25459 this.gMapContext.circle = new google.maps.Circle(options);
25460 return this.gMapContext.circle;
25466 setPosition: function(location)
25468 this.gMapContext.location = location;
25469 this.gMapContext.marker.setPosition(location);
25470 this.gMapContext.map.panTo(location);
25471 this.drawCircle(location, this.gMapContext.radius, {});
25475 if (this.gMapContext.settings.enableReverseGeocode) {
25476 this.gMapContext.geodecoder.geocode({
25477 latLng: this.gMapContext.location
25478 }, function(results, status) {
25480 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
25481 _this.gMapContext.locationName = results[0].formatted_address;
25482 _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
25484 _this.fireEvent('positionchanged', this, location);
25491 this.fireEvent('positionchanged', this, location);
25496 google.maps.event.trigger(this.gMapContext.map, "resize");
25498 this.gMapContext.map.setCenter(this.gMapContext.marker.position);
25500 this.fireEvent('resize', this);
25503 setPositionByLatLng: function(latitude, longitude)
25505 this.setPosition(new google.maps.LatLng(latitude, longitude));
25508 getCurrentPosition: function()
25511 latitude: this.gMapContext.location.lat(),
25512 longitude: this.gMapContext.location.lng()
25516 getAddressName: function()
25518 return this.gMapContext.locationName;
25521 getAddressComponents: function()
25523 return this.gMapContext.addressComponents;
25526 address_component_from_google_geocode: function(address_components)
25530 for (var i = 0; i < address_components.length; i++) {
25531 var component = address_components[i];
25532 if (component.types.indexOf("postal_code") >= 0) {
25533 result.postalCode = component.short_name;
25534 } else if (component.types.indexOf("street_number") >= 0) {
25535 result.streetNumber = component.short_name;
25536 } else if (component.types.indexOf("route") >= 0) {
25537 result.streetName = component.short_name;
25538 } else if (component.types.indexOf("neighborhood") >= 0) {
25539 result.city = component.short_name;
25540 } else if (component.types.indexOf("locality") >= 0) {
25541 result.city = component.short_name;
25542 } else if (component.types.indexOf("sublocality") >= 0) {
25543 result.district = component.short_name;
25544 } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
25545 result.stateOrProvince = component.short_name;
25546 } else if (component.types.indexOf("country") >= 0) {
25547 result.country = component.short_name;
25551 result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
25552 result.addressLine2 = "";
25556 setZoomLevel: function(zoom)
25558 this.gMapContext.map.setZoom(zoom);
25571 this.fireEvent('show', this);
25582 this.fireEvent('hide', this);
25587 Roo.apply(Roo.bootstrap.LocationPicker, {
25589 OverlayView : function(map, options)
25591 options = options || {};
25605 * @class Roo.bootstrap.Alert
25606 * @extends Roo.bootstrap.Component
25607 * Bootstrap Alert class
25608 * @cfg {String} title The title of alert
25609 * @cfg {String} html The content of alert
25610 * @cfg {String} weight ( success | info | warning | danger )
25611 * @cfg {String} faicon font-awesomeicon
25614 * Create a new alert
25615 * @param {Object} config The config object
25619 Roo.bootstrap.Alert = function(config){
25620 Roo.bootstrap.Alert.superclass.constructor.call(this, config);
25624 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component, {
25631 getAutoCreate : function()
25640 cls : 'roo-alert-icon'
25645 cls : 'roo-alert-title',
25650 cls : 'roo-alert-text',
25657 cfg.cn[0].cls += ' fa ' + this.faicon;
25661 cfg.cls += ' alert-' + this.weight;
25667 initEvents: function()
25669 this.el.setVisibilityMode(Roo.Element.DISPLAY);
25672 setTitle : function(str)
25674 this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
25677 setText : function(str)
25679 this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
25682 setWeight : function(weight)
25685 this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
25688 this.weight = weight;
25690 this.el.select('.alert',true).first().addClass('alert-' + this.weight);
25693 setIcon : function(icon)
25696 this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
25699 this.faicon = icon;
25701 this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
25722 * @class Roo.bootstrap.UploadCropbox
25723 * @extends Roo.bootstrap.Component
25724 * Bootstrap UploadCropbox class
25725 * @cfg {String} emptyText show when image has been loaded
25726 * @cfg {String} rotateNotify show when image too small to rotate
25727 * @cfg {Number} errorTimeout default 3000
25728 * @cfg {Number} minWidth default 300
25729 * @cfg {Number} minHeight default 300
25730 * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
25731 * @cfg {Boolean} isDocument (true|false) default false
25732 * @cfg {String} url action url
25733 * @cfg {String} paramName default 'imageUpload'
25734 * @cfg {String} method default POST
25735 * @cfg {Boolean} loadMask (true|false) default true
25736 * @cfg {Boolean} loadingText default 'Loading...'
25739 * Create a new UploadCropbox
25740 * @param {Object} config The config object
25743 Roo.bootstrap.UploadCropbox = function(config){
25744 Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
25748 * @event beforeselectfile
25749 * Fire before select file
25750 * @param {Roo.bootstrap.UploadCropbox} this
25752 "beforeselectfile" : true,
25755 * Fire after initEvent
25756 * @param {Roo.bootstrap.UploadCropbox} this
25761 * Fire after initEvent
25762 * @param {Roo.bootstrap.UploadCropbox} this
25763 * @param {String} data
25768 * Fire when preparing the file data
25769 * @param {Roo.bootstrap.UploadCropbox} this
25770 * @param {Object} file
25775 * Fire when get exception
25776 * @param {Roo.bootstrap.UploadCropbox} this
25777 * @param {XMLHttpRequest} xhr
25779 "exception" : true,
25781 * @event beforeloadcanvas
25782 * Fire before load the canvas
25783 * @param {Roo.bootstrap.UploadCropbox} this
25784 * @param {String} src
25786 "beforeloadcanvas" : true,
25789 * Fire when trash image
25790 * @param {Roo.bootstrap.UploadCropbox} this
25795 * Fire when download the image
25796 * @param {Roo.bootstrap.UploadCropbox} this
25800 * @event footerbuttonclick
25801 * Fire when footerbuttonclick
25802 * @param {Roo.bootstrap.UploadCropbox} this
25803 * @param {String} type
25805 "footerbuttonclick" : true,
25809 * @param {Roo.bootstrap.UploadCropbox} this
25814 * Fire when rotate the image
25815 * @param {Roo.bootstrap.UploadCropbox} this
25816 * @param {String} pos
25821 * Fire when inspect the file
25822 * @param {Roo.bootstrap.UploadCropbox} this
25823 * @param {Object} file
25828 * Fire when xhr upload the file
25829 * @param {Roo.bootstrap.UploadCropbox} this
25830 * @param {Object} data
25835 * Fire when arrange the file data
25836 * @param {Roo.bootstrap.UploadCropbox} this
25837 * @param {Object} formData
25842 this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25845 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component, {
25847 emptyText : 'Click to upload image',
25848 rotateNotify : 'Image is too small to rotate',
25849 errorTimeout : 3000,
25863 cropType : 'image/jpeg',
25865 canvasLoaded : false,
25866 isDocument : false,
25868 paramName : 'imageUpload',
25870 loadingText : 'Loading...',
25873 getAutoCreate : function()
25877 cls : 'roo-upload-cropbox',
25881 cls : 'roo-upload-cropbox-selector',
25886 cls : 'roo-upload-cropbox-body',
25887 style : 'cursor:pointer',
25891 cls : 'roo-upload-cropbox-preview'
25895 cls : 'roo-upload-cropbox-thumb'
25899 cls : 'roo-upload-cropbox-empty-notify',
25900 html : this.emptyText
25904 cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25905 html : this.rotateNotify
25911 cls : 'roo-upload-cropbox-footer',
25914 cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25924 onRender : function(ct, position)
25926 Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25928 if (this.buttons.length) {
25930 Roo.each(this.buttons, function(bb) {
25932 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25934 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25940 this.maskEl = this.el;
25944 initEvents : function()
25946 this.urlAPI = (window.createObjectURL && window) ||
25947 (window.URL && URL.revokeObjectURL && URL) ||
25948 (window.webkitURL && webkitURL);
25950 this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25951 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25953 this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25954 this.selectorEl.hide();
25956 this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25957 this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25959 this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25960 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25961 this.thumbEl.hide();
25963 this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25964 this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25966 this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25967 this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25968 this.errorEl.hide();
25970 this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25971 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25972 this.footerEl.hide();
25974 this.setThumbBoxSize();
25980 this.fireEvent('initial', this);
25987 window.addEventListener("resize", function() { _this.resize(); } );
25989 this.bodyEl.on('click', this.beforeSelectFile, this);
25992 this.bodyEl.on('touchstart', this.onTouchStart, this);
25993 this.bodyEl.on('touchmove', this.onTouchMove, this);
25994 this.bodyEl.on('touchend', this.onTouchEnd, this);
25998 this.bodyEl.on('mousedown', this.onMouseDown, this);
25999 this.bodyEl.on('mousemove', this.onMouseMove, this);
26000 var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26001 this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26002 Roo.get(document).on('mouseup', this.onMouseUp, this);
26005 this.selectorEl.on('change', this.onFileSelected, this);
26011 this.baseScale = 1;
26013 this.baseRotate = 1;
26014 this.dragable = false;
26015 this.pinching = false;
26018 this.cropData = false;
26019 this.notifyEl.dom.innerHTML = this.emptyText;
26021 this.selectorEl.dom.value = '';
26025 resize : function()
26027 if(this.fireEvent('resize', this) != false){
26028 this.setThumbBoxPosition();
26029 this.setCanvasPosition();
26033 onFooterButtonClick : function(e, el, o, type)
26036 case 'rotate-left' :
26037 this.onRotateLeft(e);
26039 case 'rotate-right' :
26040 this.onRotateRight(e);
26043 this.beforeSelectFile(e);
26058 this.fireEvent('footerbuttonclick', this, type);
26061 beforeSelectFile : function(e)
26063 e.preventDefault();
26065 if(this.fireEvent('beforeselectfile', this) != false){
26066 this.selectorEl.dom.click();
26070 onFileSelected : function(e)
26072 e.preventDefault();
26074 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26078 var file = this.selectorEl.dom.files[0];
26080 if(this.fireEvent('inspect', this, file) != false){
26081 this.prepare(file);
26086 trash : function(e)
26088 this.fireEvent('trash', this);
26091 download : function(e)
26093 this.fireEvent('download', this);
26096 loadCanvas : function(src)
26098 if(this.fireEvent('beforeloadcanvas', this, src) != false){
26102 this.imageEl = document.createElement('img');
26106 this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26108 this.imageEl.src = src;
26112 onLoadCanvas : function()
26114 this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26115 this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26117 this.bodyEl.un('click', this.beforeSelectFile, this);
26119 this.notifyEl.hide();
26120 this.thumbEl.show();
26121 this.footerEl.show();
26123 this.baseRotateLevel();
26125 if(this.isDocument){
26126 this.setThumbBoxSize();
26129 this.setThumbBoxPosition();
26131 this.baseScaleLevel();
26137 this.canvasLoaded = true;
26140 this.maskEl.unmask();
26145 setCanvasPosition : function()
26147 if(!this.canvasEl){
26151 var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26152 var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26154 this.previewEl.setLeft(pw);
26155 this.previewEl.setTop(ph);
26159 onMouseDown : function(e)
26163 this.dragable = true;
26164 this.pinching = false;
26166 if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26167 this.dragable = false;
26171 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26172 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26176 onMouseMove : function(e)
26180 if(!this.canvasLoaded){
26184 if (!this.dragable){
26188 var minX = Math.ceil(this.thumbEl.getLeft(true));
26189 var minY = Math.ceil(this.thumbEl.getTop(true));
26191 var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26192 var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26194 var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26195 var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26197 x = x - this.mouseX;
26198 y = y - this.mouseY;
26200 var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26201 var bgY = Math.ceil(y + this.previewEl.getTop(true));
26203 bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26204 bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26206 this.previewEl.setLeft(bgX);
26207 this.previewEl.setTop(bgY);
26209 this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26210 this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26213 onMouseUp : function(e)
26217 this.dragable = false;
26220 onMouseWheel : function(e)
26224 this.startScale = this.scale;
26226 this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26228 if(!this.zoomable()){
26229 this.scale = this.startScale;
26238 zoomable : function()
26240 var minScale = this.thumbEl.getWidth() / this.minWidth;
26242 if(this.minWidth < this.minHeight){
26243 minScale = this.thumbEl.getHeight() / this.minHeight;
26246 var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26247 var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26251 (this.rotate == 0 || this.rotate == 180) &&
26253 width > this.imageEl.OriginWidth ||
26254 height > this.imageEl.OriginHeight ||
26255 (width < this.minWidth && height < this.minHeight)
26263 (this.rotate == 90 || this.rotate == 270) &&
26265 width > this.imageEl.OriginWidth ||
26266 height > this.imageEl.OriginHeight ||
26267 (width < this.minHeight && height < this.minWidth)
26274 !this.isDocument &&
26275 (this.rotate == 0 || this.rotate == 180) &&
26277 width < this.minWidth ||
26278 width > this.imageEl.OriginWidth ||
26279 height < this.minHeight ||
26280 height > this.imageEl.OriginHeight
26287 !this.isDocument &&
26288 (this.rotate == 90 || this.rotate == 270) &&
26290 width < this.minHeight ||
26291 width > this.imageEl.OriginWidth ||
26292 height < this.minWidth ||
26293 height > this.imageEl.OriginHeight
26303 onRotateLeft : function(e)
26305 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26307 var minScale = this.thumbEl.getWidth() / this.minWidth;
26309 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26310 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26312 this.startScale = this.scale;
26314 while (this.getScaleLevel() < minScale){
26316 this.scale = this.scale + 1;
26318 if(!this.zoomable()){
26323 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26324 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26329 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26336 this.scale = this.startScale;
26338 this.onRotateFail();
26343 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
26345 if(this.isDocument){
26346 this.setThumbBoxSize();
26347 this.setThumbBoxPosition();
26348 this.setCanvasPosition();
26353 this.fireEvent('rotate', this, 'left');
26357 onRotateRight : function(e)
26359 if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
26361 var minScale = this.thumbEl.getWidth() / this.minWidth;
26363 var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
26364 var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
26366 this.startScale = this.scale;
26368 while (this.getScaleLevel() < minScale){
26370 this.scale = this.scale + 1;
26372 if(!this.zoomable()){
26377 Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
26378 Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
26383 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26390 this.scale = this.startScale;
26392 this.onRotateFail();
26397 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
26399 if(this.isDocument){
26400 this.setThumbBoxSize();
26401 this.setThumbBoxPosition();
26402 this.setCanvasPosition();
26407 this.fireEvent('rotate', this, 'right');
26410 onRotateFail : function()
26412 this.errorEl.show(true);
26416 (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
26421 this.previewEl.dom.innerHTML = '';
26423 var canvasEl = document.createElement("canvas");
26425 var contextEl = canvasEl.getContext("2d");
26427 canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26428 canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26429 var center = this.imageEl.OriginWidth / 2;
26431 if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
26432 canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26433 canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26434 center = this.imageEl.OriginHeight / 2;
26437 contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
26439 contextEl.translate(center, center);
26440 contextEl.rotate(this.rotate * Math.PI / 180);
26442 contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26444 this.canvasEl = document.createElement("canvas");
26446 this.contextEl = this.canvasEl.getContext("2d");
26448 switch (this.rotate) {
26451 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26452 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26454 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26459 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26460 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26462 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26463 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);
26467 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26472 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
26473 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
26475 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26476 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);
26480 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);
26485 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
26486 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
26488 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26489 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
26493 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);
26500 this.previewEl.appendChild(this.canvasEl);
26502 this.setCanvasPosition();
26507 if(!this.canvasLoaded){
26511 var imageCanvas = document.createElement("canvas");
26513 var imageContext = imageCanvas.getContext("2d");
26515 imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26516 imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
26518 var center = imageCanvas.width / 2;
26520 imageContext.translate(center, center);
26522 imageContext.rotate(this.rotate * Math.PI / 180);
26524 imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
26526 var canvas = document.createElement("canvas");
26528 var context = canvas.getContext("2d");
26530 canvas.width = this.minWidth;
26531 canvas.height = this.minHeight;
26533 switch (this.rotate) {
26536 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26537 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26539 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26540 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26542 var targetWidth = this.minWidth - 2 * x;
26543 var targetHeight = this.minHeight - 2 * y;
26547 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26548 scale = targetWidth / width;
26551 if(x > 0 && y == 0){
26552 scale = targetHeight / height;
26555 if(x > 0 && y > 0){
26556 scale = targetWidth / width;
26558 if(width < height){
26559 scale = targetHeight / height;
26563 context.scale(scale, scale);
26565 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26566 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26568 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26569 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26571 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26576 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26577 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26579 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26580 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26582 var targetWidth = this.minWidth - 2 * x;
26583 var targetHeight = this.minHeight - 2 * y;
26587 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26588 scale = targetWidth / width;
26591 if(x > 0 && y == 0){
26592 scale = targetHeight / height;
26595 if(x > 0 && y > 0){
26596 scale = targetWidth / width;
26598 if(width < height){
26599 scale = targetHeight / height;
26603 context.scale(scale, scale);
26605 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26606 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26608 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26609 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26611 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26613 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26618 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
26619 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
26621 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26622 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26624 var targetWidth = this.minWidth - 2 * x;
26625 var targetHeight = this.minHeight - 2 * y;
26629 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26630 scale = targetWidth / width;
26633 if(x > 0 && y == 0){
26634 scale = targetHeight / height;
26637 if(x > 0 && y > 0){
26638 scale = targetWidth / width;
26640 if(width < height){
26641 scale = targetHeight / height;
26645 context.scale(scale, scale);
26647 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26648 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26650 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26651 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26653 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26654 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
26656 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26661 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
26662 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
26664 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
26665 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
26667 var targetWidth = this.minWidth - 2 * x;
26668 var targetHeight = this.minHeight - 2 * y;
26672 if((x == 0 && y == 0) || (x == 0 && y > 0)){
26673 scale = targetWidth / width;
26676 if(x > 0 && y == 0){
26677 scale = targetHeight / height;
26680 if(x > 0 && y > 0){
26681 scale = targetWidth / width;
26683 if(width < height){
26684 scale = targetHeight / height;
26688 context.scale(scale, scale);
26690 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
26691 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
26693 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
26694 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
26696 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
26698 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
26705 this.cropData = canvas.toDataURL(this.cropType);
26707 if(this.fireEvent('crop', this, this.cropData) !== false){
26708 this.process(this.file, this.cropData);
26715 setThumbBoxSize : function()
26719 if(this.isDocument && typeof(this.imageEl) != 'undefined'){
26720 width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
26721 height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
26723 this.minWidth = width;
26724 this.minHeight = height;
26726 if(this.rotate == 90 || this.rotate == 270){
26727 this.minWidth = height;
26728 this.minHeight = width;
26733 width = Math.ceil(this.minWidth * height / this.minHeight);
26735 if(this.minWidth > this.minHeight){
26737 height = Math.ceil(this.minHeight * width / this.minWidth);
26740 this.thumbEl.setStyle({
26741 width : width + 'px',
26742 height : height + 'px'
26749 setThumbBoxPosition : function()
26751 var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
26752 var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
26754 this.thumbEl.setLeft(x);
26755 this.thumbEl.setTop(y);
26759 baseRotateLevel : function()
26761 this.baseRotate = 1;
26764 typeof(this.exif) != 'undefined' &&
26765 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
26766 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
26768 this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
26771 this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
26775 baseScaleLevel : function()
26779 if(this.isDocument){
26781 if(this.baseRotate == 6 || this.baseRotate == 8){
26783 height = this.thumbEl.getHeight();
26784 this.baseScale = height / this.imageEl.OriginWidth;
26786 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26787 width = this.thumbEl.getWidth();
26788 this.baseScale = width / this.imageEl.OriginHeight;
26794 height = this.thumbEl.getHeight();
26795 this.baseScale = height / this.imageEl.OriginHeight;
26797 if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26798 width = this.thumbEl.getWidth();
26799 this.baseScale = width / this.imageEl.OriginWidth;
26805 if(this.baseRotate == 6 || this.baseRotate == 8){
26807 width = this.thumbEl.getHeight();
26808 this.baseScale = width / this.imageEl.OriginHeight;
26810 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26811 height = this.thumbEl.getWidth();
26812 this.baseScale = height / this.imageEl.OriginHeight;
26815 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26816 height = this.thumbEl.getWidth();
26817 this.baseScale = height / this.imageEl.OriginHeight;
26819 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26820 width = this.thumbEl.getHeight();
26821 this.baseScale = width / this.imageEl.OriginWidth;
26828 width = this.thumbEl.getWidth();
26829 this.baseScale = width / this.imageEl.OriginWidth;
26831 if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26832 height = this.thumbEl.getHeight();
26833 this.baseScale = height / this.imageEl.OriginHeight;
26836 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26838 height = this.thumbEl.getHeight();
26839 this.baseScale = height / this.imageEl.OriginHeight;
26841 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26842 width = this.thumbEl.getWidth();
26843 this.baseScale = width / this.imageEl.OriginWidth;
26851 getScaleLevel : function()
26853 return this.baseScale * Math.pow(1.1, this.scale);
26856 onTouchStart : function(e)
26858 if(!this.canvasLoaded){
26859 this.beforeSelectFile(e);
26863 var touches = e.browserEvent.touches;
26869 if(touches.length == 1){
26870 this.onMouseDown(e);
26874 if(touches.length != 2){
26880 for(var i = 0, finger; finger = touches[i]; i++){
26881 coords.push(finger.pageX, finger.pageY);
26884 var x = Math.pow(coords[0] - coords[2], 2);
26885 var y = Math.pow(coords[1] - coords[3], 2);
26887 this.startDistance = Math.sqrt(x + y);
26889 this.startScale = this.scale;
26891 this.pinching = true;
26892 this.dragable = false;
26896 onTouchMove : function(e)
26898 if(!this.pinching && !this.dragable){
26902 var touches = e.browserEvent.touches;
26909 this.onMouseMove(e);
26915 for(var i = 0, finger; finger = touches[i]; i++){
26916 coords.push(finger.pageX, finger.pageY);
26919 var x = Math.pow(coords[0] - coords[2], 2);
26920 var y = Math.pow(coords[1] - coords[3], 2);
26922 this.endDistance = Math.sqrt(x + y);
26924 this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26926 if(!this.zoomable()){
26927 this.scale = this.startScale;
26935 onTouchEnd : function(e)
26937 this.pinching = false;
26938 this.dragable = false;
26942 process : function(file, crop)
26945 this.maskEl.mask(this.loadingText);
26948 this.xhr = new XMLHttpRequest();
26950 file.xhr = this.xhr;
26952 this.xhr.open(this.method, this.url, true);
26955 "Accept": "application/json",
26956 "Cache-Control": "no-cache",
26957 "X-Requested-With": "XMLHttpRequest"
26960 for (var headerName in headers) {
26961 var headerValue = headers[headerName];
26963 this.xhr.setRequestHeader(headerName, headerValue);
26969 this.xhr.onload = function()
26971 _this.xhrOnLoad(_this.xhr);
26974 this.xhr.onerror = function()
26976 _this.xhrOnError(_this.xhr);
26979 var formData = new FormData();
26981 formData.append('returnHTML', 'NO');
26984 formData.append('crop', crop);
26987 if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26988 formData.append(this.paramName, file, file.name);
26991 if(typeof(file.filename) != 'undefined'){
26992 formData.append('filename', file.filename);
26995 if(typeof(file.mimetype) != 'undefined'){
26996 formData.append('mimetype', file.mimetype);
26999 if(this.fireEvent('arrange', this, formData) != false){
27000 this.xhr.send(formData);
27004 xhrOnLoad : function(xhr)
27007 this.maskEl.unmask();
27010 if (xhr.readyState !== 4) {
27011 this.fireEvent('exception', this, xhr);
27015 var response = Roo.decode(xhr.responseText);
27017 if(!response.success){
27018 this.fireEvent('exception', this, xhr);
27022 var response = Roo.decode(xhr.responseText);
27024 this.fireEvent('upload', this, response);
27028 xhrOnError : function()
27031 this.maskEl.unmask();
27034 Roo.log('xhr on error');
27036 var response = Roo.decode(xhr.responseText);
27042 prepare : function(file)
27045 this.maskEl.mask(this.loadingText);
27051 if(typeof(file) === 'string'){
27052 this.loadCanvas(file);
27056 if(!file || !this.urlAPI){
27061 this.cropType = file.type;
27065 if(this.fireEvent('prepare', this, this.file) != false){
27067 var reader = new FileReader();
27069 reader.onload = function (e) {
27070 if (e.target.error) {
27071 Roo.log(e.target.error);
27075 var buffer = e.target.result,
27076 dataView = new DataView(buffer),
27078 maxOffset = dataView.byteLength - 4,
27082 if (dataView.getUint16(0) === 0xffd8) {
27083 while (offset < maxOffset) {
27084 markerBytes = dataView.getUint16(offset);
27086 if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27087 markerLength = dataView.getUint16(offset + 2) + 2;
27088 if (offset + markerLength > dataView.byteLength) {
27089 Roo.log('Invalid meta data: Invalid segment size.');
27093 if(markerBytes == 0xffe1){
27094 _this.parseExifData(
27101 offset += markerLength;
27111 var url = _this.urlAPI.createObjectURL(_this.file);
27113 _this.loadCanvas(url);
27118 reader.readAsArrayBuffer(this.file);
27124 parseExifData : function(dataView, offset, length)
27126 var tiffOffset = offset + 10,
27130 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27131 // No Exif data, might be XMP data instead
27135 // Check for the ASCII code for "Exif" (0x45786966):
27136 if (dataView.getUint32(offset + 4) !== 0x45786966) {
27137 // No Exif data, might be XMP data instead
27140 if (tiffOffset + 8 > dataView.byteLength) {
27141 Roo.log('Invalid Exif data: Invalid segment size.');
27144 // Check for the two null bytes:
27145 if (dataView.getUint16(offset + 8) !== 0x0000) {
27146 Roo.log('Invalid Exif data: Missing byte alignment offset.');
27149 // Check the byte alignment:
27150 switch (dataView.getUint16(tiffOffset)) {
27152 littleEndian = true;
27155 littleEndian = false;
27158 Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27161 // Check for the TIFF tag marker (0x002A):
27162 if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27163 Roo.log('Invalid Exif data: Missing TIFF marker.');
27166 // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27167 dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27169 this.parseExifTags(
27172 tiffOffset + dirOffset,
27177 parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27182 if (dirOffset + 6 > dataView.byteLength) {
27183 Roo.log('Invalid Exif data: Invalid directory offset.');
27186 tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27187 dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27188 if (dirEndOffset + 4 > dataView.byteLength) {
27189 Roo.log('Invalid Exif data: Invalid directory size.');
27192 for (i = 0; i < tagsNumber; i += 1) {
27196 dirOffset + 2 + 12 * i, // tag offset
27200 // Return the offset to the next directory:
27201 return dataView.getUint32(dirEndOffset, littleEndian);
27204 parseExifTag : function (dataView, tiffOffset, offset, littleEndian)
27206 var tag = dataView.getUint16(offset, littleEndian);
27208 this.exif[tag] = this.getExifValue(
27212 dataView.getUint16(offset + 2, littleEndian), // tag type
27213 dataView.getUint32(offset + 4, littleEndian), // tag length
27218 getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27220 var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27229 Roo.log('Invalid Exif data: Invalid tag type.');
27233 tagSize = tagType.size * length;
27234 // Determine if the value is contained in the dataOffset bytes,
27235 // or if the value at the dataOffset is a pointer to the actual data:
27236 dataOffset = tagSize > 4 ?
27237 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27238 if (dataOffset + tagSize > dataView.byteLength) {
27239 Roo.log('Invalid Exif data: Invalid data offset.');
27242 if (length === 1) {
27243 return tagType.getValue(dataView, dataOffset, littleEndian);
27246 for (i = 0; i < length; i += 1) {
27247 values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27250 if (tagType.ascii) {
27252 // Concatenate the chars:
27253 for (i = 0; i < values.length; i += 1) {
27255 // Ignore the terminating NULL byte(s):
27256 if (c === '\u0000') {
27268 Roo.apply(Roo.bootstrap.UploadCropbox, {
27270 'Orientation': 0x0112
27274 1: 0, //'top-left',
27276 3: 180, //'bottom-right',
27277 // 4: 'bottom-left',
27279 6: 90, //'right-top',
27280 // 7: 'right-bottom',
27281 8: 270 //'left-bottom'
27285 // byte, 8-bit unsigned int:
27287 getValue: function (dataView, dataOffset) {
27288 return dataView.getUint8(dataOffset);
27292 // ascii, 8-bit byte:
27294 getValue: function (dataView, dataOffset) {
27295 return String.fromCharCode(dataView.getUint8(dataOffset));
27300 // short, 16 bit int:
27302 getValue: function (dataView, dataOffset, littleEndian) {
27303 return dataView.getUint16(dataOffset, littleEndian);
27307 // long, 32 bit int:
27309 getValue: function (dataView, dataOffset, littleEndian) {
27310 return dataView.getUint32(dataOffset, littleEndian);
27314 // rational = two long values, first is numerator, second is denominator:
27316 getValue: function (dataView, dataOffset, littleEndian) {
27317 return dataView.getUint32(dataOffset, littleEndian) /
27318 dataView.getUint32(dataOffset + 4, littleEndian);
27322 // slong, 32 bit signed int:
27324 getValue: function (dataView, dataOffset, littleEndian) {
27325 return dataView.getInt32(dataOffset, littleEndian);
27329 // srational, two slongs, first is numerator, second is denominator:
27331 getValue: function (dataView, dataOffset, littleEndian) {
27332 return dataView.getInt32(dataOffset, littleEndian) /
27333 dataView.getInt32(dataOffset + 4, littleEndian);
27343 cls : 'btn-group roo-upload-cropbox-rotate-left',
27344 action : 'rotate-left',
27348 cls : 'btn btn-default',
27349 html : '<i class="fa fa-undo"></i>'
27355 cls : 'btn-group roo-upload-cropbox-picture',
27356 action : 'picture',
27360 cls : 'btn btn-default',
27361 html : '<i class="fa fa-picture-o"></i>'
27367 cls : 'btn-group roo-upload-cropbox-rotate-right',
27368 action : 'rotate-right',
27372 cls : 'btn btn-default',
27373 html : '<i class="fa fa-repeat"></i>'
27381 cls : 'btn-group roo-upload-cropbox-rotate-left',
27382 action : 'rotate-left',
27386 cls : 'btn btn-default',
27387 html : '<i class="fa fa-undo"></i>'
27393 cls : 'btn-group roo-upload-cropbox-download',
27394 action : 'download',
27398 cls : 'btn btn-default',
27399 html : '<i class="fa fa-download"></i>'
27405 cls : 'btn-group roo-upload-cropbox-crop',
27410 cls : 'btn btn-default',
27411 html : '<i class="fa fa-crop"></i>'
27417 cls : 'btn-group roo-upload-cropbox-trash',
27422 cls : 'btn btn-default',
27423 html : '<i class="fa fa-trash"></i>'
27429 cls : 'btn-group roo-upload-cropbox-rotate-right',
27430 action : 'rotate-right',
27434 cls : 'btn btn-default',
27435 html : '<i class="fa fa-repeat"></i>'
27443 cls : 'btn-group roo-upload-cropbox-rotate-left',
27444 action : 'rotate-left',
27448 cls : 'btn btn-default',
27449 html : '<i class="fa fa-undo"></i>'
27455 cls : 'btn-group roo-upload-cropbox-rotate-right',
27456 action : 'rotate-right',
27460 cls : 'btn btn-default',
27461 html : '<i class="fa fa-repeat"></i>'
27474 * @class Roo.bootstrap.DocumentManager
27475 * @extends Roo.bootstrap.Component
27476 * Bootstrap DocumentManager class
27477 * @cfg {String} paramName default 'imageUpload'
27478 * @cfg {String} toolTipName default 'filename'
27479 * @cfg {String} method default POST
27480 * @cfg {String} url action url
27481 * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
27482 * @cfg {Boolean} multiple multiple upload default true
27483 * @cfg {Number} thumbSize default 300
27484 * @cfg {String} fieldLabel
27485 * @cfg {Number} labelWidth default 4
27486 * @cfg {String} labelAlign (left|top) default left
27487 * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
27490 * Create a new DocumentManager
27491 * @param {Object} config The config object
27494 Roo.bootstrap.DocumentManager = function(config){
27495 Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
27498 this.delegates = [];
27503 * Fire when initial the DocumentManager
27504 * @param {Roo.bootstrap.DocumentManager} this
27509 * inspect selected file
27510 * @param {Roo.bootstrap.DocumentManager} this
27511 * @param {File} file
27516 * Fire when xhr load exception
27517 * @param {Roo.bootstrap.DocumentManager} this
27518 * @param {XMLHttpRequest} xhr
27520 "exception" : true,
27522 * @event afterupload
27523 * Fire when xhr load exception
27524 * @param {Roo.bootstrap.DocumentManager} this
27525 * @param {XMLHttpRequest} xhr
27527 "afterupload" : true,
27530 * prepare the form data
27531 * @param {Roo.bootstrap.DocumentManager} this
27532 * @param {Object} formData
27537 * Fire when remove the file
27538 * @param {Roo.bootstrap.DocumentManager} this
27539 * @param {Object} file
27544 * Fire after refresh the file
27545 * @param {Roo.bootstrap.DocumentManager} this
27550 * Fire after click the image
27551 * @param {Roo.bootstrap.DocumentManager} this
27552 * @param {Object} file
27557 * Fire when upload a image and editable set to true
27558 * @param {Roo.bootstrap.DocumentManager} this
27559 * @param {Object} file
27563 * @event beforeselectfile
27564 * Fire before select file
27565 * @param {Roo.bootstrap.DocumentManager} this
27567 "beforeselectfile" : true,
27570 * Fire before process file
27571 * @param {Roo.bootstrap.DocumentManager} this
27572 * @param {Object} file
27579 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component, {
27588 paramName : 'imageUpload',
27589 toolTipName : 'filename',
27592 labelAlign : 'left',
27597 getAutoCreate : function()
27599 var managerWidget = {
27601 cls : 'roo-document-manager',
27605 cls : 'roo-document-manager-selector',
27610 cls : 'roo-document-manager-uploader',
27614 cls : 'roo-document-manager-upload-btn',
27615 html : '<i class="fa fa-plus"></i>'
27626 cls : 'column col-md-12',
27631 if(this.fieldLabel.length){
27636 cls : 'column col-md-12',
27637 html : this.fieldLabel
27641 cls : 'column col-md-12',
27646 if(this.labelAlign == 'left'){
27650 cls : 'column col-md-' + this.labelWidth,
27651 html : this.fieldLabel
27655 cls : 'column col-md-' + (12 - this.labelWidth),
27665 cls : 'row clearfix',
27673 initEvents : function()
27675 this.managerEl = this.el.select('.roo-document-manager', true).first();
27676 this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27678 this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
27679 this.selectorEl.hide();
27682 this.selectorEl.attr('multiple', 'multiple');
27685 this.selectorEl.on('change', this.onFileSelected, this);
27687 this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
27688 this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27690 this.uploader.on('click', this.onUploaderClick, this);
27692 this.renderProgressDialog();
27696 window.addEventListener("resize", function() { _this.refresh(); } );
27698 this.fireEvent('initial', this);
27701 renderProgressDialog : function()
27705 this.progressDialog = new Roo.bootstrap.Modal({
27706 cls : 'roo-document-manager-progress-dialog',
27707 allow_close : false,
27717 btnclick : function() {
27718 _this.uploadCancel();
27724 this.progressDialog.render(Roo.get(document.body));
27726 this.progress = new Roo.bootstrap.Progress({
27727 cls : 'roo-document-manager-progress',
27732 this.progress.render(this.progressDialog.getChildContainer());
27734 this.progressBar = new Roo.bootstrap.ProgressBar({
27735 cls : 'roo-document-manager-progress-bar',
27738 aria_valuemax : 12,
27742 this.progressBar.render(this.progress.getChildContainer());
27745 onUploaderClick : function(e)
27747 e.preventDefault();
27749 if(this.fireEvent('beforeselectfile', this) != false){
27750 this.selectorEl.dom.click();
27755 onFileSelected : function(e)
27757 e.preventDefault();
27759 if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
27763 Roo.each(this.selectorEl.dom.files, function(file){
27764 if(this.fireEvent('inspect', this, file) != false){
27765 this.files.push(file);
27775 this.selectorEl.dom.value = '';
27777 if(!this.files.length){
27781 if(this.boxes > 0 && this.files.length > this.boxes){
27782 this.files = this.files.slice(0, this.boxes);
27785 this.uploader.show();
27787 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27788 this.uploader.hide();
27797 Roo.each(this.files, function(file){
27799 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27800 var f = this.renderPreview(file);
27805 if(file.type.indexOf('image') != -1){
27806 this.delegates.push(
27808 _this.process(file);
27809 }).createDelegate(this)
27817 _this.process(file);
27818 }).createDelegate(this)
27823 this.files = files;
27825 this.delegates = this.delegates.concat(docs);
27827 if(!this.delegates.length){
27832 this.progressBar.aria_valuemax = this.delegates.length;
27839 arrange : function()
27841 if(!this.delegates.length){
27842 this.progressDialog.hide();
27847 var delegate = this.delegates.shift();
27849 this.progressDialog.show();
27851 this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27853 this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27858 refresh : function()
27860 this.uploader.show();
27862 if(this.boxes > 0 && this.files.length > this.boxes - 1){
27863 this.uploader.hide();
27866 Roo.isTouch ? this.closable(false) : this.closable(true);
27868 this.fireEvent('refresh', this);
27871 onRemove : function(e, el, o)
27873 e.preventDefault();
27875 this.fireEvent('remove', this, o);
27879 remove : function(o)
27883 Roo.each(this.files, function(file){
27884 if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27893 this.files = files;
27900 Roo.each(this.files, function(file){
27905 file.target.remove();
27914 onClick : function(e, el, o)
27916 e.preventDefault();
27918 this.fireEvent('click', this, o);
27922 closable : function(closable)
27924 Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27926 el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27938 xhrOnLoad : function(xhr)
27940 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27944 if (xhr.readyState !== 4) {
27946 this.fireEvent('exception', this, xhr);
27950 var response = Roo.decode(xhr.responseText);
27952 if(!response.success){
27954 this.fireEvent('exception', this, xhr);
27958 var file = this.renderPreview(response.data);
27960 this.files.push(file);
27964 this.fireEvent('afterupload', this, xhr);
27968 xhrOnError : function(xhr)
27970 Roo.log('xhr on error');
27972 var response = Roo.decode(xhr.responseText);
27979 process : function(file)
27981 if(this.fireEvent('process', this, file) !== false){
27982 if(this.editable && file.type.indexOf('image') != -1){
27983 this.fireEvent('edit', this, file);
27987 this.uploadStart(file, false);
27994 uploadStart : function(file, crop)
27996 this.xhr = new XMLHttpRequest();
27998 if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28003 file.xhr = this.xhr;
28005 this.managerEl.createChild({
28007 cls : 'roo-document-manager-loading',
28011 tooltip : file.name,
28012 cls : 'roo-document-manager-thumb',
28013 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28019 this.xhr.open(this.method, this.url, true);
28022 "Accept": "application/json",
28023 "Cache-Control": "no-cache",
28024 "X-Requested-With": "XMLHttpRequest"
28027 for (var headerName in headers) {
28028 var headerValue = headers[headerName];
28030 this.xhr.setRequestHeader(headerName, headerValue);
28036 this.xhr.onload = function()
28038 _this.xhrOnLoad(_this.xhr);
28041 this.xhr.onerror = function()
28043 _this.xhrOnError(_this.xhr);
28046 var formData = new FormData();
28048 formData.append('returnHTML', 'NO');
28051 formData.append('crop', crop);
28054 formData.append(this.paramName, file, file.name);
28061 if(this.fireEvent('prepare', this, formData, options) != false){
28063 if(options.manually){
28067 this.xhr.send(formData);
28071 this.uploadCancel();
28074 uploadCancel : function()
28080 this.delegates = [];
28082 Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28089 renderPreview : function(file)
28091 if(typeof(file.target) != 'undefined' && file.target){
28095 var previewEl = this.managerEl.createChild({
28097 cls : 'roo-document-manager-preview',
28101 tooltip : file[this.toolTipName],
28102 cls : 'roo-document-manager-thumb',
28103 html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28108 html : '<i class="fa fa-times-circle"></i>'
28113 var close = previewEl.select('button.close', true).first();
28115 close.on('click', this.onRemove, this, file);
28117 file.target = previewEl;
28119 var image = previewEl.select('img', true).first();
28123 image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28125 image.on('click', this.onClick, this, file);
28131 onPreviewLoad : function(file, image)
28133 if(typeof(file.target) == 'undefined' || !file.target){
28137 var width = image.dom.naturalWidth || image.dom.width;
28138 var height = image.dom.naturalHeight || image.dom.height;
28140 if(width > height){
28141 file.target.addClass('wide');
28145 file.target.addClass('tall');
28150 uploadFromSource : function(file, crop)
28152 this.xhr = new XMLHttpRequest();
28154 this.managerEl.createChild({
28156 cls : 'roo-document-manager-loading',
28160 tooltip : file.name,
28161 cls : 'roo-document-manager-thumb',
28162 html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28168 this.xhr.open(this.method, this.url, true);
28171 "Accept": "application/json",
28172 "Cache-Control": "no-cache",
28173 "X-Requested-With": "XMLHttpRequest"
28176 for (var headerName in headers) {
28177 var headerValue = headers[headerName];
28179 this.xhr.setRequestHeader(headerName, headerValue);
28185 this.xhr.onload = function()
28187 _this.xhrOnLoad(_this.xhr);
28190 this.xhr.onerror = function()
28192 _this.xhrOnError(_this.xhr);
28195 var formData = new FormData();
28197 formData.append('returnHTML', 'NO');
28199 formData.append('crop', crop);
28201 if(typeof(file.filename) != 'undefined'){
28202 formData.append('filename', file.filename);
28205 if(typeof(file.mimetype) != 'undefined'){
28206 formData.append('mimetype', file.mimetype);
28209 if(this.fireEvent('prepare', this, formData) != false){
28210 this.xhr.send(formData);
28220 * @class Roo.bootstrap.DocumentViewer
28221 * @extends Roo.bootstrap.Component
28222 * Bootstrap DocumentViewer class
28223 * @cfg {Boolean} showDownload (true|false) show download button (default true)
28224 * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28227 * Create a new DocumentViewer
28228 * @param {Object} config The config object
28231 Roo.bootstrap.DocumentViewer = function(config){
28232 Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28237 * Fire after initEvent
28238 * @param {Roo.bootstrap.DocumentViewer} this
28244 * @param {Roo.bootstrap.DocumentViewer} this
28249 * Fire after download button
28250 * @param {Roo.bootstrap.DocumentViewer} this
28255 * Fire after trash button
28256 * @param {Roo.bootstrap.DocumentViewer} this
28263 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component, {
28265 showDownload : true,
28269 getAutoCreate : function()
28273 cls : 'roo-document-viewer',
28277 cls : 'roo-document-viewer-body',
28281 cls : 'roo-document-viewer-thumb',
28285 cls : 'roo-document-viewer-image'
28293 cls : 'roo-document-viewer-footer',
28296 cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
28300 cls : 'btn-group roo-document-viewer-download',
28304 cls : 'btn btn-default',
28305 html : '<i class="fa fa-download"></i>'
28311 cls : 'btn-group roo-document-viewer-trash',
28315 cls : 'btn btn-default',
28316 html : '<i class="fa fa-trash"></i>'
28329 initEvents : function()
28331 this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
28332 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
28334 this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
28335 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
28337 this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
28338 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
28340 this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
28341 this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
28343 this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
28344 this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
28346 this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
28347 this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
28349 this.bodyEl.on('click', this.onClick, this);
28350 this.downloadBtn.on('click', this.onDownload, this);
28351 this.trashBtn.on('click', this.onTrash, this);
28353 this.downloadBtn.hide();
28354 this.trashBtn.hide();
28356 if(this.showDownload){
28357 this.downloadBtn.show();
28360 if(this.showTrash){
28361 this.trashBtn.show();
28364 if(!this.showDownload && !this.showTrash) {
28365 this.footerEl.hide();
28370 initial : function()
28372 this.fireEvent('initial', this);
28376 onClick : function(e)
28378 e.preventDefault();
28380 this.fireEvent('click', this);
28383 onDownload : function(e)
28385 e.preventDefault();
28387 this.fireEvent('download', this);
28390 onTrash : function(e)
28392 e.preventDefault();
28394 this.fireEvent('trash', this);
28406 * @class Roo.bootstrap.NavProgressBar
28407 * @extends Roo.bootstrap.Component
28408 * Bootstrap NavProgressBar class
28411 * Create a new nav progress bar
28412 * @param {Object} config The config object
28415 Roo.bootstrap.NavProgressBar = function(config){
28416 Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
28418 this.bullets = this.bullets || [];
28420 // Roo.bootstrap.NavProgressBar.register(this);
28424 * Fires when the active item changes
28425 * @param {Roo.bootstrap.NavProgressBar} this
28426 * @param {Roo.bootstrap.NavProgressItem} selected The item selected
28427 * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item
28434 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component, {
28439 getAutoCreate : function()
28441 var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
28445 cls : 'roo-navigation-bar-group',
28449 cls : 'roo-navigation-top-bar'
28453 cls : 'roo-navigation-bullets-bar',
28457 cls : 'roo-navigation-bar'
28464 cls : 'roo-navigation-bottom-bar'
28474 initEvents: function()
28479 onRender : function(ct, position)
28481 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28483 if(this.bullets.length){
28484 Roo.each(this.bullets, function(b){
28493 addItem : function(cfg)
28495 var item = new Roo.bootstrap.NavProgressItem(cfg);
28497 item.parentId = this.id;
28498 item.render(this.el.select('.roo-navigation-bar', true).first(), null);
28501 var top = new Roo.bootstrap.Element({
28503 cls : 'roo-navigation-bar-text'
28506 var bottom = new Roo.bootstrap.Element({
28508 cls : 'roo-navigation-bar-text'
28511 top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
28512 bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
28514 var topText = new Roo.bootstrap.Element({
28516 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
28519 var bottomText = new Roo.bootstrap.Element({
28521 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
28524 topText.onRender(top.el, null);
28525 bottomText.onRender(bottom.el, null);
28528 item.bottomEl = bottom;
28531 this.barItems.push(item);
28536 getActive : function()
28538 var active = false;
28540 Roo.each(this.barItems, function(v){
28542 if (!v.isActive()) {
28554 setActiveItem : function(item)
28558 Roo.each(this.barItems, function(v){
28559 if (v.rid == item.rid) {
28563 if (v.isActive()) {
28564 v.setActive(false);
28569 item.setActive(true);
28571 this.fireEvent('changed', this, item, prev);
28574 getBarItem: function(rid)
28578 Roo.each(this.barItems, function(e) {
28579 if (e.rid != rid) {
28590 indexOfItem : function(item)
28594 Roo.each(this.barItems, function(v, i){
28596 if (v.rid != item.rid) {
28607 setActiveNext : function()
28609 var i = this.indexOfItem(this.getActive());
28611 if (i > this.barItems.length) {
28615 this.setActiveItem(this.barItems[i+1]);
28618 setActivePrev : function()
28620 var i = this.indexOfItem(this.getActive());
28626 this.setActiveItem(this.barItems[i-1]);
28629 format : function()
28631 if(!this.barItems.length){
28635 var width = 100 / this.barItems.length;
28637 Roo.each(this.barItems, function(i){
28638 i.el.setStyle('width', width + '%');
28639 i.topEl.el.setStyle('width', width + '%');
28640 i.bottomEl.el.setStyle('width', width + '%');
28649 * Nav Progress Item
28654 * @class Roo.bootstrap.NavProgressItem
28655 * @extends Roo.bootstrap.Component
28656 * Bootstrap NavProgressItem class
28657 * @cfg {String} rid the reference id
28658 * @cfg {Boolean} active (true|false) Is item active default false
28659 * @cfg {Boolean} disabled (true|false) Is item active default false
28660 * @cfg {String} html
28661 * @cfg {String} position (top|bottom) text position default bottom
28662 * @cfg {String} icon show icon instead of number
28665 * Create a new NavProgressItem
28666 * @param {Object} config The config object
28668 Roo.bootstrap.NavProgressItem = function(config){
28669 Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
28674 * The raw click event for the entire grid.
28675 * @param {Roo.bootstrap.NavProgressItem} this
28676 * @param {Roo.EventObject} e
28683 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component, {
28689 position : 'bottom',
28692 getAutoCreate : function()
28694 var iconCls = 'roo-navigation-bar-item-icon';
28696 iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
28700 cls: 'roo-navigation-bar-item',
28710 cfg.cls += ' active';
28713 cfg.cls += ' disabled';
28719 disable : function()
28721 this.setDisabled(true);
28724 enable : function()
28726 this.setDisabled(false);
28729 initEvents: function()
28731 this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
28733 this.iconEl.on('click', this.onClick, this);
28736 onClick : function(e)
28738 e.preventDefault();
28744 if(this.fireEvent('click', this, e) === false){
28748 this.parent().setActiveItem(this);
28751 isActive: function ()
28753 return this.active;
28756 setActive : function(state)
28758 if(this.active == state){
28762 this.active = state;
28765 this.el.addClass('active');
28769 this.el.removeClass('active');
28774 setDisabled : function(state)
28776 if(this.disabled == state){
28780 this.disabled = state;
28783 this.el.addClass('disabled');
28787 this.el.removeClass('disabled');
28790 tooltipEl : function()
28792 return this.el.select('.roo-navigation-bar-item-icon', true).first();;
28805 * @class Roo.bootstrap.FieldLabel
28806 * @extends Roo.bootstrap.Component
28807 * Bootstrap FieldLabel class
28808 * @cfg {String} html contents of the element
28809 * @cfg {String} tag tag of the element default label
28810 * @cfg {String} cls class of the element
28811 * @cfg {String} target label target
28812 * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
28813 * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
28814 * @cfg {String} validClass default "text-success fa fa-lg fa-check"
28815 * @cfg {String} iconTooltip default "This field is required"
28818 * Create a new FieldLabel
28819 * @param {Object} config The config object
28822 Roo.bootstrap.FieldLabel = function(config){
28823 Roo.bootstrap.Element.superclass.constructor.call(this, config);
28828 * Fires after the field has been marked as invalid.
28829 * @param {Roo.form.FieldLabel} this
28830 * @param {String} msg The validation message
28835 * Fires after the field has been validated with no errors.
28836 * @param {Roo.form.FieldLabel} this
28842 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component, {
28849 invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28850 validClass : 'text-success fa fa-lg fa-check',
28851 iconTooltip : 'This field is required',
28853 getAutoCreate : function(){
28857 cls : 'roo-bootstrap-field-label ' + this.cls,
28863 tooltip : this.iconTooltip
28875 initEvents: function()
28877 Roo.bootstrap.Element.superclass.initEvents.call(this);
28879 this.iconEl = this.el.select('i', true).first();
28881 this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28883 Roo.bootstrap.FieldLabel.register(this);
28887 * Mark this field as valid
28889 markValid : function()
28891 this.iconEl.show();
28893 this.iconEl.removeClass(this.invalidClass);
28895 this.iconEl.addClass(this.validClass);
28897 this.fireEvent('valid', this);
28901 * Mark this field as invalid
28902 * @param {String} msg The validation message
28904 markInvalid : function(msg)
28906 this.iconEl.show();
28908 this.iconEl.removeClass(this.validClass);
28910 this.iconEl.addClass(this.invalidClass);
28912 this.fireEvent('invalid', this, msg);
28918 Roo.apply(Roo.bootstrap.FieldLabel, {
28923 * register a FieldLabel Group
28924 * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28926 register : function(label)
28928 if(this.groups.hasOwnProperty(label.target)){
28932 this.groups[label.target] = label;
28936 * fetch a FieldLabel Group based on the target
28937 * @param {string} target
28938 * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28940 get: function(target) {
28941 if (typeof(this.groups[target]) == 'undefined') {
28945 return this.groups[target] ;
28954 * page DateSplitField.
28960 * @class Roo.bootstrap.DateSplitField
28961 * @extends Roo.bootstrap.Component
28962 * Bootstrap DateSplitField class
28963 * @cfg {string} fieldLabel - the label associated
28964 * @cfg {Number} labelWidth set the width of label (0-12)
28965 * @cfg {String} labelAlign (top|left)
28966 * @cfg {Boolean} dayAllowBlank (true|false) default false
28967 * @cfg {Boolean} monthAllowBlank (true|false) default false
28968 * @cfg {Boolean} yearAllowBlank (true|false) default false
28969 * @cfg {string} dayPlaceholder
28970 * @cfg {string} monthPlaceholder
28971 * @cfg {string} yearPlaceholder
28972 * @cfg {string} dayFormat default 'd'
28973 * @cfg {string} monthFormat default 'm'
28974 * @cfg {string} yearFormat default 'Y'
28978 * Create a new DateSplitField
28979 * @param {Object} config The config object
28982 Roo.bootstrap.DateSplitField = function(config){
28983 Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28989 * getting the data of years
28990 * @param {Roo.bootstrap.DateSplitField} this
28991 * @param {Object} years
28996 * getting the data of days
28997 * @param {Roo.bootstrap.DateSplitField} this
28998 * @param {Object} days
29003 * Fires after the field has been marked as invalid.
29004 * @param {Roo.form.Field} this
29005 * @param {String} msg The validation message
29010 * Fires after the field has been validated with no errors.
29011 * @param {Roo.form.Field} this
29017 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component, {
29020 labelAlign : 'top',
29022 dayAllowBlank : false,
29023 monthAllowBlank : false,
29024 yearAllowBlank : false,
29025 dayPlaceholder : '',
29026 monthPlaceholder : '',
29027 yearPlaceholder : '',
29031 isFormField : true,
29033 getAutoCreate : function()
29037 cls : 'row roo-date-split-field-group',
29042 cls : 'form-hidden-field roo-date-split-field-group-value',
29048 if(this.fieldLabel){
29051 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29055 html : this.fieldLabel
29061 Roo.each(['day', 'month', 'year'], function(t){
29064 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
29071 inputEl: function ()
29073 return this.el.select('.roo-date-split-field-group-value', true).first();
29076 onRender : function(ct, position)
29080 Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29082 this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29084 this.dayField = new Roo.bootstrap.ComboBox({
29085 allowBlank : this.dayAllowBlank,
29086 alwaysQuery : true,
29087 displayField : 'value',
29090 forceSelection : true,
29092 placeholder : this.dayPlaceholder,
29093 selectOnFocus : true,
29094 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29095 triggerAction : 'all',
29097 valueField : 'value',
29098 store : new Roo.data.SimpleStore({
29099 data : (function() {
29101 _this.fireEvent('days', _this, days);
29104 fields : [ 'value' ]
29107 select : function (_self, record, index)
29109 _this.setValue(_this.getValue());
29114 this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29116 this.monthField = new Roo.bootstrap.MonthField({
29117 after : '<i class=\"fa fa-calendar\"></i>',
29118 allowBlank : this.monthAllowBlank,
29119 placeholder : this.monthPlaceholder,
29122 render : function (_self)
29124 this.el.select('span.input-group-addon', true).first().on('click', function(e){
29125 e.preventDefault();
29129 select : function (_self, oldvalue, newvalue)
29131 _this.setValue(_this.getValue());
29136 this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29138 this.yearField = new Roo.bootstrap.ComboBox({
29139 allowBlank : this.yearAllowBlank,
29140 alwaysQuery : true,
29141 displayField : 'value',
29144 forceSelection : true,
29146 placeholder : this.yearPlaceholder,
29147 selectOnFocus : true,
29148 tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29149 triggerAction : 'all',
29151 valueField : 'value',
29152 store : new Roo.data.SimpleStore({
29153 data : (function() {
29155 _this.fireEvent('years', _this, years);
29158 fields : [ 'value' ]
29161 select : function (_self, record, index)
29163 _this.setValue(_this.getValue());
29168 this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29171 setValue : function(v, format)
29173 this.inputEl.dom.value = v;
29175 var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29177 var d = Date.parseDate(v, f);
29184 this.setDay(d.format(this.dayFormat));
29185 this.setMonth(d.format(this.monthFormat));
29186 this.setYear(d.format(this.yearFormat));
29193 setDay : function(v)
29195 this.dayField.setValue(v);
29196 this.inputEl.dom.value = this.getValue();
29201 setMonth : function(v)
29203 this.monthField.setValue(v, true);
29204 this.inputEl.dom.value = this.getValue();
29209 setYear : function(v)
29211 this.yearField.setValue(v);
29212 this.inputEl.dom.value = this.getValue();
29217 getDay : function()
29219 return this.dayField.getValue();
29222 getMonth : function()
29224 return this.monthField.getValue();
29227 getYear : function()
29229 return this.yearField.getValue();
29232 getValue : function()
29234 var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
29236 var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
29246 this.inputEl.dom.value = '';
29251 validate : function()
29253 var d = this.dayField.validate();
29254 var m = this.monthField.validate();
29255 var y = this.yearField.validate();
29260 (!this.dayAllowBlank && !d) ||
29261 (!this.monthAllowBlank && !m) ||
29262 (!this.yearAllowBlank && !y)
29267 if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
29276 this.markInvalid();
29281 markValid : function()
29284 var label = this.el.select('label', true).first();
29285 var icon = this.el.select('i.fa-star', true).first();
29291 this.fireEvent('valid', this);
29295 * Mark this field as invalid
29296 * @param {String} msg The validation message
29298 markInvalid : function(msg)
29301 var label = this.el.select('label', true).first();
29302 var icon = this.el.select('i.fa-star', true).first();
29304 if(label && !icon){
29305 this.el.select('.roo-date-split-field-label', true).createChild({
29307 cls : 'text-danger fa fa-lg fa-star',
29308 tooltip : 'This field is required',
29309 style : 'margin-right:5px;'
29313 this.fireEvent('invalid', this, msg);
29316 clearInvalid : function()
29318 var label = this.el.select('label', true).first();
29319 var icon = this.el.select('i.fa-star', true).first();
29325 this.fireEvent('valid', this);
29328 getName: function()
29338 * http://masonry.desandro.com
29340 * The idea is to render all the bricks based on vertical width...
29342 * The original code extends 'outlayer' - we might need to use that....
29348 * @class Roo.bootstrap.LayoutMasonry
29349 * @extends Roo.bootstrap.Component
29350 * Bootstrap Layout Masonry class
29353 * Create a new Element
29354 * @param {Object} config The config object
29357 Roo.bootstrap.LayoutMasonry = function(config){
29358 Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
29364 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component, {
29367 * @cfg {Boolean} isLayoutInstant = no animation?
29369 isLayoutInstant : false, // needed?
29372 * @cfg {Number} boxWidth width of the columns
29377 * @cfg {Number} boxHeight - 0 for square, or fix it at a certian height
29382 * @cfg {Number} padWidth padding below box..
29387 * @cfg {Number} gutter gutter width..
29392 * @cfg {Number} maxCols maximum number of columns
29398 * @cfg {Boolean} isAutoInitial defalut true
29400 isAutoInitial : true,
29405 * @cfg {Boolean} isHorizontal defalut false
29407 isHorizontal : false,
29409 currentSize : null,
29415 bricks: null, //CompositeElement
29419 _isLayoutInited : false,
29421 // isAlternative : false, // only use for vertical layout...
29424 * @cfg {Number} alternativePadWidth padding below box..
29426 alternativePadWidth : 50,
29428 getAutoCreate : function(){
29432 cls: 'blog-masonary-wrapper ' + this.cls,
29434 cls : 'mas-boxes masonary'
29441 getChildContainer: function( )
29443 if (this.boxesEl) {
29444 return this.boxesEl;
29447 this.boxesEl = this.el.select('.mas-boxes').first();
29449 return this.boxesEl;
29453 initEvents : function()
29457 if(this.isAutoInitial){
29458 Roo.log('hook children rendered');
29459 this.on('childrenrendered', function() {
29460 Roo.log('children rendered');
29466 initial : function()
29468 this.currentSize = this.el.getBox(true);
29470 Roo.EventManager.onWindowResize(this.resize, this);
29472 if(!this.isAutoInitial){
29480 //this.layout.defer(500,this);
29484 resize : function()
29488 var cs = this.el.getBox(true);
29490 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29491 Roo.log("no change in with or X");
29495 this.currentSize = cs;
29501 layout : function()
29503 this._resetLayout();
29505 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29507 this.layoutItems( isInstant );
29509 this._isLayoutInited = true;
29513 _resetLayout : function()
29515 if(this.isHorizontal){
29516 this.horizontalMeasureColumns();
29520 this.verticalMeasureColumns();
29524 verticalMeasureColumns : function()
29526 this.getContainerWidth();
29528 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29529 // this.colWidth = Math.floor(this.containerWidth * 0.8);
29533 var boxWidth = this.boxWidth + this.padWidth;
29535 if(this.containerWidth < this.boxWidth){
29536 boxWidth = this.containerWidth
29539 var containerWidth = this.containerWidth;
29541 var cols = Math.floor(containerWidth / boxWidth);
29543 this.cols = Math.max( cols, 1 );
29545 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
29547 var totalBoxWidth = this.cols * boxWidth - this.padWidth;
29549 var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
29551 this.colWidth = boxWidth + avail - this.padWidth;
29553 this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
29554 this.unitHeight = this.boxHeight > 0 ? this.boxHeight : this.unitWidth;
29557 horizontalMeasureColumns : function()
29559 this.getContainerWidth();
29561 var boxWidth = this.boxWidth;
29563 if(this.containerWidth < boxWidth){
29564 boxWidth = this.containerWidth;
29567 this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
29569 this.el.setHeight(boxWidth);
29573 getContainerWidth : function()
29575 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
29578 layoutItems : function( isInstant )
29580 var items = Roo.apply([], this.bricks);
29582 if(this.isHorizontal){
29583 this._horizontalLayoutItems( items , isInstant );
29587 // if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
29588 // this._verticalAlternativeLayoutItems( items , isInstant );
29592 this._verticalLayoutItems( items , isInstant );
29596 _verticalLayoutItems : function ( items , isInstant)
29598 if ( !items || !items.length ) {
29603 ['xs', 'xs', 'xs', 'tall'],
29604 ['xs', 'xs', 'tall'],
29605 ['xs', 'xs', 'sm'],
29606 ['xs', 'xs', 'xs'],
29612 ['sm', 'xs', 'xs'],
29616 ['tall', 'xs', 'xs', 'xs'],
29617 ['tall', 'xs', 'xs'],
29629 Roo.each(items, function(item, k){
29631 switch (item.size) {
29632 // these layouts take up a full box,
29643 boxes.push([item]);
29666 var filterPattern = function(box, length)
29674 var pattern = box.slice(0, length);
29678 Roo.each(pattern, function(i){
29679 format.push(i.size);
29682 Roo.each(standard, function(s){
29684 if(String(s) != String(format)){
29693 if(!match && length == 1){
29698 filterPattern(box, length - 1);
29702 queue.push(pattern);
29704 box = box.slice(length, box.length);
29706 filterPattern(box, 4);
29712 Roo.each(boxes, function(box, k){
29718 if(box.length == 1){
29723 filterPattern(box, 4);
29727 this._processVerticalLayoutQueue( queue, isInstant );
29731 // _verticalAlternativeLayoutItems : function( items , isInstant )
29733 // if ( !items || !items.length ) {
29737 // this._processVerticalAlternativeLayoutQueue( items, isInstant );
29741 _horizontalLayoutItems : function ( items , isInstant)
29743 if ( !items || !items.length || items.length < 3) {
29749 var eItems = items.slice(0, 3);
29751 items = items.slice(3, items.length);
29754 ['xs', 'xs', 'xs', 'wide'],
29755 ['xs', 'xs', 'wide'],
29756 ['xs', 'xs', 'sm'],
29757 ['xs', 'xs', 'xs'],
29763 ['sm', 'xs', 'xs'],
29767 ['wide', 'xs', 'xs', 'xs'],
29768 ['wide', 'xs', 'xs'],
29781 Roo.each(items, function(item, k){
29783 switch (item.size) {
29794 boxes.push([item]);
29818 var filterPattern = function(box, length)
29826 var pattern = box.slice(0, length);
29830 Roo.each(pattern, function(i){
29831 format.push(i.size);
29834 Roo.each(standard, function(s){
29836 if(String(s) != String(format)){
29845 if(!match && length == 1){
29850 filterPattern(box, length - 1);
29854 queue.push(pattern);
29856 box = box.slice(length, box.length);
29858 filterPattern(box, 4);
29864 Roo.each(boxes, function(box, k){
29870 if(box.length == 1){
29875 filterPattern(box, 4);
29882 var pos = this.el.getBox(true);
29886 var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29888 var hit_end = false;
29890 Roo.each(queue, function(box){
29894 Roo.each(box, function(b){
29896 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29906 Roo.each(box, function(b){
29908 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29911 mx = Math.max(mx, b.x);
29915 maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29919 Roo.each(box, function(b){
29921 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29935 this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29938 /** Sets position of item in DOM
29939 * @param {Element} item
29940 * @param {Number} x - horizontal position
29941 * @param {Number} y - vertical position
29942 * @param {Boolean} isInstant - disables transitions
29944 _processVerticalLayoutQueue : function( queue, isInstant )
29946 var pos = this.el.getBox(true);
29951 for (var i = 0; i < this.cols; i++){
29955 Roo.each(queue, function(box, k){
29957 var col = k % this.cols;
29959 Roo.each(box, function(b,kk){
29961 b.el.position('absolute');
29963 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29964 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29966 if(b.size == 'md-left' || b.size == 'md-right'){
29967 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29968 height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29971 b.el.setWidth(width);
29972 b.el.setHeight(height);
29974 b.el.select('iframe',true).setSize(width,height);
29978 for (var i = 0; i < this.cols; i++){
29980 if(maxY[i] < maxY[col]){
29985 col = Math.min(col, i);
29989 x = pos.x + col * (this.colWidth + this.padWidth);
29993 var positions = [];
29995 switch (box.length){
29997 positions = this.getVerticalOneBoxColPositions(x, y, box);
30000 positions = this.getVerticalTwoBoxColPositions(x, y, box);
30003 positions = this.getVerticalThreeBoxColPositions(x, y, box);
30006 positions = this.getVerticalFourBoxColPositions(x, y, box);
30012 Roo.each(box, function(b,kk){
30014 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30016 var sz = b.el.getSize();
30018 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30026 for (var i = 0; i < this.cols; i++){
30027 mY = Math.max(mY, maxY[i]);
30030 this.el.setHeight(mY - pos.y);
30034 // _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30036 // var pos = this.el.getBox(true);
30039 // var maxX = pos.right;
30041 // var maxHeight = 0;
30043 // Roo.each(items, function(item, k){
30047 // item.el.position('absolute');
30049 // var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30051 // item.el.setWidth(width);
30053 // var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30055 // item.el.setHeight(height);
30058 // item.el.setXY([x, y], isInstant ? false : true);
30060 // item.el.setXY([maxX - width, y], isInstant ? false : true);
30063 // y = y + height + this.alternativePadWidth;
30065 // maxHeight = maxHeight + height + this.alternativePadWidth;
30069 // this.el.setHeight(maxHeight);
30073 _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30075 var pos = this.el.getBox(true);
30080 var maxX = pos.right;
30082 this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30084 var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30086 Roo.each(queue, function(box, k){
30088 Roo.each(box, function(b, kk){
30090 b.el.position('absolute');
30092 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30093 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30095 if(b.size == 'md-left' || b.size == 'md-right'){
30096 width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30097 height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30100 b.el.setWidth(width);
30101 b.el.setHeight(height);
30109 var positions = [];
30111 switch (box.length){
30113 positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30116 positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30119 positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30122 positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30128 Roo.each(box, function(b,kk){
30130 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30132 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30140 _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30142 Roo.each(eItems, function(b,k){
30144 b.size = (k == 0) ? 'sm' : 'xs';
30145 b.x = (k == 0) ? 2 : 1;
30146 b.y = (k == 0) ? 2 : 1;
30148 b.el.position('absolute');
30150 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30152 b.el.setWidth(width);
30154 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30156 b.el.setHeight(height);
30160 var positions = [];
30163 x : maxX - this.unitWidth * 2 - this.gutter,
30168 x : maxX - this.unitWidth,
30169 y : minY + (this.unitWidth + this.gutter) * 2
30173 x : maxX - this.unitWidth * 3 - this.gutter * 2,
30177 Roo.each(eItems, function(b,k){
30179 b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
30185 getVerticalOneBoxColPositions : function(x, y, box)
30189 var rand = Math.floor(Math.random() * ((4 - box[0].x)));
30191 if(box[0].size == 'md-left'){
30195 if(box[0].size == 'md-right'){
30200 x : x + (this.unitWidth + this.gutter) * rand,
30207 getVerticalTwoBoxColPositions : function(x, y, box)
30211 if(box[0].size == 'xs'){
30215 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
30219 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
30233 x : x + (this.unitWidth + this.gutter) * 2,
30234 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
30241 getVerticalThreeBoxColPositions : function(x, y, box)
30245 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30253 x : x + (this.unitWidth + this.gutter) * 1,
30258 x : x + (this.unitWidth + this.gutter) * 2,
30266 if(box[0].size == 'xs' && box[1].size == 'xs'){
30275 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
30279 x : x + (this.unitWidth + this.gutter) * 1,
30293 x : x + (this.unitWidth + this.gutter) * 2,
30298 x : x + (this.unitWidth + this.gutter) * 2,
30299 y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
30306 getVerticalFourBoxColPositions : function(x, y, box)
30310 if(box[0].size == 'xs'){
30319 y : y + (this.unitHeight + this.gutter) * 1
30324 y : y + (this.unitHeight + this.gutter) * 2
30328 x : x + (this.unitWidth + this.gutter) * 1,
30342 x : x + (this.unitWidth + this.gutter) * 2,
30347 x : x + (this.unitHeightunitWidth + this.gutter) * 2,
30348 y : y + (this.unitHeight + this.gutter) * 1
30352 x : x + (this.unitWidth + this.gutter) * 2,
30353 y : y + (this.unitWidth + this.gutter) * 2
30360 getHorizontalOneBoxColPositions : function(maxX, minY, box)
30364 if(box[0].size == 'md-left'){
30366 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30373 if(box[0].size == 'md-right'){
30375 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
30376 y : minY + (this.unitWidth + this.gutter) * 1
30382 var rand = Math.floor(Math.random() * (4 - box[0].y));
30385 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30386 y : minY + (this.unitWidth + this.gutter) * rand
30393 getHorizontalTwoBoxColPositions : function(maxX, minY, box)
30397 if(box[0].size == 'xs'){
30400 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30405 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30406 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
30414 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30419 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30420 y : minY + (this.unitWidth + this.gutter) * 2
30427 getHorizontalThreeBoxColPositions : function(maxX, minY, box)
30431 if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
30434 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30439 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30440 y : minY + (this.unitWidth + this.gutter) * 1
30444 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30445 y : minY + (this.unitWidth + this.gutter) * 2
30452 if(box[0].size == 'xs' && box[1].size == 'xs'){
30455 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30460 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30465 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30466 y : minY + (this.unitWidth + this.gutter) * 1
30474 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30479 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30480 y : minY + (this.unitWidth + this.gutter) * 2
30484 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30485 y : minY + (this.unitWidth + this.gutter) * 2
30492 getHorizontalFourBoxColPositions : function(maxX, minY, box)
30496 if(box[0].size == 'xs'){
30499 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30504 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30509 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),
30514 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
30515 y : minY + (this.unitWidth + this.gutter) * 1
30523 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
30528 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
30529 y : minY + (this.unitWidth + this.gutter) * 2
30533 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
30534 y : minY + (this.unitWidth + this.gutter) * 2
30538 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),
30539 y : minY + (this.unitWidth + this.gutter) * 2
30553 * http://masonry.desandro.com
30555 * The idea is to render all the bricks based on vertical width...
30557 * The original code extends 'outlayer' - we might need to use that....
30563 * @class Roo.bootstrap.LayoutMasonryAuto
30564 * @extends Roo.bootstrap.Component
30565 * Bootstrap Layout Masonry class
30568 * Create a new Element
30569 * @param {Object} config The config object
30572 Roo.bootstrap.LayoutMasonryAuto = function(config){
30573 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
30576 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30579 * @cfg {Boolean} isFitWidth - resize the width..
30581 isFitWidth : false, // options..
30583 * @cfg {Boolean} isOriginLeft = left align?
30585 isOriginLeft : true,
30587 * @cfg {Boolean} isOriginTop = top align?
30589 isOriginTop : false,
30591 * @cfg {Boolean} isLayoutInstant = no animation?
30593 isLayoutInstant : false, // needed?
30595 * @cfg {Boolean} isResizingContainer = not sure if this is used..
30597 isResizingContainer : true,
30599 * @cfg {Number} columnWidth width of the columns
30605 * @cfg {Number} maxCols maximum number of columns
30610 * @cfg {Number} padHeight padding below box..
30616 * @cfg {Boolean} isAutoInitial defalut true
30619 isAutoInitial : true,
30625 initialColumnWidth : 0,
30626 currentSize : null,
30628 colYs : null, // array.
30635 bricks: null, //CompositeElement
30636 cols : 0, // array?
30637 // element : null, // wrapped now this.el
30638 _isLayoutInited : null,
30641 getAutoCreate : function(){
30645 cls: 'blog-masonary-wrapper ' + this.cls,
30647 cls : 'mas-boxes masonary'
30654 getChildContainer: function( )
30656 if (this.boxesEl) {
30657 return this.boxesEl;
30660 this.boxesEl = this.el.select('.mas-boxes').first();
30662 return this.boxesEl;
30666 initEvents : function()
30670 if(this.isAutoInitial){
30671 Roo.log('hook children rendered');
30672 this.on('childrenrendered', function() {
30673 Roo.log('children rendered');
30680 initial : function()
30682 this.reloadItems();
30684 this.currentSize = this.el.getBox(true);
30686 /// was window resize... - let's see if this works..
30687 Roo.EventManager.onWindowResize(this.resize, this);
30689 if(!this.isAutoInitial){
30694 this.layout.defer(500,this);
30697 reloadItems: function()
30699 this.bricks = this.el.select('.masonry-brick', true);
30701 this.bricks.each(function(b) {
30702 //Roo.log(b.getSize());
30703 if (!b.attr('originalwidth')) {
30704 b.attr('originalwidth', b.getSize().width);
30709 Roo.log(this.bricks.elements.length);
30712 resize : function()
30715 var cs = this.el.getBox(true);
30717 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
30718 Roo.log("no change in with or X");
30721 this.currentSize = cs;
30725 layout : function()
30728 this._resetLayout();
30729 //this._manageStamps();
30731 // don't animate first layout
30732 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30733 this.layoutItems( isInstant );
30735 // flag for initalized
30736 this._isLayoutInited = true;
30739 layoutItems : function( isInstant )
30741 //var items = this._getItemsForLayout( this.items );
30742 // original code supports filtering layout items.. we just ignore it..
30744 this._layoutItems( this.bricks , isInstant );
30746 this._postLayout();
30748 _layoutItems : function ( items , isInstant)
30750 //this.fireEvent( 'layout', this, items );
30753 if ( !items || !items.elements.length ) {
30754 // no items, emit event with empty array
30759 items.each(function(item) {
30760 Roo.log("layout item");
30762 // get x/y object from method
30763 var position = this._getItemLayoutPosition( item );
30765 position.item = item;
30766 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
30767 queue.push( position );
30770 this._processLayoutQueue( queue );
30772 /** Sets position of item in DOM
30773 * @param {Element} item
30774 * @param {Number} x - horizontal position
30775 * @param {Number} y - vertical position
30776 * @param {Boolean} isInstant - disables transitions
30778 _processLayoutQueue : function( queue )
30780 for ( var i=0, len = queue.length; i < len; i++ ) {
30781 var obj = queue[i];
30782 obj.item.position('absolute');
30783 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
30789 * Any logic you want to do after each layout,
30790 * i.e. size the container
30792 _postLayout : function()
30794 this.resizeContainer();
30797 resizeContainer : function()
30799 if ( !this.isResizingContainer ) {
30802 var size = this._getContainerSize();
30804 this.el.setSize(size.width,size.height);
30805 this.boxesEl.setSize(size.width,size.height);
30811 _resetLayout : function()
30813 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
30814 this.colWidth = this.el.getWidth();
30815 //this.gutter = this.el.getWidth();
30817 this.measureColumns();
30823 this.colYs.push( 0 );
30829 measureColumns : function()
30831 this.getContainerWidth();
30832 // if columnWidth is 0, default to outerWidth of first item
30833 if ( !this.columnWidth ) {
30834 var firstItem = this.bricks.first();
30835 Roo.log(firstItem);
30836 this.columnWidth = this.containerWidth;
30837 if (firstItem && firstItem.attr('originalwidth') ) {
30838 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
30840 // columnWidth fall back to item of first element
30841 Roo.log("set column width?");
30842 this.initialColumnWidth = this.columnWidth ;
30844 // if first elem has no width, default to size of container
30849 if (this.initialColumnWidth) {
30850 this.columnWidth = this.initialColumnWidth;
30855 // column width is fixed at the top - however if container width get's smaller we should
30858 // this bit calcs how man columns..
30860 var columnWidth = this.columnWidth += this.gutter;
30862 // calculate columns
30863 var containerWidth = this.containerWidth + this.gutter;
30865 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30866 // fix rounding errors, typically with gutters
30867 var excess = columnWidth - containerWidth % columnWidth;
30870 // if overshoot is less than a pixel, round up, otherwise floor it
30871 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30872 cols = Math[ mathMethod ]( cols );
30873 this.cols = Math.max( cols, 1 );
30874 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30876 // padding positioning..
30877 var totalColWidth = this.cols * this.columnWidth;
30878 var padavail = this.containerWidth - totalColWidth;
30879 // so for 2 columns - we need 3 'pads'
30881 var padNeeded = (1+this.cols) * this.padWidth;
30883 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30885 this.columnWidth += padExtra
30886 //this.padWidth = Math.floor(padavail / ( this.cols));
30888 // adjust colum width so that padding is fixed??
30890 // we have 3 columns ... total = width * 3
30891 // we have X left over... that should be used by
30893 //if (this.expandC) {
30901 getContainerWidth : function()
30903 /* // container is parent if fit width
30904 var container = this.isFitWidth ? this.element.parentNode : this.element;
30905 // check that this.size and size are there
30906 // IE8 triggers resize on body size change, so they might not be
30908 var size = getSize( container ); //FIXME
30909 this.containerWidth = size && size.innerWidth; //FIXME
30912 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
30916 _getItemLayoutPosition : function( item ) // what is item?
30918 // we resize the item to our columnWidth..
30920 item.setWidth(this.columnWidth);
30921 item.autoBoxAdjust = false;
30923 var sz = item.getSize();
30925 // how many columns does this brick span
30926 var remainder = this.containerWidth % this.columnWidth;
30928 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30929 // round if off by 1 pixel, otherwise use ceil
30930 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
30931 colSpan = Math.min( colSpan, this.cols );
30933 // normally this should be '1' as we dont' currently allow multi width columns..
30935 var colGroup = this._getColGroup( colSpan );
30936 // get the minimum Y value from the columns
30937 var minimumY = Math.min.apply( Math, colGroup );
30938 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30940 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
30942 // position the brick
30944 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30945 y: this.currentSize.y + minimumY + this.padHeight
30949 // apply setHeight to necessary columns
30950 var setHeight = minimumY + sz.height + this.padHeight;
30951 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
30953 var setSpan = this.cols + 1 - colGroup.length;
30954 for ( var i = 0; i < setSpan; i++ ) {
30955 this.colYs[ shortColIndex + i ] = setHeight ;
30962 * @param {Number} colSpan - number of columns the element spans
30963 * @returns {Array} colGroup
30965 _getColGroup : function( colSpan )
30967 if ( colSpan < 2 ) {
30968 // if brick spans only one column, use all the column Ys
30973 // how many different places could this brick fit horizontally
30974 var groupCount = this.cols + 1 - colSpan;
30975 // for each group potential horizontal position
30976 for ( var i = 0; i < groupCount; i++ ) {
30977 // make an array of colY values for that one group
30978 var groupColYs = this.colYs.slice( i, i + colSpan );
30979 // and get the max value of the array
30980 colGroup[i] = Math.max.apply( Math, groupColYs );
30985 _manageStamp : function( stamp )
30987 var stampSize = stamp.getSize();
30988 var offset = stamp.getBox();
30989 // get the columns that this stamp affects
30990 var firstX = this.isOriginLeft ? offset.x : offset.right;
30991 var lastX = firstX + stampSize.width;
30992 var firstCol = Math.floor( firstX / this.columnWidth );
30993 firstCol = Math.max( 0, firstCol );
30995 var lastCol = Math.floor( lastX / this.columnWidth );
30996 // lastCol should not go over if multiple of columnWidth #425
30997 lastCol -= lastX % this.columnWidth ? 0 : 1;
30998 lastCol = Math.min( this.cols - 1, lastCol );
31000 // set colYs to bottom of the stamp
31001 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31004 for ( var i = firstCol; i <= lastCol; i++ ) {
31005 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31010 _getContainerSize : function()
31012 this.maxY = Math.max.apply( Math, this.colYs );
31017 if ( this.isFitWidth ) {
31018 size.width = this._getContainerFitWidth();
31024 _getContainerFitWidth : function()
31026 var unusedCols = 0;
31027 // count unused columns
31030 if ( this.colYs[i] !== 0 ) {
31035 // fit container to columns that have been used
31036 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31039 needsResizeLayout : function()
31041 var previousWidth = this.containerWidth;
31042 this.getContainerWidth();
31043 return previousWidth !== this.containerWidth;
31058 * @class Roo.bootstrap.MasonryBrick
31059 * @extends Roo.bootstrap.Component
31060 * Bootstrap MasonryBrick class
31063 * Create a new MasonryBrick
31064 * @param {Object} config The config object
31067 Roo.bootstrap.MasonryBrick = function(config){
31068 Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31074 * When a MasonryBrick is clcik
31075 * @param {Roo.bootstrap.MasonryBrick} this
31076 * @param {Roo.EventObject} e
31082 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component, {
31085 * @cfg {String} title
31089 * @cfg {String} html
31093 * @cfg {String} bgimage
31097 * @cfg {String} videourl
31101 * @cfg {String} cls
31105 * @cfg {String} href
31109 * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
31114 * @cfg {String} (center|bottom) placetitle
31119 * @cfg {Boolean} isFitContainer defalut true
31121 isFitContainer : true,
31124 * @cfg {Boolean} preventDefault defalut false
31126 preventDefault : false,
31128 getAutoCreate : function()
31130 if(!this.isFitContainer){
31131 return this.getSplitAutoCreate();
31134 var cls = 'masonry-brick masonry-brick-full';
31136 if(this.href.length){
31137 cls += ' masonry-brick-link';
31140 if(this.bgimage.length){
31141 cls += ' masonry-brick-image';
31144 if(!this.html.length){
31145 cls += ' enable-mask';
31149 cls += ' masonry-' + this.size + '-brick';
31152 if(this.placetitle.length){
31154 switch (this.placetitle) {
31156 cls += ' masonry-center-title';
31159 cls += ' masonry-bottom-title';
31166 if(!this.html.length && !this.bgimage.length){
31167 cls += ' masonry-center-title';
31170 if(!this.html.length && this.bgimage.length){
31171 cls += ' masonry-bottom-title';
31176 cls += ' ' + this.cls;
31180 tag: (this.href.length) ? 'a' : 'div',
31185 cls: 'masonry-brick-paragraph',
31191 if(this.href.length){
31192 cfg.href = this.href;
31195 var cn = cfg.cn[0].cn;
31197 if(this.title.length){
31200 cls: 'masonry-brick-title',
31205 if(this.html.length){
31208 cls: 'masonry-brick-text',
31212 if (!this.title.length && !this.html.length) {
31213 cfg.cn[0].cls += ' hide';
31216 if(this.bgimage.length){
31219 cls: 'masonry-brick-image-view',
31224 if(this.videourl.length){
31225 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31226 // youtube support only?
31229 cls: 'masonry-brick-image-view',
31232 allowfullscreen : true
31240 cls: 'masonry-brick-mask'
31247 getSplitAutoCreate : function()
31249 var cls = 'masonry-brick masonry-brick-split';
31251 if(this.href.length){
31252 cls += ' masonry-brick-link';
31255 if(this.bgimage.length){
31256 cls += ' masonry-brick-image';
31260 cls += ' masonry-' + this.size + '-brick';
31263 switch (this.placetitle) {
31265 cls += ' masonry-center-title';
31268 cls += ' masonry-bottom-title';
31271 if(!this.bgimage.length){
31272 cls += ' masonry-center-title';
31275 if(this.bgimage.length){
31276 cls += ' masonry-bottom-title';
31282 cls += ' ' + this.cls;
31286 tag: (this.href.length) ? 'a' : 'div',
31291 cls: 'masonry-brick-split-head',
31295 cls: 'masonry-brick-paragraph',
31302 cls: 'masonry-brick-split-body',
31308 if(this.href.length){
31309 cfg.href = this.href;
31312 if(this.title.length){
31313 cfg.cn[0].cn[0].cn.push({
31315 cls: 'masonry-brick-title',
31320 if(this.html.length){
31321 cfg.cn[1].cn.push({
31323 cls: 'masonry-brick-text',
31328 if(this.bgimage.length){
31329 cfg.cn[0].cn.push({
31331 cls: 'masonry-brick-image-view',
31336 if(this.videourl.length){
31337 var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
31338 // youtube support only?
31339 cfg.cn[0].cn.cn.push({
31341 cls: 'masonry-brick-image-view',
31344 allowfullscreen : true
31351 initEvents: function()
31353 switch (this.size) {
31386 this.el.on('touchstart', this.onTouchStart, this);
31387 this.el.on('touchmove', this.onTouchMove, this);
31388 this.el.on('touchend', this.onTouchEnd, this);
31389 this.el.on('contextmenu', this.onContextMenu, this);
31391 this.el.on('mouseenter' ,this.enter, this);
31392 this.el.on('mouseleave', this.leave, this);
31393 this.el.on('click', this.onClick, this);
31396 if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
31397 this.parent().bricks.push(this);
31402 onClick: function(e, el)
31404 var time = this.endTimer - this.startTimer;
31408 e.preventDefault();
31413 if(!this.preventDefault){
31417 e.preventDefault();
31418 this.fireEvent('click', this);
31421 enter: function(e, el)
31423 e.preventDefault();
31425 if(!this.isFitContainer){
31429 if(this.bgimage.length && this.html.length){
31430 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31434 leave: function(e, el)
31436 e.preventDefault();
31438 if(!this.isFitContainer){
31442 if(this.bgimage.length && this.html.length){
31443 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31447 onTouchStart: function(e, el)
31449 // e.preventDefault();
31451 this.touchmoved = false;
31453 if(!this.isFitContainer){
31457 if(!this.bgimage.length || !this.html.length){
31461 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
31463 this.timer = new Date().getTime();
31467 onTouchMove: function(e, el)
31469 this.touchmoved = true;
31472 onContextMenu : function(e,el)
31474 e.preventDefault();
31475 e.stopPropagation();
31479 onTouchEnd: function(e, el)
31481 // e.preventDefault();
31483 if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
31490 if(!this.bgimage.length || !this.html.length){
31492 if(this.href.length){
31493 window.location.href = this.href;
31499 if(!this.isFitContainer){
31503 this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
31505 window.location.href = this.href;
31520 * @class Roo.bootstrap.Brick
31521 * @extends Roo.bootstrap.Component
31522 * Bootstrap Brick class
31525 * Create a new Brick
31526 * @param {Object} config The config object
31529 Roo.bootstrap.Brick = function(config){
31530 Roo.bootstrap.Brick.superclass.constructor.call(this, config);
31536 * When a Brick is click
31537 * @param {Roo.bootstrap.Brick} this
31538 * @param {Roo.EventObject} e
31544 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component, {
31547 * @cfg {String} title
31551 * @cfg {String} html
31555 * @cfg {String} bgimage
31559 * @cfg {String} cls
31563 * @cfg {String} href
31567 * @cfg {String} video
31571 * @cfg {Boolean} square
31575 getAutoCreate : function()
31577 var cls = 'roo-brick';
31579 if(this.href.length){
31580 cls += ' roo-brick-link';
31583 if(this.bgimage.length){
31584 cls += ' roo-brick-image';
31587 if(!this.html.length && !this.bgimage.length){
31588 cls += ' roo-brick-center-title';
31591 if(!this.html.length && this.bgimage.length){
31592 cls += ' roo-brick-bottom-title';
31596 cls += ' ' + this.cls;
31600 tag: (this.href.length) ? 'a' : 'div',
31605 cls: 'roo-brick-paragraph',
31611 if(this.href.length){
31612 cfg.href = this.href;
31615 var cn = cfg.cn[0].cn;
31617 if(this.title.length){
31620 cls: 'roo-brick-title',
31625 if(this.html.length){
31628 cls: 'roo-brick-text',
31635 if(this.bgimage.length){
31638 cls: 'roo-brick-image-view',
31646 initEvents: function()
31648 if(this.title.length || this.html.length){
31649 this.el.on('mouseenter' ,this.enter, this);
31650 this.el.on('mouseleave', this.leave, this);
31654 Roo.EventManager.onWindowResize(this.resize, this);
31659 resize : function()
31661 var paragraph = this.el.select('.roo-brick-paragraph', true).first();
31663 paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
31665 if(this.bgimage.length){
31666 var image = this.el.select('.roo-brick-image-view', true).first();
31667 image.setWidth(paragraph.getWidth());
31668 image.setHeight(paragraph.getWidth());
31670 this.el.setHeight(paragraph.getWidth());
31676 enter: function(e, el)
31678 e.preventDefault();
31680 if(this.bgimage.length){
31681 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
31682 this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
31686 leave: function(e, el)
31688 e.preventDefault();
31690 if(this.bgimage.length){
31691 this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
31692 this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
31708 * @class Roo.bootstrap.NumberField
31709 * @extends Roo.bootstrap.Input
31710 * Bootstrap NumberField class
31716 * Create a new NumberField
31717 * @param {Object} config The config object
31720 Roo.bootstrap.NumberField = function(config){
31721 Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
31724 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
31727 * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
31729 allowDecimals : true,
31731 * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
31733 decimalSeparator : ".",
31735 * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
31737 decimalPrecision : 2,
31739 * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
31741 allowNegative : true,
31743 * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
31745 minValue : Number.NEGATIVE_INFINITY,
31747 * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
31749 maxValue : Number.MAX_VALUE,
31751 * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
31753 minText : "The minimum value for this field is {0}",
31755 * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
31757 maxText : "The maximum value for this field is {0}",
31759 * @cfg {String} nanText Error text to display if the value is not a valid number. For example, this can happen
31760 * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
31762 nanText : "{0} is not a valid number",
31764 * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
31769 initEvents : function()
31771 Roo.bootstrap.NumberField.superclass.initEvents.call(this);
31773 var allowed = "0123456789";
31775 if(this.allowDecimals){
31776 allowed += this.decimalSeparator;
31779 if(this.allowNegative){
31783 this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
31785 var keyPress = function(e){
31787 var k = e.getKey();
31789 var c = e.getCharCode();
31792 (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
31793 allowed.indexOf(String.fromCharCode(c)) === -1
31799 if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
31803 if(allowed.indexOf(String.fromCharCode(c)) === -1){
31808 this.el.on("keypress", keyPress, this);
31811 validateValue : function(value)
31814 if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
31818 var num = this.parseValue(value);
31821 this.markInvalid(String.format(this.nanText, value));
31825 if(num < this.minValue){
31826 this.markInvalid(String.format(this.minText, this.minValue));
31830 if(num > this.maxValue){
31831 this.markInvalid(String.format(this.maxText, this.maxValue));
31838 getValue : function()
31840 return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
31843 parseValue : function(value)
31845 value = parseFloat(String(value).replace(this.decimalSeparator, "."));
31846 return isNaN(value) ? '' : value;
31849 fixPrecision : function(value)
31851 var nan = isNaN(value);
31853 if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
31854 return nan ? '' : value;
31856 return parseFloat(value).toFixed(this.decimalPrecision);
31859 setValue : function(v)
31861 v = this.fixPrecision(v);
31862 Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
31865 decimalPrecisionFcn : function(v)
31867 return Math.floor(v);
31870 beforeBlur : function()
31876 var v = this.parseValue(this.getRawValue());
31891 * @class Roo.bootstrap.DocumentSlider
31892 * @extends Roo.bootstrap.Component
31893 * Bootstrap DocumentSlider class
31896 * Create a new DocumentViewer
31897 * @param {Object} config The config object
31900 Roo.bootstrap.DocumentSlider = function(config){
31901 Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
31908 * Fire after initEvent
31909 * @param {Roo.bootstrap.DocumentSlider} this
31914 * Fire after update
31915 * @param {Roo.bootstrap.DocumentSlider} this
31921 * @param {Roo.bootstrap.DocumentSlider} this
31927 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component, {
31933 getAutoCreate : function()
31937 cls : 'roo-document-slider',
31941 cls : 'roo-document-slider-header',
31945 cls : 'roo-document-slider-header-title'
31951 cls : 'roo-document-slider-body',
31955 cls : 'roo-document-slider-prev',
31959 cls : 'fa fa-chevron-left'
31965 cls : 'roo-document-slider-thumb',
31969 cls : 'roo-document-slider-image'
31975 cls : 'roo-document-slider-next',
31979 cls : 'fa fa-chevron-right'
31991 initEvents : function()
31993 this.headerEl = this.el.select('.roo-document-slider-header', true).first();
31994 this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
31996 this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
31997 this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
31999 this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
32000 this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32002 this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
32003 this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32005 this.imageEl = this.el.select('.roo-document-slider-image', true).first();
32006 this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32008 this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
32009 this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32011 this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
32012 this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
32014 this.thumbEl.on('click', this.onClick, this);
32016 this.prevIndicator.on('click', this.prev, this);
32018 this.nextIndicator.on('click', this.next, this);
32022 initial : function()
32024 if(this.files.length){
32025 this.indicator = 1;
32029 this.fireEvent('initial', this);
32032 update : function()
32034 this.imageEl.attr('src', this.files[this.indicator - 1]);
32036 this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
32038 this.prevIndicator.show();
32040 if(this.indicator == 1){
32041 this.prevIndicator.hide();
32044 this.nextIndicator.show();
32046 if(this.indicator == this.files.length){
32047 this.nextIndicator.hide();
32050 this.thumbEl.scrollTo('top');
32052 this.fireEvent('update', this);
32055 onClick : function(e)
32057 e.preventDefault();
32059 this.fireEvent('click', this);
32064 e.preventDefault();
32066 this.indicator = Math.max(1, this.indicator - 1);
32073 e.preventDefault();
32075 this.indicator = Math.min(this.files.length, this.indicator + 1);
32082 * Ext JS Library 1.1.1
32083 * Copyright(c) 2006-2007, Ext JS, LLC.
32085 * Originally Released Under LGPL - original licence link has changed is not relivant.
32088 * <script type="text/javascript">
32093 * @class Roo.bootstrap.SplitBar
32094 * @extends Roo.util.Observable
32095 * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
32099 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
32100 Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
32101 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
32102 split.minSize = 100;
32103 split.maxSize = 600;
32104 split.animate = true;
32105 split.on('moved', splitterMoved);
32108 * Create a new SplitBar
32109 * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar.
32110 * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged
32111 * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32112 * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or
32113 Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
32114 position of the SplitBar).
32116 Roo.bootstrap.SplitBar = function(cfg){
32121 // dragElement : elm
32122 // resizingElement: el,
32124 // orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
32125 // placement : Roo.bootstrap.SplitBar.LEFT ,
32126 // existingProxy ???
32129 this.el = Roo.get(cfg.dragElement, true);
32130 this.el.dom.unselectable = "on";
32132 this.resizingEl = Roo.get(cfg.resizingElement, true);
32136 * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
32137 * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
32140 this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
32143 * The minimum size of the resizing element. (Defaults to 0)
32149 * The maximum size of the resizing element. (Defaults to 2000)
32152 this.maxSize = 2000;
32155 * Whether to animate the transition to the new size
32158 this.animate = false;
32161 * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
32164 this.useShim = false;
32169 if(!cfg.existingProxy){
32171 this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
32173 this.proxy = Roo.get(cfg.existingProxy).dom;
32176 this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
32179 this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
32182 this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
32185 this.dragSpecs = {};
32188 * @private The adapter to use to positon and resize elements
32190 this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32191 this.adapter.init(this);
32193 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32195 this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
32196 this.el.addClass("roo-splitbar-h");
32199 this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
32200 this.el.addClass("roo-splitbar-v");
32206 * Fires when the splitter is moved (alias for {@link #event-moved})
32207 * @param {Roo.bootstrap.SplitBar} this
32208 * @param {Number} newSize the new width or height
32213 * Fires when the splitter is moved
32214 * @param {Roo.bootstrap.SplitBar} this
32215 * @param {Number} newSize the new width or height
32219 * @event beforeresize
32220 * Fires before the splitter is dragged
32221 * @param {Roo.bootstrap.SplitBar} this
32223 "beforeresize" : true,
32225 "beforeapply" : true
32228 Roo.util.Observable.call(this);
32231 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
32232 onStartProxyDrag : function(x, y){
32233 this.fireEvent("beforeresize", this);
32235 var o = Roo.DomHelper.insertFirst(document.body, {cls: "roo-drag-overlay", html: " "}, true);
32237 o.enableDisplayMode("block");
32238 // all splitbars share the same overlay
32239 Roo.bootstrap.SplitBar.prototype.overlay = o;
32241 this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32242 this.overlay.show();
32243 Roo.get(this.proxy).setDisplayed("block");
32244 var size = this.adapter.getElementSize(this);
32245 this.activeMinSize = this.getMinimumSize();;
32246 this.activeMaxSize = this.getMaximumSize();;
32247 var c1 = size - this.activeMinSize;
32248 var c2 = Math.max(this.activeMaxSize - size, 0);
32249 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32250 this.dd.resetConstraints();
32251 this.dd.setXConstraint(
32252 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2,
32253 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
32255 this.dd.setYConstraint(0, 0);
32257 this.dd.resetConstraints();
32258 this.dd.setXConstraint(0, 0);
32259 this.dd.setYConstraint(
32260 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2,
32261 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
32264 this.dragSpecs.startSize = size;
32265 this.dragSpecs.startPoint = [x, y];
32266 Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
32270 * @private Called after the drag operation by the DDProxy
32272 onEndProxyDrag : function(e){
32273 Roo.get(this.proxy).setDisplayed(false);
32274 var endPoint = Roo.lib.Event.getXY(e);
32276 this.overlay.hide();
32279 if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32280 newSize = this.dragSpecs.startSize +
32281 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
32282 endPoint[0] - this.dragSpecs.startPoint[0] :
32283 this.dragSpecs.startPoint[0] - endPoint[0]
32286 newSize = this.dragSpecs.startSize +
32287 (this.placement == Roo.bootstrap.SplitBar.TOP ?
32288 endPoint[1] - this.dragSpecs.startPoint[1] :
32289 this.dragSpecs.startPoint[1] - endPoint[1]
32292 newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
32293 if(newSize != this.dragSpecs.startSize){
32294 if(this.fireEvent('beforeapply', this, newSize) !== false){
32295 this.adapter.setElementSize(this, newSize);
32296 this.fireEvent("moved", this, newSize);
32297 this.fireEvent("resize", this, newSize);
32303 * Get the adapter this SplitBar uses
32304 * @return The adapter object
32306 getAdapter : function(){
32307 return this.adapter;
32311 * Set the adapter this SplitBar uses
32312 * @param {Object} adapter A SplitBar adapter object
32314 setAdapter : function(adapter){
32315 this.adapter = adapter;
32316 this.adapter.init(this);
32320 * Gets the minimum size for the resizing element
32321 * @return {Number} The minimum size
32323 getMinimumSize : function(){
32324 return this.minSize;
32328 * Sets the minimum size for the resizing element
32329 * @param {Number} minSize The minimum size
32331 setMinimumSize : function(minSize){
32332 this.minSize = minSize;
32336 * Gets the maximum size for the resizing element
32337 * @return {Number} The maximum size
32339 getMaximumSize : function(){
32340 return this.maxSize;
32344 * Sets the maximum size for the resizing element
32345 * @param {Number} maxSize The maximum size
32347 setMaximumSize : function(maxSize){
32348 this.maxSize = maxSize;
32352 * Sets the initialize size for the resizing element
32353 * @param {Number} size The initial size
32355 setCurrentSize : function(size){
32356 var oldAnimate = this.animate;
32357 this.animate = false;
32358 this.adapter.setElementSize(this, size);
32359 this.animate = oldAnimate;
32363 * Destroy this splitbar.
32364 * @param {Boolean} removeEl True to remove the element
32366 destroy : function(removeEl){
32368 this.shim.remove();
32371 this.proxy.parentNode.removeChild(this.proxy);
32379 * @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.
32381 Roo.bootstrap.SplitBar.createProxy = function(dir){
32382 var proxy = new Roo.Element(document.createElement("div"));
32383 proxy.unselectable();
32384 var cls = 'roo-splitbar-proxy';
32385 proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
32386 document.body.appendChild(proxy.dom);
32391 * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
32392 * Default Adapter. It assumes the splitter and resizing element are not positioned
32393 * elements and only gets/sets the width of the element. Generally used for table based layouts.
32395 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
32398 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
32399 // do nothing for now
32400 init : function(s){
32404 * Called before drag operations to get the current size of the resizing element.
32405 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32407 getElementSize : function(s){
32408 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32409 return s.resizingEl.getWidth();
32411 return s.resizingEl.getHeight();
32416 * Called after drag operations to set the size of the resizing element.
32417 * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
32418 * @param {Number} newSize The new size to set
32419 * @param {Function} onComplete A function to be invoked when resizing is complete
32421 setElementSize : function(s, newSize, onComplete){
32422 if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
32424 s.resizingEl.setWidth(newSize);
32426 onComplete(s, newSize);
32429 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
32434 s.resizingEl.setHeight(newSize);
32436 onComplete(s, newSize);
32439 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
32446 *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
32447 * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
32448 * Adapter that moves the splitter element to align with the resized sizing element.
32449 * Used with an absolute positioned SplitBar.
32450 * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
32451 * document.body, make sure you assign an id to the body element.
32453 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
32454 this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
32455 this.container = Roo.get(container);
32458 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
32459 init : function(s){
32460 this.basic.init(s);
32463 getElementSize : function(s){
32464 return this.basic.getElementSize(s);
32467 setElementSize : function(s, newSize, onComplete){
32468 this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
32471 moveSplitter : function(s){
32472 var yes = Roo.bootstrap.SplitBar;
32473 switch(s.placement){
32475 s.el.setX(s.resizingEl.getRight());
32478 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
32481 s.el.setY(s.resizingEl.getBottom());
32484 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
32491 * Orientation constant - Create a vertical SplitBar
32495 Roo.bootstrap.SplitBar.VERTICAL = 1;
32498 * Orientation constant - Create a horizontal SplitBar
32502 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
32505 * Placement constant - The resizing element is to the left of the splitter element
32509 Roo.bootstrap.SplitBar.LEFT = 1;
32512 * Placement constant - The resizing element is to the right of the splitter element
32516 Roo.bootstrap.SplitBar.RIGHT = 2;
32519 * Placement constant - The resizing element is positioned above the splitter element
32523 Roo.bootstrap.SplitBar.TOP = 3;
32526 * Placement constant - The resizing element is positioned under splitter element
32530 Roo.bootstrap.SplitBar.BOTTOM = 4;
32531 Roo.namespace("Roo.bootstrap.layout");/*
32533 * Ext JS Library 1.1.1
32534 * Copyright(c) 2006-2007, Ext JS, LLC.
32536 * Originally Released Under LGPL - original licence link has changed is not relivant.
32539 * <script type="text/javascript">
32543 * @class Roo.bootstrap.layout.Manager
32544 * @extends Roo.bootstrap.Component
32545 * Base class for layout managers.
32547 Roo.bootstrap.layout.Manager = function(config)
32549 Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
32555 /** false to disable window resize monitoring @type Boolean */
32556 this.monitorWindowResize = true;
32561 * Fires when a layout is performed.
32562 * @param {Roo.LayoutManager} this
32566 * @event regionresized
32567 * Fires when the user resizes a region.
32568 * @param {Roo.LayoutRegion} region The resized region
32569 * @param {Number} newSize The new size (width for east/west, height for north/south)
32571 "regionresized" : true,
32573 * @event regioncollapsed
32574 * Fires when a region is collapsed.
32575 * @param {Roo.LayoutRegion} region The collapsed region
32577 "regioncollapsed" : true,
32579 * @event regionexpanded
32580 * Fires when a region is expanded.
32581 * @param {Roo.LayoutRegion} region The expanded region
32583 "regionexpanded" : true
32585 this.updating = false;
32588 this.el = Roo.get(config.el);
32594 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
32599 monitorWindowResize : true,
32605 onRender : function(ct, position)
32608 this.el = Roo.get(ct);
32611 //this.fireEvent('render',this);
32615 initEvents: function()
32619 // ie scrollbar fix
32620 if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
32621 document.body.scroll = "no";
32622 }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
32623 this.el.position('relative');
32625 this.id = this.el.id;
32626 this.el.addClass("roo-layout-container");
32627 Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
32628 if(this.el.dom != document.body ) {
32629 this.el.on('resize', this.layout,this);
32630 this.el.on('show', this.layout,this);
32636 * Returns true if this layout is currently being updated
32637 * @return {Boolean}
32639 isUpdating : function(){
32640 return this.updating;
32644 * Suspend the LayoutManager from doing auto-layouts while
32645 * making multiple add or remove calls
32647 beginUpdate : function(){
32648 this.updating = true;
32652 * Restore auto-layouts and optionally disable the manager from performing a layout
32653 * @param {Boolean} noLayout true to disable a layout update
32655 endUpdate : function(noLayout){
32656 this.updating = false;
32662 layout: function(){
32666 onRegionResized : function(region, newSize){
32667 this.fireEvent("regionresized", region, newSize);
32671 onRegionCollapsed : function(region){
32672 this.fireEvent("regioncollapsed", region);
32675 onRegionExpanded : function(region){
32676 this.fireEvent("regionexpanded", region);
32680 * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
32681 * performs box-model adjustments.
32682 * @return {Object} The size as an object {width: (the width), height: (the height)}
32684 getViewSize : function()
32687 if(this.el.dom != document.body){
32688 size = this.el.getSize();
32690 size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
32692 size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
32693 size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
32698 * Returns the Element this layout is bound to.
32699 * @return {Roo.Element}
32701 getEl : function(){
32706 * Returns the specified region.
32707 * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
32708 * @return {Roo.LayoutRegion}
32710 getRegion : function(target){
32711 return this.regions[target.toLowerCase()];
32714 onWindowResize : function(){
32715 if(this.monitorWindowResize){
32722 * Ext JS Library 1.1.1
32723 * Copyright(c) 2006-2007, Ext JS, LLC.
32725 * Originally Released Under LGPL - original licence link has changed is not relivant.
32728 * <script type="text/javascript">
32731 * @class Roo.bootstrap.layout.Border
32732 * @extends Roo.bootstrap.layout.Manager
32733 * This class represents a common layout manager used in desktop applications. For screenshots and more details,
32734 * please see: examples/bootstrap/nested.html<br><br>
32736 <b>The container the layout is rendered into can be either the body element or any other element.
32737 If it is not the body element, the container needs to either be an absolute positioned element,
32738 or you will need to add "position:relative" to the css of the container. You will also need to specify
32739 the container size if it is not the body element.</b>
32742 * Create a new Border
32743 * @param {Object} config Configuration options
32745 Roo.bootstrap.layout.Border = function(config){
32746 config = config || {};
32747 Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
32751 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32752 if(config[region]){
32753 config[region].region = region;
32754 this.addRegion(config[region]);
32760 Roo.bootstrap.layout.Border.regions = ["north","south","east","west","center"];
32762 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
32764 * Creates and adds a new region if it doesn't already exist.
32765 * @param {String} target The target region key (north, south, east, west or center).
32766 * @param {Object} config The regions config object
32767 * @return {BorderLayoutRegion} The new region
32769 addRegion : function(config)
32771 if(!this.regions[config.region]){
32772 var r = this.factory(config);
32773 this.bindRegion(r);
32775 return this.regions[config.region];
32779 bindRegion : function(r){
32780 this.regions[r.config.region] = r;
32782 r.on("visibilitychange", this.layout, this);
32783 r.on("paneladded", this.layout, this);
32784 r.on("panelremoved", this.layout, this);
32785 r.on("invalidated", this.layout, this);
32786 r.on("resized", this.onRegionResized, this);
32787 r.on("collapsed", this.onRegionCollapsed, this);
32788 r.on("expanded", this.onRegionExpanded, this);
32792 * Performs a layout update.
32794 layout : function()
32796 if(this.updating) {
32800 // render all the rebions if they have not been done alreayd?
32801 Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
32802 if(this.regions[region] && !this.regions[region].bodyEl){
32803 this.regions[region].onRender(this.el)
32807 var size = this.getViewSize();
32808 var w = size.width;
32809 var h = size.height;
32814 //var x = 0, y = 0;
32816 var rs = this.regions;
32817 var north = rs["north"];
32818 var south = rs["south"];
32819 var west = rs["west"];
32820 var east = rs["east"];
32821 var center = rs["center"];
32822 //if(this.hideOnLayout){ // not supported anymore
32823 //c.el.setStyle("display", "none");
32825 if(north && north.isVisible()){
32826 var b = north.getBox();
32827 var m = north.getMargins();
32828 b.width = w - (m.left+m.right);
32831 centerY = b.height + b.y + m.bottom;
32832 centerH -= centerY;
32833 north.updateBox(this.safeBox(b));
32835 if(south && south.isVisible()){
32836 var b = south.getBox();
32837 var m = south.getMargins();
32838 b.width = w - (m.left+m.right);
32840 var totalHeight = (b.height + m.top + m.bottom);
32841 b.y = h - totalHeight + m.top;
32842 centerH -= totalHeight;
32843 south.updateBox(this.safeBox(b));
32845 if(west && west.isVisible()){
32846 var b = west.getBox();
32847 var m = west.getMargins();
32848 b.height = centerH - (m.top+m.bottom);
32850 b.y = centerY + m.top;
32851 var totalWidth = (b.width + m.left + m.right);
32852 centerX += totalWidth;
32853 centerW -= totalWidth;
32854 west.updateBox(this.safeBox(b));
32856 if(east && east.isVisible()){
32857 var b = east.getBox();
32858 var m = east.getMargins();
32859 b.height = centerH - (m.top+m.bottom);
32860 var totalWidth = (b.width + m.left + m.right);
32861 b.x = w - totalWidth + m.left;
32862 b.y = centerY + m.top;
32863 centerW -= totalWidth;
32864 east.updateBox(this.safeBox(b));
32867 var m = center.getMargins();
32869 x: centerX + m.left,
32870 y: centerY + m.top,
32871 width: centerW - (m.left+m.right),
32872 height: centerH - (m.top+m.bottom)
32874 //if(this.hideOnLayout){
32875 //center.el.setStyle("display", "block");
32877 center.updateBox(this.safeBox(centerBox));
32880 this.fireEvent("layout", this);
32884 safeBox : function(box){
32885 box.width = Math.max(0, box.width);
32886 box.height = Math.max(0, box.height);
32891 * Adds a ContentPanel (or subclass) to this layout.
32892 * @param {String} target The target region key (north, south, east, west or center).
32893 * @param {Roo.ContentPanel} panel The panel to add
32894 * @return {Roo.ContentPanel} The added panel
32896 add : function(target, panel){
32898 target = target.toLowerCase();
32899 return this.regions[target].add(panel);
32903 * Remove a ContentPanel (or subclass) to this layout.
32904 * @param {String} target The target region key (north, south, east, west or center).
32905 * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
32906 * @return {Roo.ContentPanel} The removed panel
32908 remove : function(target, panel){
32909 target = target.toLowerCase();
32910 return this.regions[target].remove(panel);
32914 * Searches all regions for a panel with the specified id
32915 * @param {String} panelId
32916 * @return {Roo.ContentPanel} The panel or null if it wasn't found
32918 findPanel : function(panelId){
32919 var rs = this.regions;
32920 for(var target in rs){
32921 if(typeof rs[target] != "function"){
32922 var p = rs[target].getPanel(panelId);
32932 * Searches all regions for a panel with the specified id and activates (shows) it.
32933 * @param {String/ContentPanel} panelId The panels id or the panel itself
32934 * @return {Roo.ContentPanel} The shown panel or null
32936 showPanel : function(panelId) {
32937 var rs = this.regions;
32938 for(var target in rs){
32939 var r = rs[target];
32940 if(typeof r != "function"){
32941 if(r.hasPanel(panelId)){
32942 return r.showPanel(panelId);
32950 * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
32951 * @param {Roo.state.Provider} provider (optional) An alternate state provider
32954 restoreState : function(provider){
32956 provider = Roo.state.Manager;
32958 var sm = new Roo.LayoutStateManager();
32959 sm.init(this, provider);
32965 * Adds a xtype elements to the layout.
32969 xtype : 'ContentPanel',
32976 xtype : 'NestedLayoutPanel',
32982 items : [ ... list of content panels or nested layout panels.. ]
32986 * @param {Object} cfg Xtype definition of item to add.
32988 addxtype : function(cfg)
32990 // basically accepts a pannel...
32991 // can accept a layout region..!?!?
32992 //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
32995 // theory? children can only be panels??
32997 //if (!cfg.xtype.match(/Panel$/)) {
33002 if (typeof(cfg.region) == 'undefined') {
33003 Roo.log("Failed to add Panel, region was not set");
33007 var region = cfg.region;
33013 xitems = cfg.items;
33020 case 'Content': // ContentPanel (el, cfg)
33021 case 'Scroll': // ContentPanel (el, cfg)
33023 cfg.autoCreate = true;
33024 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33026 // var el = this.el.createChild();
33027 // ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
33030 this.add(region, ret);
33034 case 'TreePanel': // our new panel!
33035 cfg.el = this.el.createChild();
33036 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33037 this.add(region, ret);
33042 // create a new Layout (which is a Border Layout...
33044 var clayout = cfg.layout;
33045 clayout.el = this.el.createChild();
33046 clayout.items = clayout.items || [];
33050 // replace this exitems with the clayout ones..
33051 xitems = clayout.items;
33053 // force background off if it's in center...
33054 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
33055 cfg.background = false;
33057 cfg.layout = new Roo.bootstrap.layout.Border(clayout);
33060 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33061 //console.log('adding nested layout panel ' + cfg.toSource());
33062 this.add(region, ret);
33063 nb = {}; /// find first...
33068 // needs grid and region
33070 //var el = this.getRegion(region).el.createChild();
33072 *var el = this.el.createChild();
33073 // create the grid first...
33074 cfg.grid.container = el;
33075 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
33078 if (region == 'center' && this.active ) {
33079 cfg.background = false;
33082 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
33084 this.add(region, ret);
33086 if (cfg.background) {
33087 // render grid on panel activation (if panel background)
33088 ret.on('activate', function(gp) {
33089 if (!gp.grid.rendered) {
33090 // gp.grid.render(el);
33094 // cfg.grid.render(el);
33100 case 'Border': // it can get called on it'self... - might need to check if this is fixed?
33101 // it was the old xcomponent building that caused this before.
33102 // espeically if border is the top element in the tree.
33112 if (typeof(Roo[cfg.xtype]) != 'undefined') {
33114 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
33115 this.add(region, ret);
33119 throw "Can not add '" + cfg.xtype + "' to Border";
33125 this.beginUpdate();
33129 Roo.each(xitems, function(i) {
33130 region = nb && i.region ? i.region : false;
33132 var add = ret.addxtype(i);
33135 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
33136 if (!i.background) {
33137 abn[region] = nb[region] ;
33144 // make the last non-background panel active..
33145 //if (nb) { Roo.log(abn); }
33148 for(var r in abn) {
33149 region = this.getRegion(r);
33151 // tried using nb[r], but it does not work..
33153 region.showPanel(abn[r]);
33164 factory : function(cfg)
33167 var validRegions = Roo.bootstrap.layout.Border.regions;
33169 var target = cfg.region;
33172 var r = Roo.bootstrap.layout;
33176 return new r.North(cfg);
33178 return new r.South(cfg);
33180 return new r.East(cfg);
33182 return new r.West(cfg);
33184 return new r.Center(cfg);
33186 throw 'Layout region "'+target+'" not supported.';
33193 * Ext JS Library 1.1.1
33194 * Copyright(c) 2006-2007, Ext JS, LLC.
33196 * Originally Released Under LGPL - original licence link has changed is not relivant.
33199 * <script type="text/javascript">
33203 * @class Roo.bootstrap.layout.Basic
33204 * @extends Roo.util.Observable
33205 * This class represents a lightweight region in a layout manager. This region does not move dom nodes
33206 * and does not have a titlebar, tabs or any other features. All it does is size and position
33207 * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
33208 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33209 * @cfg {string} region the region that it inhabits..
33210 * @cfg {bool} skipConfig skip config?
33214 Roo.bootstrap.layout.Basic = function(config){
33216 this.mgr = config.mgr;
33218 this.position = config.region;
33220 var skipConfig = config.skipConfig;
33224 * @scope Roo.BasicLayoutRegion
33228 * @event beforeremove
33229 * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
33230 * @param {Roo.LayoutRegion} this
33231 * @param {Roo.ContentPanel} panel The panel
33232 * @param {Object} e The cancel event object
33234 "beforeremove" : true,
33236 * @event invalidated
33237 * Fires when the layout for this region is changed.
33238 * @param {Roo.LayoutRegion} this
33240 "invalidated" : true,
33242 * @event visibilitychange
33243 * Fires when this region is shown or hidden
33244 * @param {Roo.LayoutRegion} this
33245 * @param {Boolean} visibility true or false
33247 "visibilitychange" : true,
33249 * @event paneladded
33250 * Fires when a panel is added.
33251 * @param {Roo.LayoutRegion} this
33252 * @param {Roo.ContentPanel} panel The panel
33254 "paneladded" : true,
33256 * @event panelremoved
33257 * Fires when a panel is removed.
33258 * @param {Roo.LayoutRegion} this
33259 * @param {Roo.ContentPanel} panel The panel
33261 "panelremoved" : true,
33263 * @event beforecollapse
33264 * Fires when this region before collapse.
33265 * @param {Roo.LayoutRegion} this
33267 "beforecollapse" : true,
33270 * Fires when this region is collapsed.
33271 * @param {Roo.LayoutRegion} this
33273 "collapsed" : true,
33276 * Fires when this region is expanded.
33277 * @param {Roo.LayoutRegion} this
33282 * Fires when this region is slid into view.
33283 * @param {Roo.LayoutRegion} this
33285 "slideshow" : true,
33288 * Fires when this region slides out of view.
33289 * @param {Roo.LayoutRegion} this
33291 "slidehide" : true,
33293 * @event panelactivated
33294 * Fires when a panel is activated.
33295 * @param {Roo.LayoutRegion} this
33296 * @param {Roo.ContentPanel} panel The activated panel
33298 "panelactivated" : true,
33301 * Fires when the user resizes this region.
33302 * @param {Roo.LayoutRegion} this
33303 * @param {Number} newSize The new size (width for east/west, height for north/south)
33307 /** A collection of panels in this region. @type Roo.util.MixedCollection */
33308 this.panels = new Roo.util.MixedCollection();
33309 this.panels.getKey = this.getPanelId.createDelegate(this);
33311 this.activePanel = null;
33312 // ensure listeners are added...
33314 if (config.listeners || config.events) {
33315 Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
33316 listeners : config.listeners || {},
33317 events : config.events || {}
33321 if(skipConfig !== true){
33322 this.applyConfig(config);
33326 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
33328 getPanelId : function(p){
33332 applyConfig : function(config){
33333 this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33334 this.config = config;
33339 * Resizes the region to the specified size. For vertical regions (west, east) this adjusts
33340 * the width, for horizontal (north, south) the height.
33341 * @param {Number} newSize The new width or height
33343 resizeTo : function(newSize){
33344 var el = this.el ? this.el :
33345 (this.activePanel ? this.activePanel.getEl() : null);
33347 switch(this.position){
33350 el.setWidth(newSize);
33351 this.fireEvent("resized", this, newSize);
33355 el.setHeight(newSize);
33356 this.fireEvent("resized", this, newSize);
33362 getBox : function(){
33363 return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
33366 getMargins : function(){
33367 return this.margins;
33370 updateBox : function(box){
33372 var el = this.activePanel.getEl();
33373 el.dom.style.left = box.x + "px";
33374 el.dom.style.top = box.y + "px";
33375 this.activePanel.setSize(box.width, box.height);
33379 * Returns the container element for this region.
33380 * @return {Roo.Element}
33382 getEl : function(){
33383 return this.activePanel;
33387 * Returns true if this region is currently visible.
33388 * @return {Boolean}
33390 isVisible : function(){
33391 return this.activePanel ? true : false;
33394 setActivePanel : function(panel){
33395 panel = this.getPanel(panel);
33396 if(this.activePanel && this.activePanel != panel){
33397 this.activePanel.setActiveState(false);
33398 this.activePanel.getEl().setLeftTop(-10000,-10000);
33400 this.activePanel = panel;
33401 panel.setActiveState(true);
33403 panel.setSize(this.box.width, this.box.height);
33405 this.fireEvent("panelactivated", this, panel);
33406 this.fireEvent("invalidated");
33410 * Show the specified panel.
33411 * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
33412 * @return {Roo.ContentPanel} The shown panel or null
33414 showPanel : function(panel){
33415 panel = this.getPanel(panel);
33417 this.setActivePanel(panel);
33423 * Get the active panel for this region.
33424 * @return {Roo.ContentPanel} The active panel or null
33426 getActivePanel : function(){
33427 return this.activePanel;
33431 * Add the passed ContentPanel(s)
33432 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
33433 * @return {Roo.ContentPanel} The panel added (if only one was added)
33435 add : function(panel){
33436 if(arguments.length > 1){
33437 for(var i = 0, len = arguments.length; i < len; i++) {
33438 this.add(arguments[i]);
33442 if(this.hasPanel(panel)){
33443 this.showPanel(panel);
33446 var el = panel.getEl();
33447 if(el.dom.parentNode != this.mgr.el.dom){
33448 this.mgr.el.dom.appendChild(el.dom);
33450 if(panel.setRegion){
33451 panel.setRegion(this);
33453 this.panels.add(panel);
33454 el.setStyle("position", "absolute");
33455 if(!panel.background){
33456 this.setActivePanel(panel);
33457 if(this.config.initialSize && this.panels.getCount()==1){
33458 this.resizeTo(this.config.initialSize);
33461 this.fireEvent("paneladded", this, panel);
33466 * Returns true if the panel is in this region.
33467 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33468 * @return {Boolean}
33470 hasPanel : function(panel){
33471 if(typeof panel == "object"){ // must be panel obj
33472 panel = panel.getId();
33474 return this.getPanel(panel) ? true : false;
33478 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
33479 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33480 * @param {Boolean} preservePanel Overrides the config preservePanel option
33481 * @return {Roo.ContentPanel} The panel that was removed
33483 remove : function(panel, preservePanel){
33484 panel = this.getPanel(panel);
33489 this.fireEvent("beforeremove", this, panel, e);
33490 if(e.cancel === true){
33493 var panelId = panel.getId();
33494 this.panels.removeKey(panelId);
33499 * Returns the panel specified or null if it's not in this region.
33500 * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
33501 * @return {Roo.ContentPanel}
33503 getPanel : function(id){
33504 if(typeof id == "object"){ // must be panel obj
33507 return this.panels.get(id);
33511 * Returns this regions position (north/south/east/west/center).
33514 getPosition: function(){
33515 return this.position;
33519 * Ext JS Library 1.1.1
33520 * Copyright(c) 2006-2007, Ext JS, LLC.
33522 * Originally Released Under LGPL - original licence link has changed is not relivant.
33525 * <script type="text/javascript">
33529 * @class Roo.bootstrap.layout.Region
33530 * @extends Roo.bootstrap.layout.Basic
33531 * This class represents a region in a layout manager.
33533 * @cfg {Object} margins Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
33534 * @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})
33535 * @cfg {String} tabPosition (top|bottom) "top" or "bottom" (defaults to "bottom")
33536 * @cfg {Boolean} alwaysShowTabs True to always display tabs even when there is only 1 panel (defaults to false)
33537 * @cfg {Boolean} autoScroll True to enable overflow scrolling (defaults to false)
33538 * @cfg {Boolean} titlebar True to display a title bar (defaults to true)
33539 * @cfg {String} title The title for the region (overrides panel titles)
33540 * @cfg {Boolean} animate True to animate expand/collapse (defaults to false)
33541 * @cfg {Boolean} autoHide False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
33542 * @cfg {Boolean} preservePanels True to preserve removed panels so they can be readded later (defaults to false)
33543 * @cfg {Boolean} closeOnTab True to place the close icon on the tabs instead of the region titlebar (defaults to false)
33544 * @cfg {Boolean} hideTabs True to hide the tab strip (defaults to false)
33545 * @cfg {Boolean} resizeTabs True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
33546 * the space available, similar to FireFox 1.5 tabs (defaults to false)
33547 * @cfg {Number} minTabWidth The minimum tab width (defaults to 40)
33548 * @cfg {Number} preferredTabWidth The preferred tab width (defaults to 150)
33549 * @cfg {String} overflow (hidden|visible) if you have menus in the region, then you need to set this to visible.
33551 * @cfg {Boolean} hidden True to start the region hidden (defaults to false)
33552 * @cfg {Boolean} hideWhenEmpty True to hide the region when it has no panels
33553 * @cfg {Boolean} disableTabTips True to disable tab tooltips
33554 * @cfg {Number} width For East/West panels
33555 * @cfg {Number} height For North/South panels
33556 * @cfg {Boolean} split To show the splitter
33557 * @cfg {Boolean} toolbar xtype configuration for a toolbar - shows on right of tabbar
33559 * @cfg {string} cls Extra CSS classes to add to region
33561 * @cfg {Roo.bootstrap.layout.Manager} mgr The manager
33562 * @cfg {string} region the region that it inhabits..
33565 * @xxxcfg {Boolean} collapsible DISABLED False to disable collapsing (defaults to true)
33566 * @xxxcfg {Boolean} collapsed DISABLED True to set the initial display to collapsed (defaults to false)
33568 * @xxxcfg {String} collapsedTitle DISABLED Optional string message to display in the collapsed block of a north or south region
33569 * @xxxxcfg {Boolean} floatable DISABLED False to disable floating (defaults to true)
33570 * @xxxxcfg {Boolean} showPin True to show a pin button NOT SUPPORTED YET
33572 Roo.bootstrap.layout.Region = function(config)
33574 this.applyConfig(config);
33576 var mgr = config.mgr;
33577 var pos = config.region;
33578 config.skipConfig = true;
33579 Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
33582 this.onRender(mgr.el);
33585 this.visible = true;
33586 this.collapsed = false;
33587 this.unrendered_panels = [];
33590 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
33592 position: '', // set by wrapper (eg. north/south etc..)
33593 unrendered_panels : null, // unrendered panels.
33594 createBody : function(){
33595 /** This region's body element
33596 * @type Roo.Element */
33597 this.bodyEl = this.el.createChild({
33599 cls: "roo-layout-panel-body tab-content" // bootstrap added...
33603 onRender: function(ctr, pos)
33605 var dh = Roo.DomHelper;
33606 /** This region's container element
33607 * @type Roo.Element */
33608 this.el = dh.append(ctr.dom, {
33610 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
33612 /** This region's title element
33613 * @type Roo.Element */
33615 this.titleEl = dh.append(this.el.dom,
33618 unselectable: "on",
33619 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
33621 {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: " "},
33622 {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
33625 this.titleEl.enableDisplayMode();
33626 /** This region's title text element
33627 * @type HTMLElement */
33628 this.titleTextEl = this.titleEl.dom.firstChild;
33629 this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
33631 this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
33632 this.closeBtn.enableDisplayMode();
33633 this.closeBtn.on("click", this.closeClicked, this);
33634 this.closeBtn.hide();
33636 this.createBody(this.config);
33637 if(this.config.hideWhenEmpty){
33639 this.on("paneladded", this.validateVisibility, this);
33640 this.on("panelremoved", this.validateVisibility, this);
33642 if(this.autoScroll){
33643 this.bodyEl.setStyle("overflow", "auto");
33645 this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
33647 //if(c.titlebar !== false){
33648 if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
33649 this.titleEl.hide();
33651 this.titleEl.show();
33652 if(this.config.title){
33653 this.titleTextEl.innerHTML = this.config.title;
33657 if(this.config.collapsed){
33658 this.collapse(true);
33660 if(this.config.hidden){
33664 if (this.unrendered_panels && this.unrendered_panels.length) {
33665 for (var i =0;i< this.unrendered_panels.length; i++) {
33666 this.add(this.unrendered_panels[i]);
33668 this.unrendered_panels = null;
33674 applyConfig : function(c)
33677 *if(c.collapsible && this.position != "center" && !this.collapsedEl){
33678 var dh = Roo.DomHelper;
33679 if(c.titlebar !== false){
33680 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
33681 this.collapseBtn.on("click", this.collapse, this);
33682 this.collapseBtn.enableDisplayMode();
33684 if(c.showPin === true || this.showPin){
33685 this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
33686 this.stickBtn.enableDisplayMode();
33687 this.stickBtn.on("click", this.expand, this);
33688 this.stickBtn.hide();
33693 /** This region's collapsed element
33694 * @type Roo.Element */
33697 this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
33698 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
33701 if(c.floatable !== false){
33702 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
33703 this.collapsedEl.on("click", this.collapseClick, this);
33706 if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
33707 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
33708 id: "message", unselectable: "on", style:{"float":"left"}});
33709 this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
33711 this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
33712 this.expandBtn.on("click", this.expand, this);
33716 if(this.collapseBtn){
33717 this.collapseBtn.setVisible(c.collapsible == true);
33720 this.cmargins = c.cmargins || this.cmargins ||
33721 (this.position == "west" || this.position == "east" ?
33722 {top: 0, left: 2, right:2, bottom: 0} :
33723 {top: 2, left: 0, right:0, bottom: 2});
33725 this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
33728 this.bottomTabs = c.tabPosition != "top";
33730 this.autoScroll = c.autoScroll || false;
33735 this.duration = c.duration || .30;
33736 this.slideDuration = c.slideDuration || .45;
33741 * Returns true if this region is currently visible.
33742 * @return {Boolean}
33744 isVisible : function(){
33745 return this.visible;
33749 * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
33750 * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&#160;")
33752 //setCollapsedTitle : function(title){
33753 // title = title || " ";
33754 // if(this.collapsedTitleTextEl){
33755 // this.collapsedTitleTextEl.innerHTML = title;
33759 getBox : function(){
33761 // if(!this.collapsed){
33762 b = this.el.getBox(false, true);
33764 // b = this.collapsedEl.getBox(false, true);
33769 getMargins : function(){
33770 return this.margins;
33771 //return this.collapsed ? this.cmargins : this.margins;
33774 highlight : function(){
33775 this.el.addClass("x-layout-panel-dragover");
33778 unhighlight : function(){
33779 this.el.removeClass("x-layout-panel-dragover");
33782 updateBox : function(box)
33784 if (!this.bodyEl) {
33785 return; // not rendered yet..
33789 if(!this.collapsed){
33790 this.el.dom.style.left = box.x + "px";
33791 this.el.dom.style.top = box.y + "px";
33792 this.updateBody(box.width, box.height);
33794 this.collapsedEl.dom.style.left = box.x + "px";
33795 this.collapsedEl.dom.style.top = box.y + "px";
33796 this.collapsedEl.setSize(box.width, box.height);
33799 this.tabs.autoSizeTabs();
33803 updateBody : function(w, h)
33806 this.el.setWidth(w);
33807 w -= this.el.getBorderWidth("rl");
33808 if(this.config.adjustments){
33809 w += this.config.adjustments[0];
33812 if(h !== null && h > 0){
33813 this.el.setHeight(h);
33814 h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
33815 h -= this.el.getBorderWidth("tb");
33816 if(this.config.adjustments){
33817 h += this.config.adjustments[1];
33819 this.bodyEl.setHeight(h);
33821 h = this.tabs.syncHeight(h);
33824 if(this.panelSize){
33825 w = w !== null ? w : this.panelSize.width;
33826 h = h !== null ? h : this.panelSize.height;
33828 if(this.activePanel){
33829 var el = this.activePanel.getEl();
33830 w = w !== null ? w : el.getWidth();
33831 h = h !== null ? h : el.getHeight();
33832 this.panelSize = {width: w, height: h};
33833 this.activePanel.setSize(w, h);
33835 if(Roo.isIE && this.tabs){
33836 this.tabs.el.repaint();
33841 * Returns the container element for this region.
33842 * @return {Roo.Element}
33844 getEl : function(){
33849 * Hides this region.
33852 //if(!this.collapsed){
33853 this.el.dom.style.left = "-2000px";
33856 // this.collapsedEl.dom.style.left = "-2000px";
33857 // this.collapsedEl.hide();
33859 this.visible = false;
33860 this.fireEvent("visibilitychange", this, false);
33864 * Shows this region if it was previously hidden.
33867 //if(!this.collapsed){
33870 // this.collapsedEl.show();
33872 this.visible = true;
33873 this.fireEvent("visibilitychange", this, true);
33876 closeClicked : function(){
33877 if(this.activePanel){
33878 this.remove(this.activePanel);
33882 collapseClick : function(e){
33884 e.stopPropagation();
33887 e.stopPropagation();
33893 * Collapses this region.
33894 * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
33897 collapse : function(skipAnim, skipCheck = false){
33898 if(this.collapsed) {
33902 if(skipCheck || this.fireEvent("beforecollapse", this) != false){
33904 this.collapsed = true;
33906 this.split.el.hide();
33908 if(this.config.animate && skipAnim !== true){
33909 this.fireEvent("invalidated", this);
33910 this.animateCollapse();
33912 this.el.setLocation(-20000,-20000);
33914 this.collapsedEl.show();
33915 this.fireEvent("collapsed", this);
33916 this.fireEvent("invalidated", this);
33922 animateCollapse : function(){
33927 * Expands this region if it was previously collapsed.
33928 * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
33929 * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
33932 expand : function(e, skipAnim){
33934 e.stopPropagation();
33936 if(!this.collapsed || this.el.hasActiveFx()) {
33940 this.afterSlideIn();
33943 this.collapsed = false;
33944 if(this.config.animate && skipAnim !== true){
33945 this.animateExpand();
33949 this.split.el.show();
33951 this.collapsedEl.setLocation(-2000,-2000);
33952 this.collapsedEl.hide();
33953 this.fireEvent("invalidated", this);
33954 this.fireEvent("expanded", this);
33958 animateExpand : function(){
33962 initTabs : function()
33964 //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
33966 var ts = new Roo.bootstrap.panel.Tabs({
33967 el: this.bodyEl.dom,
33968 tabPosition: this.bottomTabs ? 'bottom' : 'top',
33969 disableTooltips: this.config.disableTabTips,
33970 toolbar : this.config.toolbar
33973 if(this.config.hideTabs){
33974 ts.stripWrap.setDisplayed(false);
33977 ts.resizeTabs = this.config.resizeTabs === true;
33978 ts.minTabWidth = this.config.minTabWidth || 40;
33979 ts.maxTabWidth = this.config.maxTabWidth || 250;
33980 ts.preferredTabWidth = this.config.preferredTabWidth || 150;
33981 ts.monitorResize = false;
33982 //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
33983 ts.bodyEl.addClass('roo-layout-tabs-body');
33984 this.panels.each(this.initPanelAsTab, this);
33987 initPanelAsTab : function(panel){
33988 var ti = this.tabs.addTab(
33992 this.config.closeOnTab && panel.isClosable(),
33995 if(panel.tabTip !== undefined){
33996 ti.setTooltip(panel.tabTip);
33998 ti.on("activate", function(){
33999 this.setActivePanel(panel);
34002 if(this.config.closeOnTab){
34003 ti.on("beforeclose", function(t, e){
34005 this.remove(panel);
34009 panel.tabItem = ti;
34014 updatePanelTitle : function(panel, title)
34016 if(this.activePanel == panel){
34017 this.updateTitle(title);
34020 var ti = this.tabs.getTab(panel.getEl().id);
34022 if(panel.tabTip !== undefined){
34023 ti.setTooltip(panel.tabTip);
34028 updateTitle : function(title){
34029 if(this.titleTextEl && !this.config.title){
34030 this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : " ");
34034 setActivePanel : function(panel)
34036 panel = this.getPanel(panel);
34037 if(this.activePanel && this.activePanel != panel){
34038 this.activePanel.setActiveState(false);
34040 this.activePanel = panel;
34041 panel.setActiveState(true);
34042 if(this.panelSize){
34043 panel.setSize(this.panelSize.width, this.panelSize.height);
34046 this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
34048 this.updateTitle(panel.getTitle());
34050 this.fireEvent("invalidated", this);
34052 this.fireEvent("panelactivated", this, panel);
34056 * Shows the specified panel.
34057 * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
34058 * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
34060 showPanel : function(panel)
34062 panel = this.getPanel(panel);
34065 var tab = this.tabs.getTab(panel.getEl().id);
34066 if(tab.isHidden()){
34067 this.tabs.unhideTab(tab.id);
34071 this.setActivePanel(panel);
34078 * Get the active panel for this region.
34079 * @return {Roo.ContentPanel} The active panel or null
34081 getActivePanel : function(){
34082 return this.activePanel;
34085 validateVisibility : function(){
34086 if(this.panels.getCount() < 1){
34087 this.updateTitle(" ");
34088 this.closeBtn.hide();
34091 if(!this.isVisible()){
34098 * Adds the passed ContentPanel(s) to this region.
34099 * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34100 * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
34102 add : function(panel)
34104 if(arguments.length > 1){
34105 for(var i = 0, len = arguments.length; i < len; i++) {
34106 this.add(arguments[i]);
34111 // if we have not been rendered yet, then we can not really do much of this..
34112 if (!this.bodyEl) {
34113 this.unrendered_panels.push(panel);
34120 if(this.hasPanel(panel)){
34121 this.showPanel(panel);
34124 panel.setRegion(this);
34125 this.panels.add(panel);
34126 /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
34127 // sinle panel - no tab...?? would it not be better to render it with the tabs,
34128 // and hide them... ???
34129 this.bodyEl.dom.appendChild(panel.getEl().dom);
34130 if(panel.background !== true){
34131 this.setActivePanel(panel);
34133 this.fireEvent("paneladded", this, panel);
34140 this.initPanelAsTab(panel);
34144 if(panel.background !== true){
34145 this.tabs.activate(panel.getEl().id);
34147 this.fireEvent("paneladded", this, panel);
34152 * Hides the tab for the specified panel.
34153 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34155 hidePanel : function(panel){
34156 if(this.tabs && (panel = this.getPanel(panel))){
34157 this.tabs.hideTab(panel.getEl().id);
34162 * Unhides the tab for a previously hidden panel.
34163 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34165 unhidePanel : function(panel){
34166 if(this.tabs && (panel = this.getPanel(panel))){
34167 this.tabs.unhideTab(panel.getEl().id);
34171 clearPanels : function(){
34172 while(this.panels.getCount() > 0){
34173 this.remove(this.panels.first());
34178 * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34179 * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
34180 * @param {Boolean} preservePanel Overrides the config preservePanel option
34181 * @return {Roo.ContentPanel} The panel that was removed
34183 remove : function(panel, preservePanel)
34185 panel = this.getPanel(panel);
34190 this.fireEvent("beforeremove", this, panel, e);
34191 if(e.cancel === true){
34194 preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
34195 var panelId = panel.getId();
34196 this.panels.removeKey(panelId);
34198 document.body.appendChild(panel.getEl().dom);
34201 this.tabs.removeTab(panel.getEl().id);
34202 }else if (!preservePanel){
34203 this.bodyEl.dom.removeChild(panel.getEl().dom);
34205 if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
34206 var p = this.panels.first();
34207 var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
34208 tempEl.appendChild(p.getEl().dom);
34209 this.bodyEl.update("");
34210 this.bodyEl.dom.appendChild(p.getEl().dom);
34212 this.updateTitle(p.getTitle());
34214 this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
34215 this.setActivePanel(p);
34217 panel.setRegion(null);
34218 if(this.activePanel == panel){
34219 this.activePanel = null;
34221 if(this.config.autoDestroy !== false && preservePanel !== true){
34222 try{panel.destroy();}catch(e){}
34224 this.fireEvent("panelremoved", this, panel);
34229 * Returns the TabPanel component used by this region
34230 * @return {Roo.TabPanel}
34232 getTabs : function(){
34236 createTool : function(parentEl, className){
34237 var btn = Roo.DomHelper.append(parentEl, {
34239 cls: "x-layout-tools-button",
34242 cls: "roo-layout-tools-button-inner " + className,
34246 btn.addClassOnOver("roo-layout-tools-button-over");
34251 * Ext JS Library 1.1.1
34252 * Copyright(c) 2006-2007, Ext JS, LLC.
34254 * Originally Released Under LGPL - original licence link has changed is not relivant.
34257 * <script type="text/javascript">
34263 * @class Roo.SplitLayoutRegion
34264 * @extends Roo.LayoutRegion
34265 * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
34267 Roo.bootstrap.layout.Split = function(config){
34268 this.cursor = config.cursor;
34269 Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
34272 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
34274 splitTip : "Drag to resize.",
34275 collapsibleSplitTip : "Drag to resize. Double click to hide.",
34276 useSplitTips : false,
34278 applyConfig : function(config){
34279 Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
34282 onRender : function(ctr,pos) {
34284 Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
34285 if(!this.config.split){
34290 var splitEl = Roo.DomHelper.append(ctr.dom, {
34292 id: this.el.id + "-split",
34293 cls: "roo-layout-split roo-layout-split-"+this.position,
34296 /** The SplitBar for this region
34297 * @type Roo.SplitBar */
34298 // does not exist yet...
34299 Roo.log([this.position, this.orientation]);
34301 this.split = new Roo.bootstrap.SplitBar({
34302 dragElement : splitEl,
34303 resizingElement: this.el,
34304 orientation : this.orientation
34307 this.split.on("moved", this.onSplitMove, this);
34308 this.split.useShim = this.config.useShim === true;
34309 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
34310 if(this.useSplitTips){
34311 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
34313 //if(config.collapsible){
34314 // this.split.el.on("dblclick", this.collapse, this);
34317 if(typeof this.config.minSize != "undefined"){
34318 this.split.minSize = this.config.minSize;
34320 if(typeof this.config.maxSize != "undefined"){
34321 this.split.maxSize = this.config.maxSize;
34323 if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
34324 this.hideSplitter();
34329 getHMaxSize : function(){
34330 var cmax = this.config.maxSize || 10000;
34331 var center = this.mgr.getRegion("center");
34332 return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
34335 getVMaxSize : function(){
34336 var cmax = this.config.maxSize || 10000;
34337 var center = this.mgr.getRegion("center");
34338 return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
34341 onSplitMove : function(split, newSize){
34342 this.fireEvent("resized", this, newSize);
34346 * Returns the {@link Roo.SplitBar} for this region.
34347 * @return {Roo.SplitBar}
34349 getSplitBar : function(){
34354 this.hideSplitter();
34355 Roo.bootstrap.layout.Split.superclass.hide.call(this);
34358 hideSplitter : function(){
34360 this.split.el.setLocation(-2000,-2000);
34361 this.split.el.hide();
34367 this.split.el.show();
34369 Roo.bootstrap.layout.Split.superclass.show.call(this);
34372 beforeSlide: function(){
34373 if(Roo.isGecko){// firefox overflow auto bug workaround
34374 this.bodyEl.clip();
34376 this.tabs.bodyEl.clip();
34378 if(this.activePanel){
34379 this.activePanel.getEl().clip();
34381 if(this.activePanel.beforeSlide){
34382 this.activePanel.beforeSlide();
34388 afterSlide : function(){
34389 if(Roo.isGecko){// firefox overflow auto bug workaround
34390 this.bodyEl.unclip();
34392 this.tabs.bodyEl.unclip();
34394 if(this.activePanel){
34395 this.activePanel.getEl().unclip();
34396 if(this.activePanel.afterSlide){
34397 this.activePanel.afterSlide();
34403 initAutoHide : function(){
34404 if(this.autoHide !== false){
34405 if(!this.autoHideHd){
34406 var st = new Roo.util.DelayedTask(this.slideIn, this);
34407 this.autoHideHd = {
34408 "mouseout": function(e){
34409 if(!e.within(this.el, true)){
34413 "mouseover" : function(e){
34419 this.el.on(this.autoHideHd);
34423 clearAutoHide : function(){
34424 if(this.autoHide !== false){
34425 this.el.un("mouseout", this.autoHideHd.mouseout);
34426 this.el.un("mouseover", this.autoHideHd.mouseover);
34430 clearMonitor : function(){
34431 Roo.get(document).un("click", this.slideInIf, this);
34434 // these names are backwards but not changed for compat
34435 slideOut : function(){
34436 if(this.isSlid || this.el.hasActiveFx()){
34439 this.isSlid = true;
34440 if(this.collapseBtn){
34441 this.collapseBtn.hide();
34443 this.closeBtnState = this.closeBtn.getStyle('display');
34444 this.closeBtn.hide();
34446 this.stickBtn.show();
34449 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
34450 this.beforeSlide();
34451 this.el.setStyle("z-index", 10001);
34452 this.el.slideIn(this.getSlideAnchor(), {
34453 callback: function(){
34455 this.initAutoHide();
34456 Roo.get(document).on("click", this.slideInIf, this);
34457 this.fireEvent("slideshow", this);
34464 afterSlideIn : function(){
34465 this.clearAutoHide();
34466 this.isSlid = false;
34467 this.clearMonitor();
34468 this.el.setStyle("z-index", "");
34469 if(this.collapseBtn){
34470 this.collapseBtn.show();
34472 this.closeBtn.setStyle('display', this.closeBtnState);
34474 this.stickBtn.hide();
34476 this.fireEvent("slidehide", this);
34479 slideIn : function(cb){
34480 if(!this.isSlid || this.el.hasActiveFx()){
34484 this.isSlid = false;
34485 this.beforeSlide();
34486 this.el.slideOut(this.getSlideAnchor(), {
34487 callback: function(){
34488 this.el.setLeftTop(-10000, -10000);
34490 this.afterSlideIn();
34498 slideInIf : function(e){
34499 if(!e.within(this.el)){
34504 animateCollapse : function(){
34505 this.beforeSlide();
34506 this.el.setStyle("z-index", 20000);
34507 var anchor = this.getSlideAnchor();
34508 this.el.slideOut(anchor, {
34509 callback : function(){
34510 this.el.setStyle("z-index", "");
34511 this.collapsedEl.slideIn(anchor, {duration:.3});
34513 this.el.setLocation(-10000,-10000);
34515 this.fireEvent("collapsed", this);
34522 animateExpand : function(){
34523 this.beforeSlide();
34524 this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
34525 this.el.setStyle("z-index", 20000);
34526 this.collapsedEl.hide({
34529 this.el.slideIn(this.getSlideAnchor(), {
34530 callback : function(){
34531 this.el.setStyle("z-index", "");
34534 this.split.el.show();
34536 this.fireEvent("invalidated", this);
34537 this.fireEvent("expanded", this);
34565 getAnchor : function(){
34566 return this.anchors[this.position];
34569 getCollapseAnchor : function(){
34570 return this.canchors[this.position];
34573 getSlideAnchor : function(){
34574 return this.sanchors[this.position];
34577 getAlignAdj : function(){
34578 var cm = this.cmargins;
34579 switch(this.position){
34595 getExpandAdj : function(){
34596 var c = this.collapsedEl, cm = this.cmargins;
34597 switch(this.position){
34599 return [-(cm.right+c.getWidth()+cm.left), 0];
34602 return [cm.right+c.getWidth()+cm.left, 0];
34605 return [0, -(cm.top+cm.bottom+c.getHeight())];
34608 return [0, cm.top+cm.bottom+c.getHeight()];
34614 * Ext JS Library 1.1.1
34615 * Copyright(c) 2006-2007, Ext JS, LLC.
34617 * Originally Released Under LGPL - original licence link has changed is not relivant.
34620 * <script type="text/javascript">
34623 * These classes are private internal classes
34625 Roo.bootstrap.layout.Center = function(config){
34626 config.region = "center";
34627 Roo.bootstrap.layout.Region.call(this, config);
34628 this.visible = true;
34629 this.minWidth = config.minWidth || 20;
34630 this.minHeight = config.minHeight || 20;
34633 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
34635 // center panel can't be hidden
34639 // center panel can't be hidden
34642 getMinWidth: function(){
34643 return this.minWidth;
34646 getMinHeight: function(){
34647 return this.minHeight;
34660 Roo.bootstrap.layout.North = function(config)
34662 config.region = 'north';
34663 config.cursor = 'n-resize';
34665 Roo.bootstrap.layout.Split.call(this, config);
34669 this.split.placement = Roo.bootstrap.SplitBar.TOP;
34670 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34671 this.split.el.addClass("roo-layout-split-v");
34673 var size = config.initialSize || config.height;
34674 if(typeof size != "undefined"){
34675 this.el.setHeight(size);
34678 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
34680 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34684 getBox : function(){
34685 if(this.collapsed){
34686 return this.collapsedEl.getBox();
34688 var box = this.el.getBox();
34690 box.height += this.split.el.getHeight();
34695 updateBox : function(box){
34696 if(this.split && !this.collapsed){
34697 box.height -= this.split.el.getHeight();
34698 this.split.el.setLeft(box.x);
34699 this.split.el.setTop(box.y+box.height);
34700 this.split.el.setWidth(box.width);
34702 if(this.collapsed){
34703 this.updateBody(box.width, null);
34705 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34713 Roo.bootstrap.layout.South = function(config){
34714 config.region = 'south';
34715 config.cursor = 's-resize';
34716 Roo.bootstrap.layout.Split.call(this, config);
34718 this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
34719 this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
34720 this.split.el.addClass("roo-layout-split-v");
34722 var size = config.initialSize || config.height;
34723 if(typeof size != "undefined"){
34724 this.el.setHeight(size);
34728 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
34729 orientation: Roo.bootstrap.SplitBar.VERTICAL,
34730 getBox : function(){
34731 if(this.collapsed){
34732 return this.collapsedEl.getBox();
34734 var box = this.el.getBox();
34736 var sh = this.split.el.getHeight();
34743 updateBox : function(box){
34744 if(this.split && !this.collapsed){
34745 var sh = this.split.el.getHeight();
34748 this.split.el.setLeft(box.x);
34749 this.split.el.setTop(box.y-sh);
34750 this.split.el.setWidth(box.width);
34752 if(this.collapsed){
34753 this.updateBody(box.width, null);
34755 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34759 Roo.bootstrap.layout.East = function(config){
34760 config.region = "east";
34761 config.cursor = "e-resize";
34762 Roo.bootstrap.layout.Split.call(this, config);
34764 this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
34765 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34766 this.split.el.addClass("roo-layout-split-h");
34768 var size = config.initialSize || config.width;
34769 if(typeof size != "undefined"){
34770 this.el.setWidth(size);
34773 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
34774 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34775 getBox : function(){
34776 if(this.collapsed){
34777 return this.collapsedEl.getBox();
34779 var box = this.el.getBox();
34781 var sw = this.split.el.getWidth();
34788 updateBox : function(box){
34789 if(this.split && !this.collapsed){
34790 var sw = this.split.el.getWidth();
34792 this.split.el.setLeft(box.x);
34793 this.split.el.setTop(box.y);
34794 this.split.el.setHeight(box.height);
34797 if(this.collapsed){
34798 this.updateBody(null, box.height);
34800 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34804 Roo.bootstrap.layout.West = function(config){
34805 config.region = "west";
34806 config.cursor = "w-resize";
34808 Roo.bootstrap.layout.Split.call(this, config);
34810 this.split.placement = Roo.bootstrap.SplitBar.LEFT;
34811 this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
34812 this.split.el.addClass("roo-layout-split-h");
34816 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
34817 orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
34819 onRender: function(ctr, pos)
34821 Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
34822 var size = this.config.initialSize || this.config.width;
34823 if(typeof size != "undefined"){
34824 this.el.setWidth(size);
34828 getBox : function(){
34829 if(this.collapsed){
34830 return this.collapsedEl.getBox();
34832 var box = this.el.getBox();
34834 box.width += this.split.el.getWidth();
34839 updateBox : function(box){
34840 if(this.split && !this.collapsed){
34841 var sw = this.split.el.getWidth();
34843 this.split.el.setLeft(box.x+box.width);
34844 this.split.el.setTop(box.y);
34845 this.split.el.setHeight(box.height);
34847 if(this.collapsed){
34848 this.updateBody(null, box.height);
34850 Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
34853 Roo.namespace("Roo.bootstrap.panel");/*
34855 * Ext JS Library 1.1.1
34856 * Copyright(c) 2006-2007, Ext JS, LLC.
34858 * Originally Released Under LGPL - original licence link has changed is not relivant.
34861 * <script type="text/javascript">
34864 * @class Roo.ContentPanel
34865 * @extends Roo.util.Observable
34866 * A basic ContentPanel element.
34867 * @cfg {Boolean} fitToFrame True for this panel to adjust its size to fit when the region resizes (defaults to false)
34868 * @cfg {Boolean} fitContainer When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container (defaults to false)
34869 * @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
34870 * @cfg {Boolean} closable True if the panel can be closed/removed
34871 * @cfg {Boolean} background True if the panel should not be activated when it is added (defaults to false)
34872 * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
34873 * @cfg {Toolbar} toolbar A toolbar for this panel
34874 * @cfg {Boolean} autoScroll True to scroll overflow in this panel (use with {@link #fitToFrame})
34875 * @cfg {String} title The title for this panel
34876 * @cfg {Array} adjustments Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
34877 * @cfg {String} url Calls {@link #setUrl} with this value
34878 * @cfg {String} region (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
34879 * @cfg {String/Object} params When used with {@link #url}, calls {@link #setUrl} with this value
34880 * @cfg {Boolean} loadOnce When used with {@link #url}, calls {@link #setUrl} with this value
34881 * @cfg {String} content Raw content to fill content panel with (uses setContent on construction.)
34882 * @cfg {Boolean} badges render the badges
34885 * Create a new ContentPanel.
34886 * @param {String/HTMLElement/Roo.Element} el The container element for this panel
34887 * @param {String/Object} config A string to set only the title or a config object
34888 * @param {String} content (optional) Set the HTML content for this panel
34889 * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
34891 Roo.bootstrap.panel.Content = function( config){
34893 this.tpl = config.tpl || false;
34895 var el = config.el;
34896 var content = config.content;
34898 if(config.autoCreate){ // xtype is available if this is called from factory
34901 this.el = Roo.get(el);
34902 if(!this.el && config && config.autoCreate){
34903 if(typeof config.autoCreate == "object"){
34904 if(!config.autoCreate.id){
34905 config.autoCreate.id = config.id||el;
34907 this.el = Roo.DomHelper.append(document.body,
34908 config.autoCreate, true);
34910 var elcfg = { tag: "div",
34911 cls: "roo-layout-inactive-content",
34915 elcfg.html = config.html;
34919 this.el = Roo.DomHelper.append(document.body, elcfg , true);
34922 this.closable = false;
34923 this.loaded = false;
34924 this.active = false;
34927 if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
34929 this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
34931 this.wrapEl = this.el; //this.el.wrap();
34933 if (config.toolbar.items) {
34934 ti = config.toolbar.items ;
34935 delete config.toolbar.items ;
34939 this.toolbar.render(this.wrapEl, 'before');
34940 for(var i =0;i < ti.length;i++) {
34941 // Roo.log(['add child', items[i]]);
34942 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
34944 this.toolbar.items = nitems;
34945 this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
34946 delete config.toolbar;
34950 // xtype created footer. - not sure if will work as we normally have to render first..
34951 if (this.footer && !this.footer.el && this.footer.xtype) {
34952 if (!this.wrapEl) {
34953 this.wrapEl = this.el.wrap();
34956 this.footer.container = this.wrapEl.createChild();
34958 this.footer = Roo.factory(this.footer, Roo);
34963 if(typeof config == "string"){
34964 this.title = config;
34966 Roo.apply(this, config);
34970 this.resizeEl = Roo.get(this.resizeEl, true);
34972 this.resizeEl = this.el;
34974 // handle view.xtype
34982 * Fires when this panel is activated.
34983 * @param {Roo.ContentPanel} this
34987 * @event deactivate
34988 * Fires when this panel is activated.
34989 * @param {Roo.ContentPanel} this
34991 "deactivate" : true,
34995 * Fires when this panel is resized if fitToFrame is true.
34996 * @param {Roo.ContentPanel} this
34997 * @param {Number} width The width after any component adjustments
34998 * @param {Number} height The height after any component adjustments
35004 * Fires when this tab is created
35005 * @param {Roo.ContentPanel} this
35016 if(this.autoScroll){
35017 this.resizeEl.setStyle("overflow", "auto");
35019 // fix randome scrolling
35020 //this.el.on('scroll', function() {
35021 // Roo.log('fix random scolling');
35022 // this.scrollTo('top',0);
35025 content = content || this.content;
35027 this.setContent(content);
35029 if(config && config.url){
35030 this.setUrl(this.url, this.params, this.loadOnce);
35035 Roo.bootstrap.panel.Content.superclass.constructor.call(this);
35037 if (this.view && typeof(this.view.xtype) != 'undefined') {
35038 this.view.el = this.el.appendChild(document.createElement("div"));
35039 this.view = Roo.factory(this.view);
35040 this.view.render && this.view.render(false, '');
35044 this.fireEvent('render', this);
35047 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
35051 setRegion : function(region){
35052 this.region = region;
35053 this.setActiveClass(region && !this.background);
35057 setActiveClass: function(state)
35060 this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
35061 this.el.setStyle('position','relative');
35063 this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
35064 this.el.setStyle('position', 'absolute');
35069 * Returns the toolbar for this Panel if one was configured.
35070 * @return {Roo.Toolbar}
35072 getToolbar : function(){
35073 return this.toolbar;
35076 setActiveState : function(active)
35078 this.active = active;
35079 this.setActiveClass(active);
35081 this.fireEvent("deactivate", this);
35083 this.fireEvent("activate", this);
35087 * Updates this panel's element
35088 * @param {String} content The new content
35089 * @param {Boolean} loadScripts (optional) true to look for and process scripts
35091 setContent : function(content, loadScripts){
35092 this.el.update(content, loadScripts);
35095 ignoreResize : function(w, h){
35096 if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
35099 this.lastSize = {width: w, height: h};
35104 * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
35105 * @return {Roo.UpdateManager} The UpdateManager
35107 getUpdateManager : function(){
35108 return this.el.getUpdateManager();
35111 * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
35112 * @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:
35115 url: "your-url.php",
35116 params: {param1: "foo", param2: "bar"}, // or a URL encoded string
35117 callback: yourFunction,
35118 scope: yourObject, //(optional scope)
35121 text: "Loading...",
35126 * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
35127 * 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.
35128 * @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}
35129 * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
35130 * @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.
35131 * @return {Roo.ContentPanel} this
35134 var um = this.el.getUpdateManager();
35135 um.update.apply(um, arguments);
35141 * 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.
35142 * @param {String/Function} url The URL to load the content from or a function to call to get the URL
35143 * @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)
35144 * @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)
35145 * @return {Roo.UpdateManager} The UpdateManager
35147 setUrl : function(url, params, loadOnce){
35148 if(this.refreshDelegate){
35149 this.removeListener("activate", this.refreshDelegate);
35151 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
35152 this.on("activate", this.refreshDelegate);
35153 return this.el.getUpdateManager();
35156 _handleRefresh : function(url, params, loadOnce){
35157 if(!loadOnce || !this.loaded){
35158 var updater = this.el.getUpdateManager();
35159 updater.update(url, params, this._setLoaded.createDelegate(this));
35163 _setLoaded : function(){
35164 this.loaded = true;
35168 * Returns this panel's id
35171 getId : function(){
35176 * Returns this panel's element - used by regiosn to add.
35177 * @return {Roo.Element}
35179 getEl : function(){
35180 return this.wrapEl || this.el;
35185 adjustForComponents : function(width, height)
35187 //Roo.log('adjustForComponents ');
35188 if(this.resizeEl != this.el){
35189 width -= this.el.getFrameWidth('lr');
35190 height -= this.el.getFrameWidth('tb');
35193 var te = this.toolbar.getEl();
35194 height -= te.getHeight();
35195 te.setWidth(width);
35198 var te = this.footer.getEl();
35199 Roo.log("footer:" + te.getHeight());
35201 height -= te.getHeight();
35202 te.setWidth(width);
35206 if(this.adjustments){
35207 width += this.adjustments[0];
35208 height += this.adjustments[1];
35210 return {"width": width, "height": height};
35213 setSize : function(width, height){
35214 if(this.fitToFrame && !this.ignoreResize(width, height)){
35215 if(this.fitContainer && this.resizeEl != this.el){
35216 this.el.setSize(width, height);
35218 var size = this.adjustForComponents(width, height);
35219 this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
35220 this.fireEvent('resize', this, size.width, size.height);
35225 * Returns this panel's title
35228 getTitle : function(){
35233 * Set this panel's title
35234 * @param {String} title
35236 setTitle : function(title){
35237 this.title = title;
35239 this.region.updatePanelTitle(this, title);
35244 * Returns true is this panel was configured to be closable
35245 * @return {Boolean}
35247 isClosable : function(){
35248 return this.closable;
35251 beforeSlide : function(){
35253 this.resizeEl.clip();
35256 afterSlide : function(){
35258 this.resizeEl.unclip();
35262 * Force a content refresh from the URL specified in the {@link #setUrl} method.
35263 * Will fail silently if the {@link #setUrl} method has not been called.
35264 * This does not activate the panel, just updates its content.
35266 refresh : function(){
35267 if(this.refreshDelegate){
35268 this.loaded = false;
35269 this.refreshDelegate();
35274 * Destroys this panel
35276 destroy : function(){
35277 this.el.removeAllListeners();
35278 var tempEl = document.createElement("span");
35279 tempEl.appendChild(this.el.dom);
35280 tempEl.innerHTML = "";
35286 * form - if the content panel contains a form - this is a reference to it.
35287 * @type {Roo.form.Form}
35291 * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
35292 * This contains a reference to it.
35298 * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
35308 * @param {Object} cfg Xtype definition of item to add.
35312 getChildContainer: function () {
35313 return this.getEl();
35318 var ret = new Roo.factory(cfg);
35323 if (cfg.xtype.match(/^Form$/)) {
35326 //if (this.footer) {
35327 // el = this.footer.container.insertSibling(false, 'before');
35329 el = this.el.createChild();
35332 this.form = new Roo.form.Form(cfg);
35335 if ( this.form.allItems.length) {
35336 this.form.render(el.dom);
35340 // should only have one of theses..
35341 if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
35342 // views.. should not be just added - used named prop 'view''
35344 cfg.el = this.el.appendChild(document.createElement("div"));
35347 var ret = new Roo.factory(cfg);
35349 ret.render && ret.render(false, ''); // render blank..
35359 * @class Roo.bootstrap.panel.Grid
35360 * @extends Roo.bootstrap.panel.Content
35362 * Create a new GridPanel.
35363 * @cfg {Roo.bootstrap.Table} grid The grid for this panel
35364 * @param {Object} config A the config object
35370 Roo.bootstrap.panel.Grid = function(config)
35374 this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
35375 {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
35377 config.el = this.wrapper;
35378 //this.el = this.wrapper;
35380 if (config.container) {
35381 // ctor'ed from a Border/panel.grid
35384 this.wrapper.setStyle("overflow", "hidden");
35385 this.wrapper.addClass('roo-grid-container');
35390 if(config.toolbar){
35391 var tool_el = this.wrapper.createChild();
35392 this.toolbar = Roo.factory(config.toolbar);
35394 if (config.toolbar.items) {
35395 ti = config.toolbar.items ;
35396 delete config.toolbar.items ;
35400 this.toolbar.render(tool_el);
35401 for(var i =0;i < ti.length;i++) {
35402 // Roo.log(['add child', items[i]]);
35403 nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
35405 this.toolbar.items = nitems;
35407 delete config.toolbar;
35410 Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
35411 config.grid.scrollBody = true;;
35412 config.grid.monitorWindowResize = false; // turn off autosizing
35413 config.grid.autoHeight = false;
35414 config.grid.autoWidth = false;
35416 this.grid = new config.grid.xns[config.grid.xtype](config.grid);
35418 if (config.background) {
35419 // render grid on panel activation (if panel background)
35420 this.on('activate', function(gp) {
35421 if (!gp.grid.rendered) {
35422 gp.grid.render(this.wrapper);
35423 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35428 this.grid.render(this.wrapper);
35429 this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");
35432 //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
35433 // ??? needed ??? config.el = this.wrapper;
35438 // xtype created footer. - not sure if will work as we normally have to render first..
35439 if (this.footer && !this.footer.el && this.footer.xtype) {
35441 var ctr = this.grid.getView().getFooterPanel(true);
35442 this.footer.dataSource = this.grid.dataSource;
35443 this.footer = Roo.factory(this.footer, Roo);
35444 this.footer.render(ctr);
35454 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
35455 getId : function(){
35456 return this.grid.id;
35460 * Returns the grid for this panel
35461 * @return {Roo.bootstrap.Table}
35463 getGrid : function(){
35467 setSize : function(width, height){
35468 if(!this.ignoreResize(width, height)){
35469 var grid = this.grid;
35470 var size = this.adjustForComponents(width, height);
35471 var gridel = grid.getGridEl();
35472 gridel.setSize(size.width, size.height);
35474 var thd = grid.getGridEl().select('thead',true).first();
35475 var tbd = grid.getGridEl().select('tbody', true).first();
35477 tbd.setSize(width, height - thd.getHeight());
35486 beforeSlide : function(){
35487 this.grid.getView().scroller.clip();
35490 afterSlide : function(){
35491 this.grid.getView().scroller.unclip();
35494 destroy : function(){
35495 this.grid.destroy();
35497 Roo.bootstrap.panel.Grid.superclass.destroy.call(this);
35502 * @class Roo.bootstrap.panel.Nest
35503 * @extends Roo.bootstrap.panel.Content
35505 * Create a new Panel, that can contain a layout.Border.
35508 * @param {Roo.BorderLayout} layout The layout for this panel
35509 * @param {String/Object} config A string to set only the title or a config object
35511 Roo.bootstrap.panel.Nest = function(config)
35513 // construct with only one argument..
35514 /* FIXME - implement nicer consturctors
35515 if (layout.layout) {
35517 layout = config.layout;
35518 delete config.layout;
35520 if (layout.xtype && !layout.getEl) {
35521 // then layout needs constructing..
35522 layout = Roo.factory(layout, Roo);
35526 config.el = config.layout.getEl();
35528 Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
35530 config.layout.monitorWindowResize = false; // turn off autosizing
35531 this.layout = config.layout;
35532 this.layout.getEl().addClass("roo-layout-nested-layout");
35539 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
35541 setSize : function(width, height){
35542 if(!this.ignoreResize(width, height)){
35543 var size = this.adjustForComponents(width, height);
35544 var el = this.layout.getEl();
35545 if (size.height < 1) {
35546 el.setWidth(size.width);
35548 el.setSize(size.width, size.height);
35550 var touch = el.dom.offsetWidth;
35551 this.layout.layout();
35552 // ie requires a double layout on the first pass
35553 if(Roo.isIE && !this.initialized){
35554 this.initialized = true;
35555 this.layout.layout();
35560 // activate all subpanels if not currently active..
35562 setActiveState : function(active){
35563 this.active = active;
35564 this.setActiveClass(active);
35567 this.fireEvent("deactivate", this);
35571 this.fireEvent("activate", this);
35572 // not sure if this should happen before or after..
35573 if (!this.layout) {
35574 return; // should not happen..
35577 for (var r in this.layout.regions) {
35578 reg = this.layout.getRegion(r);
35579 if (reg.getActivePanel()) {
35580 //reg.showPanel(reg.getActivePanel()); // force it to activate..
35581 reg.setActivePanel(reg.getActivePanel());
35584 if (!reg.panels.length) {
35587 reg.showPanel(reg.getPanel(0));
35596 * Returns the nested BorderLayout for this panel
35597 * @return {Roo.BorderLayout}
35599 getLayout : function(){
35600 return this.layout;
35604 * Adds a xtype elements to the layout of the nested panel
35608 xtype : 'ContentPanel',
35615 xtype : 'NestedLayoutPanel',
35621 items : [ ... list of content panels or nested layout panels.. ]
35625 * @param {Object} cfg Xtype definition of item to add.
35627 addxtype : function(cfg) {
35628 return this.layout.addxtype(cfg);
35633 * Ext JS Library 1.1.1
35634 * Copyright(c) 2006-2007, Ext JS, LLC.
35636 * Originally Released Under LGPL - original licence link has changed is not relivant.
35639 * <script type="text/javascript">
35642 * @class Roo.TabPanel
35643 * @extends Roo.util.Observable
35644 * A lightweight tab container.
35648 // basic tabs 1, built from existing content
35649 var tabs = new Roo.TabPanel("tabs1");
35650 tabs.addTab("script", "View Script");
35651 tabs.addTab("markup", "View Markup");
35652 tabs.activate("script");
35654 // more advanced tabs, built from javascript
35655 var jtabs = new Roo.TabPanel("jtabs");
35656 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
35658 // set up the UpdateManager
35659 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
35660 var updater = tab2.getUpdateManager();
35661 updater.setDefaultUrl("ajax1.htm");
35662 tab2.on('activate', updater.refresh, updater, true);
35664 // Use setUrl for Ajax loading
35665 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
35666 tab3.setUrl("ajax2.htm", null, true);
35669 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
35672 jtabs.activate("jtabs-1");
35675 * Create a new TabPanel.
35676 * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
35677 * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
35679 Roo.bootstrap.panel.Tabs = function(config){
35681 * The container element for this TabPanel.
35682 * @type Roo.Element
35684 this.el = Roo.get(config.el);
35687 if(typeof config == "boolean"){
35688 this.tabPosition = config ? "bottom" : "top";
35690 Roo.apply(this, config);
35694 if(this.tabPosition == "bottom"){
35695 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35696 this.el.addClass("roo-tabs-bottom");
35698 this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
35699 this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
35700 this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
35702 Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
35704 if(this.tabPosition != "bottom"){
35705 /** The body element that contains {@link Roo.TabPanelItem} bodies. +
35706 * @type Roo.Element
35708 this.bodyEl = Roo.get(this.createBody(this.el.dom));
35709 this.el.addClass("roo-tabs-top");
35713 this.bodyEl.setStyle("position", "relative");
35715 this.active = null;
35716 this.activateDelegate = this.activate.createDelegate(this);
35721 * Fires when the active tab changes
35722 * @param {Roo.TabPanel} this
35723 * @param {Roo.TabPanelItem} activePanel The new active tab
35727 * @event beforetabchange
35728 * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
35729 * @param {Roo.TabPanel} this
35730 * @param {Object} e Set cancel to true on this object to cancel the tab change
35731 * @param {Roo.TabPanelItem} tab The tab being changed to
35733 "beforetabchange" : true
35736 Roo.EventManager.onWindowResize(this.onResize, this);
35737 this.cpad = this.el.getPadding("lr");
35738 this.hiddenCount = 0;
35741 // toolbar on the tabbar support...
35742 if (this.toolbar) {
35743 alert("no toolbar support yet");
35744 this.toolbar = false;
35746 var tcfg = this.toolbar;
35747 tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');
35748 this.toolbar = new Roo.Toolbar(tcfg);
35749 if (Roo.isSafari) {
35750 var tbl = tcfg.container.child('table', true);
35751 tbl.setAttribute('width', '100%');
35759 Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
35762 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
35764 *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
35766 tabPosition : "top",
35768 *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
35770 currentTabWidth : 0,
35772 *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
35776 *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
35780 *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
35782 preferredTabWidth : 175,
35784 *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
35786 resizeTabs : false,
35788 *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
35790 monitorResize : true,
35792 *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar.
35797 * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
35798 * @param {String} id The id of the div to use <b>or create</b>
35799 * @param {String} text The text for the tab
35800 * @param {String} content (optional) Content to put in the TabPanelItem body
35801 * @param {Boolean} closable (optional) True to create a close icon on the tab
35802 * @return {Roo.TabPanelItem} The created TabPanelItem
35804 addTab : function(id, text, content, closable, tpl)
35806 var item = new Roo.bootstrap.panel.TabItem({
35810 closable : closable,
35813 this.addTabItem(item);
35815 item.setContent(content);
35821 * Returns the {@link Roo.TabPanelItem} with the specified id/index
35822 * @param {String/Number} id The id or index of the TabPanelItem to fetch.
35823 * @return {Roo.TabPanelItem}
35825 getTab : function(id){
35826 return this.items[id];
35830 * Hides the {@link Roo.TabPanelItem} with the specified id/index
35831 * @param {String/Number} id The id or index of the TabPanelItem to hide.
35833 hideTab : function(id){
35834 var t = this.items[id];
35837 this.hiddenCount++;
35838 this.autoSizeTabs();
35843 * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
35844 * @param {String/Number} id The id or index of the TabPanelItem to unhide.
35846 unhideTab : function(id){
35847 var t = this.items[id];
35849 t.setHidden(false);
35850 this.hiddenCount--;
35851 this.autoSizeTabs();
35856 * Adds an existing {@link Roo.TabPanelItem}.
35857 * @param {Roo.TabPanelItem} item The TabPanelItem to add
35859 addTabItem : function(item){
35860 this.items[item.id] = item;
35861 this.items.push(item);
35862 // if(this.resizeTabs){
35863 // item.setWidth(this.currentTabWidth || this.preferredTabWidth);
35864 // this.autoSizeTabs();
35866 // item.autoSize();
35871 * Removes a {@link Roo.TabPanelItem}.
35872 * @param {String/Number} id The id or index of the TabPanelItem to remove.
35874 removeTab : function(id){
35875 var items = this.items;
35876 var tab = items[id];
35877 if(!tab) { return; }
35878 var index = items.indexOf(tab);
35879 if(this.active == tab && items.length > 1){
35880 var newTab = this.getNextAvailable(index);
35885 this.stripEl.dom.removeChild(tab.pnode.dom);
35886 if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
35887 this.bodyEl.dom.removeChild(tab.bodyEl.dom);
35889 items.splice(index, 1);
35890 delete this.items[tab.id];
35891 tab.fireEvent("close", tab);
35892 tab.purgeListeners();
35893 this.autoSizeTabs();
35896 getNextAvailable : function(start){
35897 var items = this.items;
35899 // look for a next tab that will slide over to
35900 // replace the one being removed
35901 while(index < items.length){
35902 var item = items[++index];
35903 if(item && !item.isHidden()){
35907 // if one isn't found select the previous tab (on the left)
35910 var item = items[--index];
35911 if(item && !item.isHidden()){
35919 * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
35920 * @param {String/Number} id The id or index of the TabPanelItem to disable.
35922 disableTab : function(id){
35923 var tab = this.items[id];
35924 if(tab && this.active != tab){
35930 * Enables a {@link Roo.TabPanelItem} that is disabled.
35931 * @param {String/Number} id The id or index of the TabPanelItem to enable.
35933 enableTab : function(id){
35934 var tab = this.items[id];
35939 * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
35940 * @param {String/Number} id The id or index of the TabPanelItem to activate.
35941 * @return {Roo.TabPanelItem} The TabPanelItem.
35943 activate : function(id){
35944 var tab = this.items[id];
35948 if(tab == this.active || tab.disabled){
35952 this.fireEvent("beforetabchange", this, e, tab);
35953 if(e.cancel !== true && !tab.disabled){
35955 this.active.hide();
35957 this.active = this.items[id];
35958 this.active.show();
35959 this.fireEvent("tabchange", this, this.active);
35965 * Gets the active {@link Roo.TabPanelItem}.
35966 * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
35968 getActiveTab : function(){
35969 return this.active;
35973 * Updates the tab body element to fit the height of the container element
35974 * for overflow scrolling
35975 * @param {Number} targetHeight (optional) Override the starting height from the elements height
35977 syncHeight : function(targetHeight){
35978 var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
35979 var bm = this.bodyEl.getMargins();
35980 var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
35981 this.bodyEl.setHeight(newHeight);
35985 onResize : function(){
35986 if(this.monitorResize){
35987 this.autoSizeTabs();
35992 * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
35994 beginUpdate : function(){
35995 this.updating = true;
35999 * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
36001 endUpdate : function(){
36002 this.updating = false;
36003 this.autoSizeTabs();
36007 * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
36009 autoSizeTabs : function(){
36010 var count = this.items.length;
36011 var vcount = count - this.hiddenCount;
36012 if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
36015 var w = Math.max(this.el.getWidth() - this.cpad, 10);
36016 var availWidth = Math.floor(w / vcount);
36017 var b = this.stripBody;
36018 if(b.getWidth() > w){
36019 var tabs = this.items;
36020 this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
36021 if(availWidth < this.minTabWidth){
36022 /*if(!this.sleft){ // incomplete scrolling code
36023 this.createScrollButtons();
36026 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
36029 if(this.currentTabWidth < this.preferredTabWidth){
36030 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
36036 * Returns the number of tabs in this TabPanel.
36039 getCount : function(){
36040 return this.items.length;
36044 * Resizes all the tabs to the passed width
36045 * @param {Number} The new width
36047 setTabWidth : function(width){
36048 this.currentTabWidth = width;
36049 for(var i = 0, len = this.items.length; i < len; i++) {
36050 if(!this.items[i].isHidden()) {
36051 this.items[i].setWidth(width);
36057 * Destroys this TabPanel
36058 * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
36060 destroy : function(removeEl){
36061 Roo.EventManager.removeResizeListener(this.onResize, this);
36062 for(var i = 0, len = this.items.length; i < len; i++){
36063 this.items[i].purgeListeners();
36065 if(removeEl === true){
36066 this.el.update("");
36071 createStrip : function(container)
36073 var strip = document.createElement("nav");
36074 strip.className = "navbar navbar-default"; //"x-tabs-wrap";
36075 container.appendChild(strip);
36079 createStripList : function(strip)
36081 // div wrapper for retard IE
36082 // returns the "tr" element.
36083 strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
36084 //'<div class="x-tabs-strip-wrap">'+
36085 // '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
36086 // '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
36087 return strip.firstChild; //.firstChild.firstChild.firstChild;
36089 createBody : function(container)
36091 var body = document.createElement("div");
36092 Roo.id(body, "tab-body");
36093 //Roo.fly(body).addClass("x-tabs-body");
36094 Roo.fly(body).addClass("tab-content");
36095 container.appendChild(body);
36098 createItemBody :function(bodyEl, id){
36099 var body = Roo.getDom(id);
36101 body = document.createElement("div");
36104 //Roo.fly(body).addClass("x-tabs-item-body");
36105 Roo.fly(body).addClass("tab-pane");
36106 bodyEl.insertBefore(body, bodyEl.firstChild);
36110 createStripElements : function(stripEl, text, closable, tpl)
36112 var td = document.createElement("li"); // was td..
36115 //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
36118 stripEl.appendChild(td);
36120 td.className = "x-tabs-closable";
36121 if(!this.closeTpl){
36122 this.closeTpl = new Roo.Template(
36123 '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36124 '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
36125 '<div unselectable="on" class="close-icon"> </div></em></span></a>'
36128 var el = this.closeTpl.overwrite(td, {"text": text});
36129 var close = el.getElementsByTagName("div")[0];
36130 var inner = el.getElementsByTagName("em")[0];
36131 return {"el": el, "close": close, "inner": inner};
36134 // not sure what this is..
36135 // if(!this.tabTpl){
36136 //this.tabTpl = new Roo.Template(
36137 // '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
36138 // '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
36140 // this.tabTpl = new Roo.Template(
36141 // '<a href="#">' +
36142 // '<span unselectable="on"' +
36143 // (this.disableTooltips ? '' : ' title="{text}"') +
36144 // ' >{text}</span></a>'
36150 var template = tpl || this.tabTpl || false;
36154 template = new Roo.Template(
36156 '<span unselectable="on"' +
36157 (this.disableTooltips ? '' : ' title="{text}"') +
36158 ' >{text}</span></a>'
36162 switch (typeof(template)) {
36166 template = new Roo.Template(template);
36172 var el = template.overwrite(td, {"text": text});
36174 var inner = el.getElementsByTagName("span")[0];
36176 return {"el": el, "inner": inner};
36184 * @class Roo.TabPanelItem
36185 * @extends Roo.util.Observable
36186 * Represents an individual item (tab plus body) in a TabPanel.
36187 * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
36188 * @param {String} id The id of this TabPanelItem
36189 * @param {String} text The text for the tab of this TabPanelItem
36190 * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
36192 Roo.bootstrap.panel.TabItem = function(config){
36194 * The {@link Roo.TabPanel} this TabPanelItem belongs to
36195 * @type Roo.TabPanel
36197 this.tabPanel = config.panel;
36199 * The id for this TabPanelItem
36202 this.id = config.id;
36204 this.disabled = false;
36206 this.text = config.text;
36208 this.loaded = false;
36209 this.closable = config.closable;
36212 * The body element for this TabPanelItem.
36213 * @type Roo.Element
36215 this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
36216 this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
36217 this.bodyEl.setStyle("display", "block");
36218 this.bodyEl.setStyle("zoom", "1");
36219 //this.hideAction();
36221 var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
36223 this.el = Roo.get(els.el);
36224 this.inner = Roo.get(els.inner, true);
36225 this.textEl = Roo.get(this.el.dom.firstChild, true);
36226 this.pnode = Roo.get(els.el.parentNode, true);
36227 this.el.on("mousedown", this.onTabMouseDown, this);
36228 this.el.on("click", this.onTabClick, this);
36230 if(config.closable){
36231 var c = Roo.get(els.close, true);
36232 c.dom.title = this.closeText;
36233 c.addClassOnOver("close-over");
36234 c.on("click", this.closeClick, this);
36240 * Fires when this tab becomes the active tab.
36241 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36242 * @param {Roo.TabPanelItem} this
36246 * @event beforeclose
36247 * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
36248 * @param {Roo.TabPanelItem} this
36249 * @param {Object} e Set cancel to true on this object to cancel the close.
36251 "beforeclose": true,
36254 * Fires when this tab is closed.
36255 * @param {Roo.TabPanelItem} this
36259 * @event deactivate
36260 * Fires when this tab is no longer the active tab.
36261 * @param {Roo.TabPanel} tabPanel The parent TabPanel
36262 * @param {Roo.TabPanelItem} this
36264 "deactivate" : true
36266 this.hidden = false;
36268 Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
36271 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
36273 purgeListeners : function(){
36274 Roo.util.Observable.prototype.purgeListeners.call(this);
36275 this.el.removeAllListeners();
36278 * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
36281 this.pnode.addClass("active");
36284 this.tabPanel.stripWrap.repaint();
36286 this.fireEvent("activate", this.tabPanel, this);
36290 * Returns true if this tab is the active tab.
36291 * @return {Boolean}
36293 isActive : function(){
36294 return this.tabPanel.getActiveTab() == this;
36298 * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
36301 this.pnode.removeClass("active");
36303 this.fireEvent("deactivate", this.tabPanel, this);
36306 hideAction : function(){
36307 this.bodyEl.hide();
36308 this.bodyEl.setStyle("position", "absolute");
36309 this.bodyEl.setLeft("-20000px");
36310 this.bodyEl.setTop("-20000px");
36313 showAction : function(){
36314 this.bodyEl.setStyle("position", "relative");
36315 this.bodyEl.setTop("");
36316 this.bodyEl.setLeft("");
36317 this.bodyEl.show();
36321 * Set the tooltip for the tab.
36322 * @param {String} tooltip The tab's tooltip
36324 setTooltip : function(text){
36325 if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
36326 this.textEl.dom.qtip = text;
36327 this.textEl.dom.removeAttribute('title');
36329 this.textEl.dom.title = text;
36333 onTabClick : function(e){
36334 e.preventDefault();
36335 this.tabPanel.activate(this.id);
36338 onTabMouseDown : function(e){
36339 e.preventDefault();
36340 this.tabPanel.activate(this.id);
36343 getWidth : function(){
36344 return this.inner.getWidth();
36347 setWidth : function(width){
36348 var iwidth = width - this.pnode.getPadding("lr");
36349 this.inner.setWidth(iwidth);
36350 this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
36351 this.pnode.setWidth(width);
36355 * Show or hide the tab
36356 * @param {Boolean} hidden True to hide or false to show.
36358 setHidden : function(hidden){
36359 this.hidden = hidden;
36360 this.pnode.setStyle("display", hidden ? "none" : "");
36364 * Returns true if this tab is "hidden"
36365 * @return {Boolean}
36367 isHidden : function(){
36368 return this.hidden;
36372 * Returns the text for this tab
36375 getText : function(){
36379 autoSize : function(){
36380 //this.el.beginMeasure();
36381 this.textEl.setWidth(1);
36383 * #2804 [new] Tabs in Roojs
36384 * increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
36386 //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
36387 //this.el.endMeasure();
36391 * Sets the text for the tab (Note: this also sets the tooltip text)
36392 * @param {String} text The tab's text and tooltip
36394 setText : function(text){
36396 this.textEl.update(text);
36397 this.setTooltip(text);
36398 //if(!this.tabPanel.resizeTabs){
36399 // this.autoSize();
36403 * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
36405 activate : function(){
36406 this.tabPanel.activate(this.id);
36410 * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
36412 disable : function(){
36413 if(this.tabPanel.active != this){
36414 this.disabled = true;
36415 this.pnode.addClass("disabled");
36420 * Enables this TabPanelItem if it was previously disabled.
36422 enable : function(){
36423 this.disabled = false;
36424 this.pnode.removeClass("disabled");
36428 * Sets the content for this TabPanelItem.
36429 * @param {String} content The content
36430 * @param {Boolean} loadScripts true to look for and load scripts
36432 setContent : function(content, loadScripts){
36433 this.bodyEl.update(content, loadScripts);
36437 * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
36438 * @return {Roo.UpdateManager} The UpdateManager
36440 getUpdateManager : function(){
36441 return this.bodyEl.getUpdateManager();
36445 * Set a URL to be used to load the content for this TabPanelItem.
36446 * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
36447 * @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)
36448 * @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)
36449 * @return {Roo.UpdateManager} The UpdateManager
36451 setUrl : function(url, params, loadOnce){
36452 if(this.refreshDelegate){
36453 this.un('activate', this.refreshDelegate);
36455 this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36456 this.on("activate", this.refreshDelegate);
36457 return this.bodyEl.getUpdateManager();
36461 _handleRefresh : function(url, params, loadOnce){
36462 if(!loadOnce || !this.loaded){
36463 var updater = this.bodyEl.getUpdateManager();
36464 updater.update(url, params, this._setLoaded.createDelegate(this));
36469 * Forces a content refresh from the URL specified in the {@link #setUrl} method.
36470 * Will fail silently if the setUrl method has not been called.
36471 * This does not activate the panel, just updates its content.
36473 refresh : function(){
36474 if(this.refreshDelegate){
36475 this.loaded = false;
36476 this.refreshDelegate();
36481 _setLoaded : function(){
36482 this.loaded = true;
36486 closeClick : function(e){
36489 this.fireEvent("beforeclose", this, o);
36490 if(o.cancel !== true){
36491 this.tabPanel.removeTab(this.id);
36495 * The text displayed in the tooltip for the close icon.
36498 closeText : "Close this tab"