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
22 * Do not use directly - it does not do anything..
23 * @param {Object} config The config object
28 Roo.bootstrap.Component = function(config){
29 Roo.bootstrap.Component.superclass.constructor.call(this, config);
32 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
35 allowDomMove : false, // to stop relocations in parent onRender...
45 * Initialize Events for the element
47 initEvents : function() { },
53 can_build_overlaid : true,
60 // returns the parent component..
61 return Roo.ComponentMgr.get(this.parentId)
67 onRender : function(ct, position)
69 // Roo.log("Call onRender: " + this.xtype);
71 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
74 if (this.el.attr('xtype')) {
75 this.el.attr('xtypex', this.el.attr('xtype'));
76 this.el.dom.removeAttribute('xtype');
86 var cfg = Roo.apply({}, this.getAutoCreate());
89 // fill in the extra attributes
90 if (this.xattr && typeof(this.xattr) =='object') {
91 for (var i in this.xattr) {
92 cfg[i] = this.xattr[i];
97 cfg.dataId = this.dataId;
101 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
104 if (this.style) { // fixme needs to support more complex style data.
105 cfg.style = this.style;
109 cfg.name = this.name;
114 this.el = ct.createChild(cfg, position);
117 this.tooltipEl().attr('tooltip', this.tooltip);
120 if(this.tabIndex !== undefined){
121 this.el.dom.setAttribute('tabIndex', this.tabIndex);
128 * Fetch the element to add children to
129 * @return {Roo.Element} defaults to this.el
131 getChildContainer : function()
136 * Fetch the element to display the tooltip on.
137 * @return {Roo.Element} defaults to this.el
139 tooltipEl : function()
144 addxtype : function(tree,cntr)
148 cn = Roo.factory(tree);
150 cn.parentType = this.xtype; //??
151 cn.parentId = this.id;
153 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
155 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
157 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
159 var build_from_html = Roo.XComponent.build_from_html;
161 var is_body = (tree.xtype == 'Body') ;
163 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
165 var self_cntr_el = Roo.get(this[cntr](false));
167 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
168 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
169 return this.addxtypeChild(tree,cntr);
172 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
175 return this.addxtypeChild(Roo.apply({}, tree),cntr);
178 Roo.log('skipping render');
186 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
192 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
196 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
201 addxtypeChild : function (tree, cntr)
203 Roo.debug && Roo.log('addxtypeChild:' + cntr);
205 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
208 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
209 (typeof(tree['flexy:foreach']) != 'undefined');
213 skip_children = false;
214 // render the element if it's not BODY.
215 if (tree.xtype != 'Body') {
217 cn = Roo.factory(tree);
219 cn.parentType = this.xtype; //??
220 cn.parentId = this.id;
222 var build_from_html = Roo.XComponent.build_from_html;
225 // does the container contain child eleemnts with 'xtype' attributes.
226 // that match this xtype..
227 // note - when we render we create these as well..
228 // so we should check to see if body has xtype set.
229 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
231 var self_cntr_el = Roo.get(this[cntr](false));
232 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
235 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
236 // and are not displayed -this causes this to use up the wrong element when matching.
237 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
240 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
241 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
247 //echild.dom.removeAttribute('xtype');
249 Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
250 Roo.debug && Roo.log(self_cntr_el);
251 Roo.debug && Roo.log(echild);
252 Roo.debug && Roo.log(cn);
258 // if object has flexy:if - then it may or may not be rendered.
259 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
260 // skip a flexy if element.
261 Roo.debug && Roo.log('skipping render');
262 Roo.debug && Roo.log(tree);
264 Roo.debug && Roo.log('skipping all children');
265 skip_children = true;
270 // actually if flexy:foreach is found, we really want to create
271 // multiple copies here...
273 //Roo.log(this[cntr]());
274 cn.render(this[cntr](true));
276 // then add the element..
284 if (typeof (tree.menu) != 'undefined') {
285 tree.menu.parentType = cn.xtype;
286 tree.menu.triggerEl = cn.el;
287 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
291 if (!tree.items || !tree.items.length) {
295 var items = tree.items;
298 //Roo.log(items.length);
300 if (!skip_children) {
301 for(var i =0;i < items.length;i++) {
302 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
324 * @class Roo.bootstrap.Body
325 * @extends Roo.bootstrap.Component
326 * Bootstrap Body class
330 * @param {Object} config The config object
333 Roo.bootstrap.Body = function(config){
334 Roo.bootstrap.Body.superclass.constructor.call(this, config);
335 this.el = Roo.get(document.body);
336 if (this.cls && this.cls.length) {
337 Roo.get(document.body).addClass(this.cls);
341 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
346 onRender : function(ct, position)
348 /* Roo.log("Roo.bootstrap.Body - onRender");
349 if (this.cls && this.cls.length) {
350 Roo.get(document.body).addClass(this.cls);
370 * @class Roo.bootstrap.ButtonGroup
371 * @extends Roo.bootstrap.Component
372 * Bootstrap ButtonGroup class
373 * @cfg {String} size lg | sm | xs (default empty normal)
374 * @cfg {String} align vertical | justified (default none)
375 * @cfg {String} direction up | down (default down)
376 * @cfg {Boolean} toolbar false | true
377 * @cfg {Boolean} btn true | false
382 * @param {Object} config The config object
385 Roo.bootstrap.ButtonGroup = function(config){
386 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
389 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
397 getAutoCreate : function(){
403 cfg.html = this.html || cfg.html;
414 if (['vertical','justified'].indexOf(this.align)!==-1) {
415 cfg.cls = 'btn-group-' + this.align;
417 if (this.align == 'justified') {
418 console.log(this.items);
422 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
423 cfg.cls += ' btn-group-' + this.size;
426 if (this.direction == 'up') {
427 cfg.cls += ' dropup' ;
443 * @class Roo.bootstrap.Button
444 * @extends Roo.bootstrap.Component
445 * Bootstrap Button class
446 * @cfg {String} html The button content
447 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
448 * @cfg {String} size empty | lg | sm | xs
449 * @cfg {String} tag empty | a | input | submit
450 * @cfg {String} href empty or href
451 * @cfg {Boolean} disabled false | true
452 * @cfg {Boolean} isClose false | true
453 * @cfg {String} glyphicon empty | 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
454 * @cfg {String} badge text for badge
455 * @cfg {String} theme default (or empty) | glow
456 * @cfg {Boolean} inverse false | true
457 * @cfg {Boolean} toggle false | true
458 * @cfg {String} ontext text for on toggle state
459 * @cfg {String} offtext text for off toggle state
460 * @cfg {Boolean} defaulton true | false
461 * @cfg {Boolean} preventDefault (true | false) default true
462 * @cfg {Boolean} removeClass true | false remove the standard class..
463 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
466 * Create a new button
467 * @param {Object} config The config object
471 Roo.bootstrap.Button = function(config){
472 Roo.bootstrap.Button.superclass.constructor.call(this, config);
477 * When a butotn is pressed
478 * @param {Roo.EventObject} e
483 * After the button has been toggles
484 * @param {Roo.EventObject} e
485 * @param {boolean} pressed (also available as button.pressed)
491 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
509 preventDefault: true,
518 getAutoCreate : function(){
526 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
527 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
532 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
534 if (this.toggle == true) {
537 cls: 'slider-frame roo-button',
542 'data-off-text':'OFF',
543 cls: 'slider-button',
549 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
550 cfg.cls += ' '+this.weight;
559 cfg["aria-hidden"] = true;
561 cfg.html = "×";
567 if (this.theme==='default') {
568 cfg.cls = 'btn roo-button';
570 //if (this.parentType != 'Navbar') {
571 this.weight = this.weight.length ? this.weight : 'default';
573 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
575 cfg.cls += ' btn-' + this.weight;
577 } else if (this.theme==='glow') {
580 cfg.cls = 'btn-glow roo-button';
582 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
584 cfg.cls += ' ' + this.weight;
590 this.cls += ' inverse';
595 cfg.cls += ' active';
599 cfg.disabled = 'disabled';
603 Roo.log('changing to ul' );
605 this.glyphicon = 'caret';
608 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
610 //gsRoo.log(this.parentType);
611 if (this.parentType === 'Navbar' && !this.parent().bar) {
612 Roo.log('changing to li?');
621 href : this.href || '#'
624 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
625 cfg.cls += ' dropdown';
632 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
634 if (this.glyphicon) {
635 cfg.html = ' ' + cfg.html;
640 cls: 'glyphicon glyphicon-' + this.glyphicon
650 // cfg.cls='btn roo-button';
654 var value = cfg.html;
659 cls: 'glyphicon glyphicon-' + this.glyphicon,
678 cfg.cls += ' dropdown';
679 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
682 if (cfg.tag !== 'a' && this.href !== '') {
683 throw "Tag must be a to set href.";
684 } else if (this.href.length > 0) {
685 cfg.href = this.href;
688 if(this.removeClass){
693 cfg.target = this.target;
698 initEvents: function() {
699 // Roo.log('init events?');
700 // Roo.log(this.el.dom);
703 if (typeof (this.menu) != 'undefined') {
704 this.menu.parentType = this.xtype;
705 this.menu.triggerEl = this.el;
706 this.addxtype(Roo.apply({}, this.menu));
710 if (this.el.hasClass('roo-button')) {
711 this.el.on('click', this.onClick, this);
713 this.el.select('.roo-button').on('click', this.onClick, this);
716 if(this.removeClass){
717 this.el.on('click', this.onClick, this);
720 this.el.enableDisplayMode();
723 onClick : function(e)
729 Roo.log('button on click ');
730 if(this.preventDefault){
733 if (this.pressed === true || this.pressed === false) {
734 this.pressed = !this.pressed;
735 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
736 this.fireEvent('toggle', this, e, this.pressed);
740 this.fireEvent('click', this, e);
744 * Enables this button
748 this.disabled = false;
749 this.el.removeClass('disabled');
753 * Disable this button
757 this.disabled = true;
758 this.el.addClass('disabled');
761 * sets the active state on/off,
762 * @param {Boolean} state (optional) Force a particular state
764 setActive : function(v) {
766 this.el[v ? 'addClass' : 'removeClass']('active');
769 * toggles the current active state
771 toggleActive : function()
773 var active = this.el.hasClass('active');
774 this.setActive(!active);
778 setText : function(str)
780 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
784 return this.el.select('.roo-button-text',true).first().dom.innerHTML;
807 * @class Roo.bootstrap.Column
808 * @extends Roo.bootstrap.Component
809 * Bootstrap Column class
810 * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
811 * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
812 * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
813 * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
814 * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
815 * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
816 * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
817 * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
820 * @cfg {Boolean} hidden (true|false) hide the element
821 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
822 * @cfg {String} fa (ban|check|...) font awesome icon
823 * @cfg {Number} fasize (1|2|....) font awsome size
825 * @cfg {String} icon (info-sign|check|...) glyphicon name
827 * @cfg {String} html content of column.
830 * Create a new Column
831 * @param {Object} config The config object
834 Roo.bootstrap.Column = function(config){
835 Roo.bootstrap.Column.superclass.constructor.call(this, config);
838 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
856 getAutoCreate : function(){
857 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
865 ['xs','sm','md','lg'].map(function(size){
866 //Roo.log( size + ':' + settings[size]);
868 if (settings[size+'off'] !== false) {
869 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
872 if (settings[size] === false) {
875 Roo.log(settings[size]);
876 if (!settings[size]) { // 0 = hidden
877 cfg.cls += ' hidden-' + size;
880 cfg.cls += ' col-' + size + '-' + settings[size];
885 cfg.cls += ' hidden';
888 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
889 cfg.cls +=' alert alert-' + this.alert;
893 if (this.html.length) {
894 cfg.html = this.html;
898 if (this.fasize > 1) {
899 fasize = ' fa-' + this.fasize + 'x';
901 cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
906 cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + + (cfg.html || '')
925 * @class Roo.bootstrap.Container
926 * @extends Roo.bootstrap.Component
927 * Bootstrap Container class
928 * @cfg {Boolean} jumbotron is it a jumbotron element
929 * @cfg {String} html content of element
930 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
931 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
932 * @cfg {String} header content of header (for panel)
933 * @cfg {String} footer content of footer (for panel)
934 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
935 * @cfg {String} tag (header|aside|section) type of HTML tag.
936 * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
937 * @cfg {String} fa (ban|check|...) font awesome icon
938 * @cfg {String} icon (info-sign|check|...) glyphicon name
939 * @cfg {Boolean} hidden (true|false) hide the element
943 * Create a new Container
944 * @param {Object} config The config object
947 Roo.bootstrap.Container = function(config){
948 Roo.bootstrap.Container.superclass.constructor.call(this, config);
951 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
965 getChildContainer : function() {
971 if (this.panel.length) {
972 return this.el.select('.panel-body',true).first();
979 getAutoCreate : function(){
982 tag : this.tag || 'div',
986 if (this.jumbotron) {
987 cfg.cls = 'jumbotron';
992 // - this is applied by the parent..
994 // cfg.cls = this.cls + '';
997 if (this.sticky.length) {
999 var bd = Roo.get(document.body);
1000 if (!bd.hasClass('bootstrap-sticky')) {
1001 bd.addClass('bootstrap-sticky');
1002 Roo.select('html',true).setStyle('height', '100%');
1005 cfg.cls += 'bootstrap-sticky-' + this.sticky;
1009 if (this.well.length) {
1010 switch (this.well) {
1013 cfg.cls +=' well well-' +this.well;
1022 cfg.cls += ' hidden';
1026 if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1027 cfg.cls +=' alert alert-' + this.alert;
1032 if (this.panel.length) {
1033 cfg.cls += ' panel panel-' + this.panel;
1035 if (this.header.length) {
1038 cls : 'panel-heading',
1041 cls : 'panel-title',
1054 if (this.footer.length) {
1056 cls : 'panel-footer',
1065 body.html = this.html || cfg.html;
1066 // prefix with the icons..
1068 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1071 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1076 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1077 cfg.cls = 'container';
1083 titleEl : function()
1085 if(!this.el || !this.panel.length || !this.header.length){
1089 return this.el.select('.panel-title',true).first();
1092 setTitle : function(v)
1094 var titleEl = this.titleEl();
1100 titleEl.dom.innerHTML = v;
1103 getTitle : function()
1106 var titleEl = this.titleEl();
1112 return titleEl.dom.innerHTML;
1126 * @class Roo.bootstrap.Img
1127 * @extends Roo.bootstrap.Component
1128 * Bootstrap Img class
1129 * @cfg {Boolean} imgResponsive false | true
1130 * @cfg {String} border rounded | circle | thumbnail
1131 * @cfg {String} src image source
1132 * @cfg {String} alt image alternative text
1133 * @cfg {String} href a tag href
1134 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1137 * Create a new Input
1138 * @param {Object} config The config object
1141 Roo.bootstrap.Img = function(config){
1142 Roo.bootstrap.Img.superclass.constructor.call(this, config);
1148 * The img click event for the img.
1149 * @param {Roo.EventObject} e
1155 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1157 imgResponsive: true,
1163 getAutoCreate : function(){
1167 cls: (this.imgResponsive) ? 'img-responsive' : '',
1171 cfg.html = this.html || cfg.html;
1173 cfg.src = this.src || cfg.src;
1175 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1176 cfg.cls += ' img-' + this.border;
1193 a.target = this.target;
1199 return (this.href) ? a : cfg;
1202 initEvents: function() {
1205 this.el.on('click', this.onClick, this);
1209 onClick : function(e)
1211 Roo.log('img onclick');
1212 this.fireEvent('click', this, e);
1226 * @class Roo.bootstrap.Link
1227 * @extends Roo.bootstrap.Component
1228 * Bootstrap Link Class
1229 * @cfg {String} alt image alternative text
1230 * @cfg {String} href a tag href
1231 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1232 * @cfg {String} html the content of the link.
1233 * @cfg {String} anchor name for the anchor link
1235 * @cfg {Boolean} preventDefault (true | false) default false
1239 * Create a new Input
1240 * @param {Object} config The config object
1243 Roo.bootstrap.Link = function(config){
1244 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1250 * The img click event for the img.
1251 * @param {Roo.EventObject} e
1257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1261 preventDefault: false,
1265 getAutoCreate : function()
1271 // anchor's do not require html/href...
1272 if (this.anchor === false) {
1273 cfg.html = this.html || 'html-missing';
1274 cfg.href = this.href || '#';
1276 cfg.name = this.anchor;
1277 if (this.html !== false) {
1278 cfg.html = this.html;
1280 if (this.href !== false) {
1281 cfg.href = this.href;
1285 if(this.alt !== false){
1290 if(this.target !== false) {
1291 cfg.target = this.target;
1297 initEvents: function() {
1299 if(!this.href || this.preventDefault){
1300 this.el.on('click', this.onClick, this);
1304 onClick : function(e)
1306 if(this.preventDefault){
1309 //Roo.log('img onclick');
1310 this.fireEvent('click', this, e);
1323 * @class Roo.bootstrap.Header
1324 * @extends Roo.bootstrap.Component
1325 * Bootstrap Header class
1326 * @cfg {String} html content of header
1327 * @cfg {Number} level (1|2|3|4|5|6) default 1
1330 * Create a new Header
1331 * @param {Object} config The config object
1335 Roo.bootstrap.Header = function(config){
1336 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1339 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1347 getAutoCreate : function(){
1350 tag: 'h' + (1 *this.level),
1351 html: this.html || 'fill in html'
1363 * Ext JS Library 1.1.1
1364 * Copyright(c) 2006-2007, Ext JS, LLC.
1366 * Originally Released Under LGPL - original licence link has changed is not relivant.
1369 * <script type="text/javascript">
1373 * @class Roo.bootstrap.MenuMgr
1374 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1377 Roo.bootstrap.MenuMgr = function(){
1378 var menus, active, groups = {}, attached = false, lastShow = new Date();
1380 // private - called when first menu is created
1383 active = new Roo.util.MixedCollection();
1384 Roo.get(document).addKeyListener(27, function(){
1385 if(active.length > 0){
1393 if(active && active.length > 0){
1394 var c = active.clone();
1404 if(active.length < 1){
1405 Roo.get(document).un("mouseup", onMouseDown);
1413 var last = active.last();
1414 lastShow = new Date();
1417 Roo.get(document).on("mouseup", onMouseDown);
1422 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1423 m.parentMenu.activeChild = m;
1424 }else if(last && last.isVisible()){
1425 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1430 function onBeforeHide(m){
1432 m.activeChild.hide();
1434 if(m.autoHideTimer){
1435 clearTimeout(m.autoHideTimer);
1436 delete m.autoHideTimer;
1441 function onBeforeShow(m){
1442 var pm = m.parentMenu;
1443 if(!pm && !m.allowOtherMenus){
1445 }else if(pm && pm.activeChild && active != m){
1446 pm.activeChild.hide();
1451 function onMouseDown(e){
1452 Roo.log("on MouseDown");
1453 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu") && !e.getTarget('.user-menu')){
1461 function onBeforeCheck(mi, state){
1463 var g = groups[mi.group];
1464 for(var i = 0, l = g.length; i < l; i++){
1466 g[i].setChecked(false);
1475 * Hides all menus that are currently visible
1477 hideAll : function(){
1482 register : function(menu){
1486 menus[menu.id] = menu;
1487 menu.on("beforehide", onBeforeHide);
1488 menu.on("hide", onHide);
1489 menu.on("beforeshow", onBeforeShow);
1490 menu.on("show", onShow);
1492 if(g && menu.events["checkchange"]){
1496 groups[g].push(menu);
1497 menu.on("checkchange", onCheck);
1502 * Returns a {@link Roo.menu.Menu} object
1503 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1504 * be used to generate and return a new Menu instance.
1506 get : function(menu){
1507 if(typeof menu == "string"){ // menu id
1509 }else if(menu.events){ // menu instance
1512 /*else if(typeof menu.length == 'number'){ // array of menu items?
1513 return new Roo.bootstrap.Menu({items:menu});
1514 }else{ // otherwise, must be a config
1515 return new Roo.bootstrap.Menu(menu);
1522 unregister : function(menu){
1523 delete menus[menu.id];
1524 menu.un("beforehide", onBeforeHide);
1525 menu.un("hide", onHide);
1526 menu.un("beforeshow", onBeforeShow);
1527 menu.un("show", onShow);
1529 if(g && menu.events["checkchange"]){
1530 groups[g].remove(menu);
1531 menu.un("checkchange", onCheck);
1536 registerCheckable : function(menuItem){
1537 var g = menuItem.group;
1542 groups[g].push(menuItem);
1543 menuItem.on("beforecheckchange", onBeforeCheck);
1548 unregisterCheckable : function(menuItem){
1549 var g = menuItem.group;
1551 groups[g].remove(menuItem);
1552 menuItem.un("beforecheckchange", onBeforeCheck);
1564 * @class Roo.bootstrap.Menu
1565 * @extends Roo.bootstrap.Component
1566 * Bootstrap Menu class - container for MenuItems
1567 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1571 * @param {Object} config The config object
1575 Roo.bootstrap.Menu = function(config){
1576 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1577 if (this.registerMenu) {
1578 Roo.bootstrap.MenuMgr.register(this);
1583 * Fires before this menu is displayed
1584 * @param {Roo.menu.Menu} this
1589 * Fires before this menu is hidden
1590 * @param {Roo.menu.Menu} this
1595 * Fires after this menu is displayed
1596 * @param {Roo.menu.Menu} this
1601 * Fires after this menu is hidden
1602 * @param {Roo.menu.Menu} this
1607 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1608 * @param {Roo.menu.Menu} this
1609 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1610 * @param {Roo.EventObject} e
1615 * Fires when the mouse is hovering over this menu
1616 * @param {Roo.menu.Menu} this
1617 * @param {Roo.EventObject} e
1618 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1623 * Fires when the mouse exits this menu
1624 * @param {Roo.menu.Menu} this
1625 * @param {Roo.EventObject} e
1626 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1631 * Fires when a menu item contained in this menu is clicked
1632 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1633 * @param {Roo.EventObject} e
1637 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1640 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1644 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1647 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1649 registerMenu : true,
1651 menuItems :false, // stores the menu items..
1657 getChildContainer : function() {
1661 getAutoCreate : function(){
1663 //if (['right'].indexOf(this.align)!==-1) {
1664 // cfg.cn[1].cls += ' pull-right'
1670 cls : 'dropdown-menu' ,
1671 style : 'z-index:1000'
1675 if (this.type === 'submenu') {
1676 cfg.cls = 'submenu active';
1678 if (this.type === 'treeview') {
1679 cfg.cls = 'treeview-menu';
1684 initEvents : function() {
1686 // Roo.log("ADD event");
1687 // Roo.log(this.triggerEl.dom);
1688 this.triggerEl.on('click', this.onTriggerPress, this);
1689 this.triggerEl.addClass('dropdown-toggle');
1690 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1692 this.el.on("mouseover", this.onMouseOver, this);
1693 this.el.on("mouseout", this.onMouseOut, this);
1697 findTargetItem : function(e){
1698 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1702 //Roo.log(t); Roo.log(t.id);
1704 //Roo.log(this.menuitems);
1705 return this.menuitems.get(t.id);
1707 //return this.items.get(t.menuItemId);
1712 onClick : function(e){
1713 Roo.log("menu.onClick");
1714 var t = this.findTargetItem(e);
1715 if(!t || t.isContainer){
1720 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1721 if(t == this.activeItem && t.shouldDeactivate(e)){
1722 this.activeItem.deactivate();
1723 delete this.activeItem;
1727 this.setActiveItem(t, true);
1735 Roo.log('pass click event');
1739 this.fireEvent("click", this, t, e);
1743 onMouseOver : function(e){
1744 var t = this.findTargetItem(e);
1747 // if(t.canActivate && !t.disabled){
1748 // this.setActiveItem(t, true);
1752 this.fireEvent("mouseover", this, e, t);
1754 isVisible : function(){
1755 return !this.hidden;
1757 onMouseOut : function(e){
1758 var t = this.findTargetItem(e);
1761 // if(t == this.activeItem && t.shouldDeactivate(e)){
1762 // this.activeItem.deactivate();
1763 // delete this.activeItem;
1766 this.fireEvent("mouseout", this, e, t);
1771 * Displays this menu relative to another element
1772 * @param {String/HTMLElement/Roo.Element} element The element to align to
1773 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1774 * the element (defaults to this.defaultAlign)
1775 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1777 show : function(el, pos, parentMenu){
1778 this.parentMenu = parentMenu;
1782 this.fireEvent("beforeshow", this);
1783 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1786 * Displays this menu at a specific xy position
1787 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1788 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1790 showAt : function(xy, parentMenu, /* private: */_e){
1791 this.parentMenu = parentMenu;
1796 this.fireEvent("beforeshow", this);
1798 //xy = this.el.adjustForConstraints(xy);
1800 //this.el.setXY(xy);
1802 this.hideMenuItems();
1803 this.hidden = false;
1804 this.triggerEl.addClass('open');
1806 this.fireEvent("show", this);
1812 this.doFocus.defer(50, this);
1816 doFocus : function(){
1818 this.focusEl.focus();
1823 * Hides this menu and optionally all parent menus
1824 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1826 hide : function(deep){
1828 this.hideMenuItems();
1829 if(this.el && this.isVisible()){
1830 this.fireEvent("beforehide", this);
1831 if(this.activeItem){
1832 this.activeItem.deactivate();
1833 this.activeItem = null;
1835 this.triggerEl.removeClass('open');;
1837 this.fireEvent("hide", this);
1839 if(deep === true && this.parentMenu){
1840 this.parentMenu.hide(true);
1844 onTriggerPress : function(e)
1847 Roo.log('trigger press');
1848 //Roo.log(e.getTarget());
1849 // Roo.log(this.triggerEl.dom);
1850 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1853 if (this.isVisible()) {
1857 this.show(this.triggerEl, false, false);
1866 hideMenuItems : function()
1868 //$(backdrop).remove()
1869 Roo.select('.open',true).each(function(aa) {
1871 aa.removeClass('open');
1872 //var parent = getParent($(this))
1873 //var relatedTarget = { relatedTarget: this }
1875 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1876 //if (e.isDefaultPrevented()) return
1877 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1880 addxtypeChild : function (tree, cntr) {
1881 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1883 this.menuitems.add(comp);
1904 * @class Roo.bootstrap.MenuItem
1905 * @extends Roo.bootstrap.Component
1906 * Bootstrap MenuItem class
1907 * @cfg {String} html the menu label
1908 * @cfg {String} href the link
1909 * @cfg {Boolean} preventDefault (true | false) default true
1910 * @cfg {Boolean} isContainer (true | false) default false
1914 * Create a new MenuItem
1915 * @param {Object} config The config object
1919 Roo.bootstrap.MenuItem = function(config){
1920 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1925 * The raw click event for the entire grid.
1926 * @param {Roo.EventObject} e
1932 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1936 preventDefault: true,
1937 isContainer : false,
1939 getAutoCreate : function(){
1941 if(this.isContainer){
1944 cls: 'dropdown-menu-item'
1950 cls: 'dropdown-menu-item',
1959 if (this.parent().type == 'treeview') {
1960 cfg.cls = 'treeview-menu';
1963 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1964 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1968 initEvents: function() {
1970 //this.el.select('a').on('click', this.onClick, this);
1973 onClick : function(e)
1975 Roo.log('item on click ');
1976 //if(this.preventDefault){
1977 // e.preventDefault();
1979 //this.parent().hideMenuItems();
1981 this.fireEvent('click', this, e);
2000 * @class Roo.bootstrap.MenuSeparator
2001 * @extends Roo.bootstrap.Component
2002 * Bootstrap MenuSeparator class
2005 * Create a new MenuItem
2006 * @param {Object} config The config object
2010 Roo.bootstrap.MenuSeparator = function(config){
2011 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2014 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
2016 getAutoCreate : function(){
2031 <div class="modal fade">
2032 <div class="modal-dialog">
2033 <div class="modal-content">
2034 <div class="modal-header">
2035 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
2036 <h4 class="modal-title">Modal title</h4>
2038 <div class="modal-body">
2039 <p>One fine body…</p>
2041 <div class="modal-footer">
2042 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
2043 <button type="button" class="btn btn-primary">Save changes</button>
2045 </div><!-- /.modal-content -->
2046 </div><!-- /.modal-dialog -->
2047 </div><!-- /.modal -->
2057 * @class Roo.bootstrap.Modal
2058 * @extends Roo.bootstrap.Component
2059 * Bootstrap Modal class
2060 * @cfg {String} title Title of dialog
2061 * @cfg {Boolean} specificTitle (true|false) default false
2062 * @cfg {Array} buttons Array of buttons or standard button set..
2063 * @cfg {String} buttonPosition (left|right|center) default right
2064 * @cfg {Boolean} animate (true | false) default true
2067 * Create a new Modal Dialog
2068 * @param {Object} config The config object
2071 Roo.bootstrap.Modal = function(config){
2072 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2077 * The raw btnclick event for the button
2078 * @param {Roo.EventObject} e
2082 this.buttons = this.buttons || [];
2085 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
2087 title : 'test dialog',
2094 specificTitle: false,
2096 buttonPosition: 'right',
2100 onRender : function(ct, position)
2102 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2105 var cfg = Roo.apply({}, this.getAutoCreate());
2108 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2110 //if (!cfg.name.length) {
2114 cfg.cls += ' ' + this.cls;
2117 cfg.style = this.style;
2119 this.el = Roo.get(document.body).createChild(cfg, position);
2121 //var type = this.el.dom.type;
2123 if(this.tabIndex !== undefined){
2124 this.el.dom.setAttribute('tabIndex', this.tabIndex);
2129 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2130 this.maskEl.enableDisplayMode("block");
2132 //this.el.addClass("x-dlg-modal");
2134 if (this.buttons.length) {
2135 Roo.each(this.buttons, function(bb) {
2136 b = Roo.apply({}, bb);
2137 b.xns = b.xns || Roo.bootstrap;
2138 b.xtype = b.xtype || 'Button';
2139 if (typeof(b.listeners) == 'undefined') {
2140 b.listeners = { click : this.onButtonClick.createDelegate(this) };
2143 var btn = Roo.factory(b);
2145 btn.onRender(this.el.select('.modal-footer div').first());
2149 // render the children.
2152 if(typeof(this.items) != 'undefined'){
2153 var items = this.items;
2156 for(var i =0;i < items.length;i++) {
2157 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2161 this.items = nitems;
2163 this.body = this.el.select('.modal-body',true).first();
2164 this.close = this.el.select('.modal-header .close', true).first();
2165 this.footer = this.el.select('.modal-footer',true).first();
2167 //this.el.addClass([this.fieldClass, this.cls]);
2170 getAutoCreate : function(){
2175 html : this.html || ''
2180 cls : 'modal-title',
2184 if(this.specificTitle){
2190 style : 'display: none',
2193 cls: "modal-dialog",
2196 cls : "modal-content",
2199 cls : 'modal-header',
2211 cls : 'modal-footer',
2215 cls: 'btn-' + this.buttonPosition
2232 modal.cls += ' fade';
2238 getChildContainer : function() {
2240 return this.el.select('.modal-body',true).first();
2243 getButtonContainer : function() {
2244 return this.el.select('.modal-footer div',true).first();
2247 initEvents : function()
2249 this.el.select('.modal-header .close').on('click', this.hide, this);
2251 // this.addxtype(this);
2255 if (!this.rendered) {
2259 this.el.setStyle('display', 'block');
2263 (function(){ _this.el.addClass('in'); }).defer(50);
2265 this.el.addClass('in');
2268 Roo.get(document.body).addClass("x-body-masked");
2269 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2271 this.el.setStyle('zIndex', '10001');
2272 this.fireEvent('show', this);
2279 Roo.get(document.body).removeClass("x-body-masked");
2280 this.el.removeClass('in');
2284 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2286 this.el.setStyle('display', 'none');
2289 this.fireEvent('hide', this);
2292 addButton : function(str, cb)
2296 var b = Roo.apply({}, { html : str } );
2297 b.xns = b.xns || Roo.bootstrap;
2298 b.xtype = b.xtype || 'Button';
2299 if (typeof(b.listeners) == 'undefined') {
2300 b.listeners = { click : cb.createDelegate(this) };
2303 var btn = Roo.factory(b);
2305 btn.onRender(this.el.select('.modal-footer div').first());
2311 setDefaultButton : function(btn)
2313 //this.el.select('.modal-footer').()
2315 resizeTo: function(w,h)
2319 setContentSize : function(w, h)
2323 onButtonClick: function(btn,e)
2326 this.fireEvent('btnclick', btn.name, e);
2328 setTitle: function(str) {
2329 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2335 Roo.apply(Roo.bootstrap.Modal, {
2337 * Button config that displays a single OK button
2346 * Button config that displays Yes and No buttons
2362 * Button config that displays OK and Cancel buttons
2377 * Button config that displays Yes, No and Cancel buttons
2399 * messagebox - can be used as a replace
2403 * @class Roo.MessageBox
2404 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2408 Roo.Msg.alert('Status', 'Changes saved successfully.');
2410 // Prompt for user data:
2411 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2413 // process text value...
2417 // Show a dialog using config options:
2419 title:'Save Changes?',
2420 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2421 buttons: Roo.Msg.YESNOCANCEL,
2428 Roo.bootstrap.MessageBox = function(){
2429 var dlg, opt, mask, waitTimer;
2430 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2431 var buttons, activeTextEl, bwidth;
2435 var handleButton = function(button){
2437 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2441 var handleHide = function(){
2443 dlg.el.removeClass(opt.cls);
2446 // Roo.TaskMgr.stop(waitTimer);
2447 // waitTimer = null;
2452 var updateButtons = function(b){
2455 buttons["ok"].hide();
2456 buttons["cancel"].hide();
2457 buttons["yes"].hide();
2458 buttons["no"].hide();
2459 //dlg.footer.dom.style.display = 'none';
2462 dlg.footer.dom.style.display = '';
2463 for(var k in buttons){
2464 if(typeof buttons[k] != "function"){
2467 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2468 width += buttons[k].el.getWidth()+15;
2478 var handleEsc = function(d, k, e){
2479 if(opt && opt.closable !== false){
2489 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2490 * @return {Roo.BasicDialog} The BasicDialog element
2492 getDialog : function(){
2494 dlg = new Roo.bootstrap.Modal( {
2497 //constraintoviewport:false,
2499 //collapsible : false,
2504 //buttonAlign:"center",
2505 closeClick : function(){
2506 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2509 handleButton("cancel");
2514 dlg.on("hide", handleHide);
2516 //dlg.addKeyListener(27, handleEsc);
2518 this.buttons = buttons;
2519 var bt = this.buttonText;
2520 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2521 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2522 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2523 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2525 bodyEl = dlg.body.createChild({
2527 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2528 '<textarea class="roo-mb-textarea"></textarea>' +
2529 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2531 msgEl = bodyEl.dom.firstChild;
2532 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2533 textboxEl.enableDisplayMode();
2534 textboxEl.addKeyListener([10,13], function(){
2535 if(dlg.isVisible() && opt && opt.buttons){
2538 }else if(opt.buttons.yes){
2539 handleButton("yes");
2543 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2544 textareaEl.enableDisplayMode();
2545 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2546 progressEl.enableDisplayMode();
2547 var pf = progressEl.dom.firstChild;
2549 pp = Roo.get(pf.firstChild);
2550 pp.setHeight(pf.offsetHeight);
2558 * Updates the message box body text
2559 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2560 * the XHTML-compliant non-breaking space character '&#160;')
2561 * @return {Roo.MessageBox} This message box
2563 updateText : function(text){
2564 if(!dlg.isVisible() && !opt.width){
2565 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2567 msgEl.innerHTML = text || ' ';
2569 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2570 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2572 Math.min(opt.width || cw , this.maxWidth),
2573 Math.max(opt.minWidth || this.minWidth, bwidth)
2576 activeTextEl.setWidth(w);
2578 if(dlg.isVisible()){
2579 dlg.fixedcenter = false;
2581 // to big, make it scroll. = But as usual stupid IE does not support
2584 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2585 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2586 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2588 bodyEl.dom.style.height = '';
2589 bodyEl.dom.style.overflowY = '';
2592 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2594 bodyEl.dom.style.overflowX = '';
2597 dlg.setContentSize(w, bodyEl.getHeight());
2598 if(dlg.isVisible()){
2599 dlg.fixedcenter = true;
2605 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2606 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2607 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2608 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2609 * @return {Roo.MessageBox} This message box
2611 updateProgress : function(value, text){
2613 this.updateText(text);
2615 if (pp) { // weird bug on my firefox - for some reason this is not defined
2616 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2622 * Returns true if the message box is currently displayed
2623 * @return {Boolean} True if the message box is visible, else false
2625 isVisible : function(){
2626 return dlg && dlg.isVisible();
2630 * Hides the message box if it is displayed
2633 if(this.isVisible()){
2639 * Displays a new message box, or reinitializes an existing message box, based on the config options
2640 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2641 * The following config object properties are supported:
2643 Property Type Description
2644 ---------- --------------- ------------------------------------------------------------------------------------
2645 animEl String/Element An id or Element from which the message box should animate as it opens and
2646 closes (defaults to undefined)
2647 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2648 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2649 closable Boolean False to hide the top-right close button (defaults to true). Note that
2650 progress and wait dialogs will ignore this property and always hide the
2651 close button as they can only be closed programmatically.
2652 cls String A custom CSS class to apply to the message box element
2653 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2654 displayed (defaults to 75)
2655 fn Function A callback function to execute after closing the dialog. The arguments to the
2656 function will be btn (the name of the button that was clicked, if applicable,
2657 e.g. "ok"), and text (the value of the active text field, if applicable).
2658 Progress and wait dialogs will ignore this option since they do not respond to
2659 user actions and can only be closed programmatically, so any required function
2660 should be called by the same code after it closes the dialog.
2661 icon String A CSS class that provides a background image to be used as an icon for
2662 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2663 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2664 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2665 modal Boolean False to allow user interaction with the page while the message box is
2666 displayed (defaults to true)
2667 msg String A string that will replace the existing message box body text (defaults
2668 to the XHTML-compliant non-breaking space character ' ')
2669 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2670 progress Boolean True to display a progress bar (defaults to false)
2671 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2672 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2673 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2674 title String The title text
2675 value String The string value to set into the active textbox element if displayed
2676 wait Boolean True to display a progress bar (defaults to false)
2677 width Number The width of the dialog in pixels
2684 msg: 'Please enter your address:',
2686 buttons: Roo.MessageBox.OKCANCEL,
2689 animEl: 'addAddressBtn'
2692 * @param {Object} config Configuration options
2693 * @return {Roo.MessageBox} This message box
2695 show : function(options)
2698 // this causes nightmares if you show one dialog after another
2699 // especially on callbacks..
2701 if(this.isVisible()){
2704 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2705 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2706 Roo.log("New Dialog Message:" + options.msg )
2707 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2708 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2711 var d = this.getDialog();
2713 d.setTitle(opt.title || " ");
2714 d.close.setDisplayed(opt.closable !== false);
2715 activeTextEl = textboxEl;
2716 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2721 textareaEl.setHeight(typeof opt.multiline == "number" ?
2722 opt.multiline : this.defaultTextHeight);
2723 activeTextEl = textareaEl;
2732 progressEl.setDisplayed(opt.progress === true);
2733 this.updateProgress(0);
2734 activeTextEl.dom.value = opt.value || "";
2736 dlg.setDefaultButton(activeTextEl);
2738 var bs = opt.buttons;
2742 }else if(bs && bs.yes){
2743 db = buttons["yes"];
2745 dlg.setDefaultButton(db);
2747 bwidth = updateButtons(opt.buttons);
2748 this.updateText(opt.msg);
2750 d.el.addClass(opt.cls);
2752 d.proxyDrag = opt.proxyDrag === true;
2753 d.modal = opt.modal !== false;
2754 d.mask = opt.modal !== false ? mask : false;
2756 // force it to the end of the z-index stack so it gets a cursor in FF
2757 document.body.appendChild(dlg.el.dom);
2758 d.animateTarget = null;
2759 d.show(options.animEl);
2765 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2766 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2767 * and closing the message box when the process is complete.
2768 * @param {String} title The title bar text
2769 * @param {String} msg The message box body text
2770 * @return {Roo.MessageBox} This message box
2772 progress : function(title, msg){
2779 minWidth: this.minProgressWidth,
2786 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2787 * If a callback function is passed it will be called after the user clicks the button, and the
2788 * id of the button that was clicked will be passed as the only parameter to the callback
2789 * (could also be the top-right close button).
2790 * @param {String} title The title bar text
2791 * @param {String} msg The message box body text
2792 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2793 * @param {Object} scope (optional) The scope of the callback function
2794 * @return {Roo.MessageBox} This message box
2796 alert : function(title, msg, fn, scope){
2809 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2810 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2811 * You are responsible for closing the message box when the process is complete.
2812 * @param {String} msg The message box body text
2813 * @param {String} title (optional) The title bar text
2814 * @return {Roo.MessageBox} This message box
2816 wait : function(msg, title){
2827 waitTimer = Roo.TaskMgr.start({
2829 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2837 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2838 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2839 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2840 * @param {String} title The title bar text
2841 * @param {String} msg The message box body text
2842 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2843 * @param {Object} scope (optional) The scope of the callback function
2844 * @return {Roo.MessageBox} This message box
2846 confirm : function(title, msg, fn, scope){
2850 buttons: this.YESNO,
2859 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2860 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2861 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2862 * (could also be the top-right close button) and the text that was entered will be passed as the two
2863 * parameters to the callback.
2864 * @param {String} title The title bar text
2865 * @param {String} msg The message box body text
2866 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2867 * @param {Object} scope (optional) The scope of the callback function
2868 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2869 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2870 * @return {Roo.MessageBox} This message box
2872 prompt : function(title, msg, fn, scope, multiline){
2876 buttons: this.OKCANCEL,
2881 multiline: multiline,
2888 * Button config that displays a single OK button
2893 * Button config that displays Yes and No buttons
2896 YESNO : {yes:true, no:true},
2898 * Button config that displays OK and Cancel buttons
2901 OKCANCEL : {ok:true, cancel:true},
2903 * Button config that displays Yes, No and Cancel buttons
2906 YESNOCANCEL : {yes:true, no:true, cancel:true},
2909 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2912 defaultTextHeight : 75,
2914 * The maximum width in pixels of the message box (defaults to 600)
2919 * The minimum width in pixels of the message box (defaults to 100)
2924 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2925 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2928 minProgressWidth : 250,
2930 * An object containing the default button text strings that can be overriden for localized language support.
2931 * Supported properties are: ok, cancel, yes and no.
2932 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2945 * Shorthand for {@link Roo.MessageBox}
2947 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2948 Roo.Msg = Roo.Msg || Roo.MessageBox;
2957 * @class Roo.bootstrap.Navbar
2958 * @extends Roo.bootstrap.Component
2959 * Bootstrap Navbar class
2962 * Create a new Navbar
2963 * @param {Object} config The config object
2967 Roo.bootstrap.Navbar = function(config){
2968 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2972 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2981 getAutoCreate : function(){
2984 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2988 initEvents :function ()
2990 //Roo.log(this.el.select('.navbar-toggle',true));
2991 this.el.select('.navbar-toggle',true).on('click', function() {
2992 // Roo.log('click');
2993 this.el.select('.navbar-collapse',true).toggleClass('in');
3001 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3003 var size = this.el.getSize();
3004 this.maskEl.setSize(size.width, size.height);
3005 this.maskEl.enableDisplayMode("block");
3014 getChildContainer : function()
3016 if (this.el.select('.collapse').getCount()) {
3017 return this.el.select('.collapse',true).first();
3050 * @class Roo.bootstrap.NavSimplebar
3051 * @extends Roo.bootstrap.Navbar
3052 * Bootstrap Sidebar class
3054 * @cfg {Boolean} inverse is inverted color
3056 * @cfg {String} type (nav | pills | tabs)
3057 * @cfg {Boolean} arrangement stacked | justified
3058 * @cfg {String} align (left | right) alignment
3060 * @cfg {Boolean} main (true|false) main nav bar? default false
3061 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3063 * @cfg {String} tag (header|footer|nav|div) default is nav
3069 * Create a new Sidebar
3070 * @param {Object} config The config object
3074 Roo.bootstrap.NavSimplebar = function(config){
3075 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3078 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
3094 getAutoCreate : function(){
3098 tag : this.tag || 'div',
3111 this.type = this.type || 'nav';
3112 if (['tabs','pills'].indexOf(this.type)!==-1) {
3113 cfg.cn[0].cls += ' nav-' + this.type
3117 if (this.type!=='nav') {
3118 Roo.log('nav type must be nav/tabs/pills')
3120 cfg.cn[0].cls += ' navbar-nav'
3126 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3127 cfg.cn[0].cls += ' nav-' + this.arrangement;
3131 if (this.align === 'right') {
3132 cfg.cn[0].cls += ' navbar-right';
3136 cfg.cls += ' navbar-inverse';
3163 * @class Roo.bootstrap.NavHeaderbar
3164 * @extends Roo.bootstrap.NavSimplebar
3165 * Bootstrap Sidebar class
3167 * @cfg {String} brand what is brand
3168 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3169 * @cfg {String} brand_href href of the brand
3170 * @cfg {Boolean} srButton generate the sr-only button (true | false) default true
3171 * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3174 * Create a new Sidebar
3175 * @param {Object} config The config object
3179 Roo.bootstrap.NavHeaderbar = function(config){
3180 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3183 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
3191 getAutoCreate : function(){
3194 tag: this.nav || 'nav',
3203 cls: 'navbar-header',
3208 cls: 'navbar-toggle',
3209 'data-toggle': 'collapse',
3214 html: 'Toggle navigation'
3236 cls: 'collapse navbar-collapse',
3240 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3242 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3243 cfg.cls += ' navbar-' + this.position;
3245 // tag can override this..
3247 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3250 if (this.brand !== '') {
3253 href: this.brand_href ? this.brand_href : '#',
3254 cls: 'navbar-brand',
3262 cfg.cls += ' main-nav';
3270 initEvents : function()
3272 Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3274 if (this.autohide) {
3279 Roo.get(document).on('scroll',function(e) {
3280 var ns = Roo.get(document).getScroll().top;
3281 var os = prevScroll;
3285 ft.removeClass('slideDown');
3286 ft.addClass('slideUp');
3289 ft.removeClass('slideUp');
3290 ft.addClass('slideDown');
3314 * @class Roo.bootstrap.NavSidebar
3315 * @extends Roo.bootstrap.Navbar
3316 * Bootstrap Sidebar class
3319 * Create a new Sidebar
3320 * @param {Object} config The config object
3324 Roo.bootstrap.NavSidebar = function(config){
3325 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3328 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3330 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3332 getAutoCreate : function(){
3337 cls: 'sidebar sidebar-nav'
3359 * @class Roo.bootstrap.NavGroup
3360 * @extends Roo.bootstrap.Component
3361 * Bootstrap NavGroup class
3362 * @cfg {String} align left | right
3363 * @cfg {Boolean} inverse false | true
3364 * @cfg {String} type (nav|pills|tab) default nav
3365 * @cfg {String} navId - reference Id for navbar.
3369 * Create a new nav group
3370 * @param {Object} config The config object
3373 Roo.bootstrap.NavGroup = function(config){
3374 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3377 Roo.bootstrap.NavGroup.register(this);
3381 * Fires when the active item changes
3382 * @param {Roo.bootstrap.NavGroup} this
3383 * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3384 * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item
3391 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3402 getAutoCreate : function()
3404 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3411 if (['tabs','pills'].indexOf(this.type)!==-1) {
3412 cfg.cls += ' nav-' + this.type
3414 if (this.type!=='nav') {
3415 Roo.log('nav type must be nav/tabs/pills')
3417 cfg.cls += ' navbar-nav'
3420 if (this.parent().sidebar) {
3423 cls: 'dashboard-menu sidebar-menu'
3429 if (this.form === true) {
3435 if (this.align === 'right') {
3436 cfg.cls += ' navbar-right';
3438 cfg.cls += ' navbar-left';
3442 if (this.align === 'right') {
3443 cfg.cls += ' navbar-right';
3447 cfg.cls += ' navbar-inverse';
3455 * sets the active Navigation item
3456 * @param {Roo.bootstrap.NavItem} the new current navitem
3458 setActiveItem : function(item)
3461 Roo.each(this.navItems, function(v){
3466 v.setActive(false, true);
3473 item.setActive(true, true);
3474 this.fireEvent('changed', this, item, prev);
3479 * gets the active Navigation item
3480 * @return {Roo.bootstrap.NavItem} the current navitem
3482 getActive : function()
3486 Roo.each(this.navItems, function(v){
3497 indexOfNav : function()
3501 Roo.each(this.navItems, function(v,i){
3512 * adds a Navigation item
3513 * @param {Roo.bootstrap.NavItem} the navitem to add
3515 addItem : function(cfg)
3517 var cn = new Roo.bootstrap.NavItem(cfg);
3519 cn.parentId = this.id;
3520 cn.onRender(this.el, null);
3524 * register a Navigation item
3525 * @param {Roo.bootstrap.NavItem} the navitem to add
3527 register : function(item)
3529 this.navItems.push( item);
3530 item.navId = this.navId;
3535 * clear all the Navigation item
3538 clearAll : function()
3541 this.el.dom.innerHTML = '';
3544 getNavItem: function(tabId)
3547 Roo.each(this.navItems, function(e) {
3548 if (e.tabId == tabId) {
3558 setActiveNext : function()
3560 var i = this.indexOfNav(this.getActive());
3561 if (i > this.navItems.length) {
3564 this.setActiveItem(this.navItems[i+1]);
3566 setActivePrev : function()
3568 var i = this.indexOfNav(this.getActive());
3572 this.setActiveItem(this.navItems[i-1]);
3574 clearWasActive : function(except) {
3575 Roo.each(this.navItems, function(e) {
3576 if (e.tabId != except.tabId && e.was_active) {
3577 e.was_active = false;
3584 getWasActive : function ()
3587 Roo.each(this.navItems, function(e) {
3602 Roo.apply(Roo.bootstrap.NavGroup, {
3606 * register a Navigation Group
3607 * @param {Roo.bootstrap.NavGroup} the navgroup to add
3609 register : function(navgrp)
3611 this.groups[navgrp.navId] = navgrp;
3615 * fetch a Navigation Group based on the navigation ID
3616 * @param {string} the navgroup to add
3617 * @returns {Roo.bootstrap.NavGroup} the navgroup
3619 get: function(navId) {
3620 if (typeof(this.groups[navId]) == 'undefined') {
3622 //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
3624 return this.groups[navId] ;
3639 * @class Roo.bootstrap.NavItem
3640 * @extends Roo.bootstrap.Component
3641 * Bootstrap Navbar.NavItem class
3642 * @cfg {String} href link to
3643 * @cfg {String} html content of button
3644 * @cfg {String} badge text inside badge
3645 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3646 * @cfg {String} glyphicon name of glyphicon
3647 * @cfg {String} icon name of font awesome icon
3648 * @cfg {Boolean} active Is item active
3649 * @cfg {Boolean} disabled Is item disabled
3651 * @cfg {Boolean} preventDefault (true | false) default false
3652 * @cfg {String} tabId the tab that this item activates.
3653 * @cfg {String} tagtype (a|span) render as a href or span?
3656 * Create a new Navbar Item
3657 * @param {Object} config The config object
3659 Roo.bootstrap.NavItem = function(config){
3660 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3665 * The raw click event for the entire grid.
3666 * @param {Roo.EventObject} e
3671 * Fires when the active item active state changes
3672 * @param {Roo.bootstrap.NavItem} this
3673 * @param {boolean} state the new state
3681 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
3689 preventDefault : false,
3696 getAutoCreate : function(){
3704 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3706 if (this.disabled) {
3707 cfg.cls += ' disabled';
3710 if (this.href || this.html || this.glyphicon || this.icon) {
3714 href : this.href || "#",
3715 html: this.html || ''
3720 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
3723 if(this.glyphicon) {
3724 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> ' + cfg.cn[0].html;
3729 cfg.cn[0].html += " <span class='caret'></span>";
3733 if (this.badge !== '') {
3735 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3743 initEvents: function()
3745 if (typeof (this.menu) != 'undefined') {
3746 this.menu.parentType = this.xtype;
3747 this.menu.triggerEl = this.el;
3748 this.addxtype(Roo.apply({}, this.menu));
3751 this.el.select('a',true).on('click', this.onClick, this);
3753 if(this.tagtype == 'span'){
3754 this.el.select('span',true).on('click', this.onClick, this);
3757 // at this point parent should be available..
3758 this.parent().register(this);
3761 onClick : function(e)
3764 if(this.preventDefault){
3767 if (this.disabled) {
3771 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3772 if (tg && tg.transition) {
3773 Roo.log("waiting for the transitionend");
3777 Roo.log("fire event clicked");
3778 if(this.fireEvent('click', this, e) === false){
3782 if(this.tagtype == 'span'){
3786 var p = this.parent();
3787 if (['tabs','pills'].indexOf(p.type)!==-1) {
3788 if (typeof(p.setActiveItem) !== 'undefined') {
3789 p.setActiveItem(this);
3792 // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
3793 if (p.parentType == 'NavHeaderbar' && !this.menu) {
3794 // remove the collapsed menu expand...
3795 p.parent().el.select('.navbar-collapse',true).removeClass('in');
3800 isActive: function () {
3803 setActive : function(state, fire, is_was_active)
3805 if (this.active && !state & this.navId) {
3806 this.was_active = true;
3807 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3809 nv.clearWasActive(this);
3813 this.active = state;
3816 this.el.removeClass('active');
3817 } else if (!this.el.hasClass('active')) {
3818 this.el.addClass('active');
3821 this.fireEvent('changed', this, state);
3824 // show a panel if it's registered and related..
3826 if (!this.navId || !this.tabId || !state || is_was_active) {
3830 var tg = Roo.bootstrap.TabGroup.get(this.navId);
3834 var pan = tg.getPanelByName(this.tabId);
3838 // if we can not flip to new panel - go back to old nav highlight..
3839 if (false == tg.showPanel(pan)) {
3840 var nv = Roo.bootstrap.NavGroup.get(this.navId);
3842 var onav = nv.getWasActive();
3844 onav.setActive(true, false, true);
3853 // this should not be here...
3854 setDisabled : function(state)
3856 this.disabled = state;
3858 this.el.removeClass('disabled');
3859 } else if (!this.el.hasClass('disabled')) {
3860 this.el.addClass('disabled');
3873 * <span> icon </span>
3874 * <span> text </span>
3875 * <span>badge </span>
3879 * @class Roo.bootstrap.NavSidebarItem
3880 * @extends Roo.bootstrap.NavItem
3881 * Bootstrap Navbar.NavSidebarItem class
3883 * Create a new Navbar Button
3884 * @param {Object} config The config object
3886 Roo.bootstrap.NavSidebarItem = function(config){
3887 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3892 * The raw click event for the entire grid.
3893 * @param {Roo.EventObject} e
3898 * Fires when the active item active state changes
3899 * @param {Roo.bootstrap.NavSidebarItem} this
3900 * @param {boolean} state the new state
3908 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
3911 getAutoCreate : function(){
3916 href : this.href || '#',
3928 html : this.html || ''
3933 cfg.cls += ' active';
3937 if (this.glyphicon || this.icon) {
3938 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
3939 a.cn.push({ tag : 'i', cls : c }) ;
3944 if (this.badge !== '') {
3945 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
3949 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3950 a.cls += 'dropdown-toggle treeview' ;
3974 * @class Roo.bootstrap.Row
3975 * @extends Roo.bootstrap.Component
3976 * Bootstrap Row class (contains columns...)
3980 * @param {Object} config The config object
3983 Roo.bootstrap.Row = function(config){
3984 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3987 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3989 getAutoCreate : function(){
4008 * @class Roo.bootstrap.Element
4009 * @extends Roo.bootstrap.Component
4010 * Bootstrap Element class
4011 * @cfg {String} html contents of the element
4012 * @cfg {String} tag tag of the element
4013 * @cfg {String} cls class of the element
4016 * Create a new Element
4017 * @param {Object} config The config object
4020 Roo.bootstrap.Element = function(config){
4021 Roo.bootstrap.Element.superclass.constructor.call(this, config);
4024 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
4031 getAutoCreate : function(){
4056 * @class Roo.bootstrap.Pagination
4057 * @extends Roo.bootstrap.Component
4058 * Bootstrap Pagination class
4059 * @cfg {String} size xs | sm | md | lg
4060 * @cfg {Boolean} inverse false | true
4063 * Create a new Pagination
4064 * @param {Object} config The config object
4067 Roo.bootstrap.Pagination = function(config){
4068 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4071 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
4077 getAutoCreate : function(){
4083 cfg.cls += ' inverse';
4089 cfg.cls += " " + this.cls;
4107 * @class Roo.bootstrap.PaginationItem
4108 * @extends Roo.bootstrap.Component
4109 * Bootstrap PaginationItem class
4110 * @cfg {String} html text
4111 * @cfg {String} href the link
4112 * @cfg {Boolean} preventDefault (true | false) default true
4113 * @cfg {Boolean} active (true | false) default false
4117 * Create a new PaginationItem
4118 * @param {Object} config The config object
4122 Roo.bootstrap.PaginationItem = function(config){
4123 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4128 * The raw click event for the entire grid.
4129 * @param {Roo.EventObject} e
4135 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
4139 preventDefault: true,
4143 getAutoCreate : function(){
4149 href : this.href ? this.href : '#',
4150 html : this.html ? this.html : ''
4160 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4166 initEvents: function() {
4168 this.el.on('click', this.onClick, this);
4171 onClick : function(e)
4173 Roo.log('PaginationItem on click ');
4174 if(this.preventDefault){
4178 this.fireEvent('click', this, e);
4194 * @class Roo.bootstrap.Slider
4195 * @extends Roo.bootstrap.Component
4196 * Bootstrap Slider class
4199 * Create a new Slider
4200 * @param {Object} config The config object
4203 Roo.bootstrap.Slider = function(config){
4204 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4207 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
4209 getAutoCreate : function(){
4213 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4217 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4229 * Ext JS Library 1.1.1
4230 * Copyright(c) 2006-2007, Ext JS, LLC.
4232 * Originally Released Under LGPL - original licence link has changed is not relivant.
4235 * <script type="text/javascript">
4240 * @class Roo.grid.ColumnModel
4241 * @extends Roo.util.Observable
4242 * This is the default implementation of a ColumnModel used by the Grid. It defines
4243 * the columns in the grid.
4246 var colModel = new Roo.grid.ColumnModel([
4247 {header: "Ticker", width: 60, sortable: true, locked: true},
4248 {header: "Company Name", width: 150, sortable: true},
4249 {header: "Market Cap.", width: 100, sortable: true},
4250 {header: "$ Sales", width: 100, sortable: true, renderer: money},
4251 {header: "Employees", width: 100, sortable: true, resizable: false}
4256 * The config options listed for this class are options which may appear in each
4257 * individual column definition.
4258 * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
4260 * @param {Object} config An Array of column config objects. See this class's
4261 * config objects for details.
4263 Roo.grid.ColumnModel = function(config){
4265 * The config passed into the constructor
4267 this.config = config;
4270 // if no id, create one
4271 // if the column does not have a dataIndex mapping,
4272 // map it to the order it is in the config
4273 for(var i = 0, len = config.length; i < len; i++){
4275 if(typeof c.dataIndex == "undefined"){
4278 if(typeof c.renderer == "string"){
4279 c.renderer = Roo.util.Format[c.renderer];
4281 if(typeof c.id == "undefined"){
4284 if(c.editor && c.editor.xtype){
4285 c.editor = Roo.factory(c.editor, Roo.grid);
4287 if(c.editor && c.editor.isFormField){
4288 c.editor = new Roo.grid.GridEditor(c.editor);
4290 this.lookup[c.id] = c;
4294 * The width of columns which have no width specified (defaults to 100)
4297 this.defaultWidth = 100;
4300 * Default sortable of columns which have no sortable specified (defaults to false)
4303 this.defaultSortable = false;
4307 * @event widthchange
4308 * Fires when the width of a column changes.
4309 * @param {ColumnModel} this
4310 * @param {Number} columnIndex The column index
4311 * @param {Number} newWidth The new width
4313 "widthchange": true,
4315 * @event headerchange
4316 * Fires when the text of a header changes.
4317 * @param {ColumnModel} this
4318 * @param {Number} columnIndex The column index
4319 * @param {Number} newText The new header text
4321 "headerchange": true,
4323 * @event hiddenchange
4324 * Fires when a column is hidden or "unhidden".
4325 * @param {ColumnModel} this
4326 * @param {Number} columnIndex The column index
4327 * @param {Boolean} hidden true if hidden, false otherwise
4329 "hiddenchange": true,
4331 * @event columnmoved
4332 * Fires when a column is moved.
4333 * @param {ColumnModel} this
4334 * @param {Number} oldIndex
4335 * @param {Number} newIndex
4337 "columnmoved" : true,
4339 * @event columlockchange
4340 * Fires when a column's locked state is changed
4341 * @param {ColumnModel} this
4342 * @param {Number} colIndex
4343 * @param {Boolean} locked true if locked
4345 "columnlockchange" : true
4347 Roo.grid.ColumnModel.superclass.constructor.call(this);
4349 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
4351 * @cfg {String} header The header text to display in the Grid view.
4354 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
4355 * {@link Roo.data.Record} definition from which to draw the column's value. If not
4356 * specified, the column's index is used as an index into the Record's data Array.
4359 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
4360 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
4363 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
4364 * Defaults to the value of the {@link #defaultSortable} property.
4365 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
4368 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
4371 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
4374 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
4377 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
4380 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
4381 * given the cell's data value. See {@link #setRenderer}. If not specified, the
4382 * default renderer uses the raw data value. If an object is returned (bootstrap only)
4383 * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
4386 * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor
4389 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
4392 * @cfg {String} cursor (Optional)
4395 * Returns the id of the column at the specified index.
4396 * @param {Number} index The column index
4397 * @return {String} the id
4399 getColumnId : function(index){
4400 return this.config[index].id;
4404 * Returns the column for a specified id.
4405 * @param {String} id The column id
4406 * @return {Object} the column
4408 getColumnById : function(id){
4409 return this.lookup[id];
4414 * Returns the column for a specified dataIndex.
4415 * @param {String} dataIndex The column dataIndex
4416 * @return {Object|Boolean} the column or false if not found
4418 getColumnByDataIndex: function(dataIndex){
4419 var index = this.findColumnIndex(dataIndex);
4420 return index > -1 ? this.config[index] : false;
4424 * Returns the index for a specified column id.
4425 * @param {String} id The column id
4426 * @return {Number} the index, or -1 if not found
4428 getIndexById : function(id){
4429 for(var i = 0, len = this.config.length; i < len; i++){
4430 if(this.config[i].id == id){
4438 * Returns the index for a specified column dataIndex.
4439 * @param {String} dataIndex The column dataIndex
4440 * @return {Number} the index, or -1 if not found
4443 findColumnIndex : function(dataIndex){
4444 for(var i = 0, len = this.config.length; i < len; i++){
4445 if(this.config[i].dataIndex == dataIndex){
4453 moveColumn : function(oldIndex, newIndex){
4454 var c = this.config[oldIndex];
4455 this.config.splice(oldIndex, 1);
4456 this.config.splice(newIndex, 0, c);
4457 this.dataMap = null;
4458 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4461 isLocked : function(colIndex){
4462 return this.config[colIndex].locked === true;
4465 setLocked : function(colIndex, value, suppressEvent){
4466 if(this.isLocked(colIndex) == value){
4469 this.config[colIndex].locked = value;
4471 this.fireEvent("columnlockchange", this, colIndex, value);
4475 getTotalLockedWidth : function(){
4477 for(var i = 0; i < this.config.length; i++){
4478 if(this.isLocked(i) && !this.isHidden(i)){
4479 this.totalWidth += this.getColumnWidth(i);
4485 getLockedCount : function(){
4486 for(var i = 0, len = this.config.length; i < len; i++){
4487 if(!this.isLocked(i)){
4494 * Returns the number of columns.
4497 getColumnCount : function(visibleOnly){
4498 if(visibleOnly === true){
4500 for(var i = 0, len = this.config.length; i < len; i++){
4501 if(!this.isHidden(i)){
4507 return this.config.length;
4511 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4512 * @param {Function} fn
4513 * @param {Object} scope (optional)
4514 * @return {Array} result
4516 getColumnsBy : function(fn, scope){
4518 for(var i = 0, len = this.config.length; i < len; i++){
4519 var c = this.config[i];
4520 if(fn.call(scope||this, c, i) === true){
4528 * Returns true if the specified column is sortable.
4529 * @param {Number} col The column index
4532 isSortable : function(col){
4533 if(typeof this.config[col].sortable == "undefined"){
4534 return this.defaultSortable;
4536 return this.config[col].sortable;
4540 * Returns the rendering (formatting) function defined for the column.
4541 * @param {Number} col The column index.
4542 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4544 getRenderer : function(col){
4545 if(!this.config[col].renderer){
4546 return Roo.grid.ColumnModel.defaultRenderer;
4548 return this.config[col].renderer;
4552 * Sets the rendering (formatting) function for a column.
4553 * @param {Number} col The column index
4554 * @param {Function} fn The function to use to process the cell's raw data
4555 * to return HTML markup for the grid view. The render function is called with
4556 * the following parameters:<ul>
4557 * <li>Data value.</li>
4558 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4559 * <li>css A CSS style string to apply to the table cell.</li>
4560 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4561 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4562 * <li>Row index</li>
4563 * <li>Column index</li>
4564 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4566 setRenderer : function(col, fn){
4567 this.config[col].renderer = fn;
4571 * Returns the width for the specified column.
4572 * @param {Number} col The column index
4575 getColumnWidth : function(col){
4576 return this.config[col].width * 1 || this.defaultWidth;
4580 * Sets the width for a column.
4581 * @param {Number} col The column index
4582 * @param {Number} width The new width
4584 setColumnWidth : function(col, width, suppressEvent){
4585 this.config[col].width = width;
4586 this.totalWidth = null;
4588 this.fireEvent("widthchange", this, col, width);
4593 * Returns the total width of all columns.
4594 * @param {Boolean} includeHidden True to include hidden column widths
4597 getTotalWidth : function(includeHidden){
4598 if(!this.totalWidth){
4599 this.totalWidth = 0;
4600 for(var i = 0, len = this.config.length; i < len; i++){
4601 if(includeHidden || !this.isHidden(i)){
4602 this.totalWidth += this.getColumnWidth(i);
4606 return this.totalWidth;
4610 * Returns the header for the specified column.
4611 * @param {Number} col The column index
4614 getColumnHeader : function(col){
4615 return this.config[col].header;
4619 * Sets the header for a column.
4620 * @param {Number} col The column index
4621 * @param {String} header The new header
4623 setColumnHeader : function(col, header){
4624 this.config[col].header = header;
4625 this.fireEvent("headerchange", this, col, header);
4629 * Returns the tooltip for the specified column.
4630 * @param {Number} col The column index
4633 getColumnTooltip : function(col){
4634 return this.config[col].tooltip;
4637 * Sets the tooltip for a column.
4638 * @param {Number} col The column index
4639 * @param {String} tooltip The new tooltip
4641 setColumnTooltip : function(col, tooltip){
4642 this.config[col].tooltip = tooltip;
4646 * Returns the dataIndex for the specified column.
4647 * @param {Number} col The column index
4650 getDataIndex : function(col){
4651 return this.config[col].dataIndex;
4655 * Sets the dataIndex for a column.
4656 * @param {Number} col The column index
4657 * @param {Number} dataIndex The new dataIndex
4659 setDataIndex : function(col, dataIndex){
4660 this.config[col].dataIndex = dataIndex;
4666 * Returns true if the cell is editable.
4667 * @param {Number} colIndex The column index
4668 * @param {Number} rowIndex The row index
4671 isCellEditable : function(colIndex, rowIndex){
4672 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4676 * Returns the editor defined for the cell/column.
4677 * return false or null to disable editing.
4678 * @param {Number} colIndex The column index
4679 * @param {Number} rowIndex The row index
4682 getCellEditor : function(colIndex, rowIndex){
4683 return this.config[colIndex].editor;
4687 * Sets if a column is editable.
4688 * @param {Number} col The column index
4689 * @param {Boolean} editable True if the column is editable
4691 setEditable : function(col, editable){
4692 this.config[col].editable = editable;
4697 * Returns true if the column is hidden.
4698 * @param {Number} colIndex The column index
4701 isHidden : function(colIndex){
4702 return this.config[colIndex].hidden;
4707 * Returns true if the column width cannot be changed
4709 isFixed : function(colIndex){
4710 return this.config[colIndex].fixed;
4714 * Returns true if the column can be resized
4717 isResizable : function(colIndex){
4718 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4721 * Sets if a column is hidden.
4722 * @param {Number} colIndex The column index
4723 * @param {Boolean} hidden True if the column is hidden
4725 setHidden : function(colIndex, hidden){
4726 this.config[colIndex].hidden = hidden;
4727 this.totalWidth = null;
4728 this.fireEvent("hiddenchange", this, colIndex, hidden);
4732 * Sets the editor for a column.
4733 * @param {Number} col The column index
4734 * @param {Object} editor The editor object
4736 setEditor : function(col, editor){
4737 this.config[col].editor = editor;
4741 Roo.grid.ColumnModel.defaultRenderer = function(value){
4742 if(typeof value == "string" && value.length < 1){
4748 // Alias for backwards compatibility
4749 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4752 * Ext JS Library 1.1.1
4753 * Copyright(c) 2006-2007, Ext JS, LLC.
4755 * Originally Released Under LGPL - original licence link has changed is not relivant.
4758 * <script type="text/javascript">
4762 * @class Roo.LoadMask
4763 * A simple utility class for generically masking elements while loading data. If the element being masked has
4764 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4765 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
4766 * element's UpdateManager load indicator and will be destroyed after the initial load.
4768 * Create a new LoadMask
4769 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4770 * @param {Object} config The config object
4772 Roo.LoadMask = function(el, config){
4773 this.el = Roo.get(el);
4774 Roo.apply(this, config);
4776 this.store.on('beforeload', this.onBeforeLoad, this);
4777 this.store.on('load', this.onLoad, this);
4778 this.store.on('loadexception', this.onLoadException, this);
4779 this.removeMask = false;
4781 var um = this.el.getUpdateManager();
4782 um.showLoadIndicator = false; // disable the default indicator
4783 um.on('beforeupdate', this.onBeforeLoad, this);
4784 um.on('update', this.onLoad, this);
4785 um.on('failure', this.onLoad, this);
4786 this.removeMask = true;
4790 Roo.LoadMask.prototype = {
4792 * @cfg {Boolean} removeMask
4793 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4794 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
4798 * The text to display in a centered loading message box (defaults to 'Loading...')
4802 * @cfg {String} msgCls
4803 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4805 msgCls : 'x-mask-loading',
4808 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4814 * Disables the mask to prevent it from being displayed
4816 disable : function(){
4817 this.disabled = true;
4821 * Enables the mask so that it can be displayed
4823 enable : function(){
4824 this.disabled = false;
4827 onLoadException : function()
4831 if (typeof(arguments[3]) != 'undefined') {
4832 Roo.MessageBox.alert("Error loading",arguments[3]);
4836 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4837 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4846 this.el.unmask(this.removeMask);
4851 this.el.unmask(this.removeMask);
4855 onBeforeLoad : function(){
4857 this.el.mask(this.msg, this.msgCls);
4862 destroy : function(){
4864 this.store.un('beforeload', this.onBeforeLoad, this);
4865 this.store.un('load', this.onLoad, this);
4866 this.store.un('loadexception', this.onLoadException, this);
4868 var um = this.el.getUpdateManager();
4869 um.un('beforeupdate', this.onBeforeLoad, this);
4870 um.un('update', this.onLoad, this);
4871 um.un('failure', this.onLoad, this);
4882 * @class Roo.bootstrap.Table
4883 * @extends Roo.bootstrap.Component
4884 * Bootstrap Table class
4885 * @cfg {String} cls table class
4886 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4887 * @cfg {String} bgcolor Specifies the background color for a table
4888 * @cfg {Number} border Specifies whether the table cells should have borders or not
4889 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4890 * @cfg {Number} cellspacing Specifies the space between cells
4891 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4892 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4893 * @cfg {String} sortable Specifies that the table should be sortable
4894 * @cfg {String} summary Specifies a summary of the content of a table
4895 * @cfg {Number} width Specifies the width of a table
4896 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4898 * @cfg {boolean} striped Should the rows be alternative striped
4899 * @cfg {boolean} bordered Add borders to the table
4900 * @cfg {boolean} hover Add hover highlighting
4901 * @cfg {boolean} condensed Format condensed
4902 * @cfg {boolean} responsive Format condensed
4903 * @cfg {Boolean} loadMask (true|false) default false
4904 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4905 * @cfg {Boolean} thead (true|false) generate thead, default true
4906 * @cfg {Boolean} RowSelection (true|false) default false
4907 * @cfg {Boolean} CellSelection (true|false) default false
4908 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
4912 * Create a new Table
4913 * @param {Object} config The config object
4916 Roo.bootstrap.Table = function(config){
4917 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4920 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4921 this.sm = this.selModel;
4922 this.sm.xmodule = this.xmodule || false;
4924 if (this.cm && typeof(this.cm.config) == 'undefined') {
4925 this.colModel = new Roo.grid.ColumnModel(this.cm);
4926 this.cm = this.colModel;
4927 this.cm.xmodule = this.xmodule || false;
4930 this.store= Roo.factory(this.store, Roo.data);
4931 this.ds = this.store;
4932 this.ds.xmodule = this.xmodule || false;
4935 if (this.footer && this.store) {
4936 this.footer.dataSource = this.ds;
4937 this.footer = Roo.factory(this.footer);
4944 * Fires when a cell is clicked
4945 * @param {Roo.bootstrap.Table} this
4946 * @param {Roo.Element} el
4947 * @param {Number} rowIndex
4948 * @param {Number} columnIndex
4949 * @param {Roo.EventObject} e
4953 * @event celldblclick
4954 * Fires when a cell is double clicked
4955 * @param {Roo.bootstrap.Table} this
4956 * @param {Roo.Element} el
4957 * @param {Number} rowIndex
4958 * @param {Number} columnIndex
4959 * @param {Roo.EventObject} e
4961 "celldblclick" : true,
4964 * Fires when a row is clicked
4965 * @param {Roo.bootstrap.Table} this
4966 * @param {Roo.Element} el
4967 * @param {Number} rowIndex
4968 * @param {Roo.EventObject} e
4972 * @event rowdblclick
4973 * Fires when a row is double clicked
4974 * @param {Roo.bootstrap.Table} this
4975 * @param {Roo.Element} el
4976 * @param {Number} rowIndex
4977 * @param {Roo.EventObject} e
4979 "rowdblclick" : true,
4982 * Fires when a mouseover occur
4983 * @param {Roo.bootstrap.Table} this
4984 * @param {Roo.Element} el
4985 * @param {Number} rowIndex
4986 * @param {Number} columnIndex
4987 * @param {Roo.EventObject} e
4992 * Fires when a mouseout occur
4993 * @param {Roo.bootstrap.Table} this
4994 * @param {Roo.Element} el
4995 * @param {Number} rowIndex
4996 * @param {Number} columnIndex
4997 * @param {Roo.EventObject} e
5002 * Fires when a row is rendered, so you can change add a style to it.
5003 * @param {Roo.bootstrap.Table} this
5004 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5011 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5035 RowSelection : false,
5036 CellSelection : false,
5039 // Roo.Element - the tbody
5042 getAutoCreate : function(){
5043 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5052 cfg.cls += ' table-striped';
5056 cfg.cls += ' table-hover';
5058 if (this.bordered) {
5059 cfg.cls += ' table-bordered';
5061 if (this.condensed) {
5062 cfg.cls += ' table-condensed';
5064 if (this.responsive) {
5065 cfg.cls += ' table-responsive';
5069 cfg.cls+= ' ' +this.cls;
5072 // this lot should be simplifed...
5075 cfg.align=this.align;
5078 cfg.bgcolor=this.bgcolor;
5081 cfg.border=this.border;
5083 if (this.cellpadding) {
5084 cfg.cellpadding=this.cellpadding;
5086 if (this.cellspacing) {
5087 cfg.cellspacing=this.cellspacing;
5090 cfg.frame=this.frame;
5093 cfg.rules=this.rules;
5095 if (this.sortable) {
5096 cfg.sortable=this.sortable;
5099 cfg.summary=this.summary;
5102 cfg.width=this.width;
5105 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5108 if(this.store || this.cm){
5110 cfg.cn.push(this.renderHeader());
5113 cfg.cn.push(this.renderBody());
5116 cfg.cn.push(this.renderFooter());
5119 cfg.cls+= ' TableGrid';
5122 return { cn : [ cfg ] };
5125 initEvents : function()
5127 if(!this.store || !this.cm){
5131 //Roo.log('initEvents with ds!!!!');
5133 this.mainBody = this.el.select('tbody', true).first();
5138 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5139 e.on('click', _this.sort, _this);
5142 this.el.on("click", this.onClick, this);
5143 this.el.on("dblclick", this.onDblClick, this);
5145 this.parent().el.setStyle('position', 'relative');
5147 this.footer.parentId = this.id;
5148 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5151 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5153 this.store.on('load', this.onLoad, this);
5154 this.store.on('beforeload', this.onBeforeLoad, this);
5155 this.store.on('update', this.onUpdate, this);
5159 onMouseover : function(e, el)
5161 var cell = Roo.get(el);
5167 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5168 cell = cell.findParent('td', false, true);
5171 var row = cell.findParent('tr', false, true);
5172 var cellIndex = cell.dom.cellIndex;
5173 var rowIndex = row.dom.rowIndex - 1; // start from 0
5175 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5179 onMouseout : function(e, el)
5181 var cell = Roo.get(el);
5187 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5188 cell = cell.findParent('td', false, true);
5191 var row = cell.findParent('tr', false, true);
5192 var cellIndex = cell.dom.cellIndex;
5193 var rowIndex = row.dom.rowIndex - 1; // start from 0
5195 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5199 onClick : function(e, el)
5201 var cell = Roo.get(el);
5203 if(!cell || (!this.CellSelection && !this.RowSelection)){
5208 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5209 cell = cell.findParent('td', false, true);
5212 var row = cell.findParent('tr', false, true);
5213 var cellIndex = cell.dom.cellIndex;
5214 var rowIndex = row.dom.rowIndex - 1;
5216 if(this.CellSelection){
5217 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5220 if(this.RowSelection){
5221 this.fireEvent('rowclick', this, row, rowIndex, e);
5227 onDblClick : function(e,el)
5229 var cell = Roo.get(el);
5231 if(!cell || (!this.CellSelection && !this.RowSelection)){
5235 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5236 cell = cell.findParent('td', false, true);
5239 var row = cell.findParent('tr', false, true);
5240 var cellIndex = cell.dom.cellIndex;
5241 var rowIndex = row.dom.rowIndex - 1;
5243 if(this.CellSelection){
5244 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5247 if(this.RowSelection){
5248 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5252 sort : function(e,el)
5254 var col = Roo.get(el)
5256 if(!col.hasClass('sortable')){
5260 var sort = col.attr('sort');
5263 if(col.hasClass('glyphicon-arrow-up')){
5267 this.store.sortInfo = {field : sort, direction : dir};
5270 Roo.log("calling footer first");
5271 this.footer.onClick('first');
5274 this.store.load({ params : { start : 0 } });
5278 renderHeader : function()
5287 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5289 var config = cm.config[i];
5294 html: cm.getColumnHeader(i)
5297 if(typeof(config.hidden) != 'undefined' && config.hidden){
5298 c.style += ' display:none;';
5301 if(typeof(config.dataIndex) != 'undefined'){
5302 c.sort = config.dataIndex;
5305 if(typeof(config.sortable) != 'undefined' && config.sortable){
5309 if(typeof(config.align) != 'undefined' && config.align.length){
5310 c.style += ' text-align:' + config.align + ';';
5313 if(typeof(config.width) != 'undefined'){
5314 c.style += ' width:' + config.width + 'px;';
5323 renderBody : function()
5333 colspan : this.cm.getColumnCount()
5343 renderFooter : function()
5353 colspan : this.cm.getColumnCount()
5367 Roo.log('ds onload');
5372 var ds = this.store;
5374 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5375 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5377 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5378 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5381 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5382 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5386 var tbody = this.mainBody;
5388 if(ds.getCount() > 0){
5389 ds.data.each(function(d,rowIndex){
5390 var row = this.renderRow(cm, ds, rowIndex);
5392 tbody.createChild(row);
5396 if(row.cellObjects.length){
5397 Roo.each(row.cellObjects, function(r){
5398 _this.renderCellObject(r);
5405 Roo.each(this.el.select('tbody td', true).elements, function(e){
5406 e.on('mouseover', _this.onMouseover, _this);
5409 Roo.each(this.el.select('tbody td', true).elements, function(e){
5410 e.on('mouseout', _this.onMouseout, _this);
5413 //if(this.loadMask){
5414 // this.maskEl.hide();
5419 onUpdate : function(ds,record)
5421 this.refreshRow(record);
5423 onRemove : function(ds, record, index, isUpdate){
5424 if(isUpdate !== true){
5425 this.fireEvent("beforerowremoved", this, index, record);
5427 var bt = this.mainBody.dom;
5429 bt.removeChild(bt.rows[index]);
5432 if(isUpdate !== true){
5433 //this.stripeRows(index);
5434 //this.syncRowHeights(index, index);
5436 this.fireEvent("rowremoved", this, index, record);
5441 refreshRow : function(record){
5442 var ds = this.store, index;
5443 if(typeof record == 'number'){
5445 record = ds.getAt(index);
5447 index = ds.indexOf(record);
5449 this.insertRow(ds, index, true);
5450 this.onRemove(ds, record, index+1, true);
5451 //this.syncRowHeights(index, index);
5453 this.fireEvent("rowupdated", this, index, record);
5456 insertRow : function(dm, rowIndex, isUpdate){
5459 this.fireEvent("beforerowsinserted", this, rowIndex);
5461 //var s = this.getScrollState();
5462 var row = this.renderRow(this.cm, this.store, rowIndex);
5463 // insert before rowIndex..
5464 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5468 if(row.cellObjects.length){
5469 Roo.each(row.cellObjects, function(r){
5470 _this.renderCellObject(r);
5475 this.fireEvent("rowsinserted", this, rowIndex);
5476 //this.syncRowHeights(firstRow, lastRow);
5477 //this.stripeRows(firstRow);
5484 getRowDom : function(rowIndex)
5486 // not sure if I need to check this.. but let's do it anyway..
5487 return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5488 this.mainBody.dom.rows[rowIndex] : false
5490 // returns the object tree for a tr..
5493 renderRow : function(cm, ds, rowIndex) {
5495 var d = ds.getAt(rowIndex);
5502 var cellObjects = [];
5504 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5505 var config = cm.config[i];
5507 var renderer = cm.getRenderer(i);
5511 if(typeof(renderer) !== 'undefined'){
5512 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5514 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5515 // and are rendered into the cells after the row is rendered - using the id for the element.
5517 if(typeof(value) === 'object'){
5527 rowIndex : rowIndex,
5532 this.fireEvent('rowclass', this, rowcfg);
5536 cls : rowcfg.rowClass,
5538 html: (typeof(value) === 'object') ? '' : value
5545 if(typeof(config.hidden) != 'undefined' && config.hidden){
5546 td.style += ' display:none;';
5549 if(typeof(config.align) != 'undefined' && config.align.length){
5550 td.style += ' text-align:' + config.align + ';';
5553 if(typeof(config.width) != 'undefined'){
5554 td.style += ' width:' + config.width + 'px;';
5557 if(typeof(config.cursor) != 'undefined'){
5558 td.style += ' cursor:' + config.cursor + ';';
5565 row.cellObjects = cellObjects;
5573 onBeforeLoad : function()
5575 //Roo.log('ds onBeforeLoad');
5579 //if(this.loadMask){
5580 // this.maskEl.show();
5586 this.el.select('tbody', true).first().dom.innerHTML = '';
5589 getSelectionModel : function(){
5591 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5593 return this.selModel;
5596 * Render the Roo.bootstrap object from renderder
5598 renderCellObject : function(r)
5602 var t = r.cfg.render(r.container);
5605 Roo.each(r.cfg.cn, function(c){
5607 container: t.getChildContainer(),
5610 _this.renderCellObject(child);
5627 * @class Roo.bootstrap.TableCell
5628 * @extends Roo.bootstrap.Component
5629 * Bootstrap TableCell class
5630 * @cfg {String} html cell contain text
5631 * @cfg {String} cls cell class
5632 * @cfg {String} tag cell tag (td|th) default td
5633 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5634 * @cfg {String} align Aligns the content in a cell
5635 * @cfg {String} axis Categorizes cells
5636 * @cfg {String} bgcolor Specifies the background color of a cell
5637 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5638 * @cfg {Number} colspan Specifies the number of columns a cell should span
5639 * @cfg {String} headers Specifies one or more header cells a cell is related to
5640 * @cfg {Number} height Sets the height of a cell
5641 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5642 * @cfg {Number} rowspan Sets the number of rows a cell should span
5643 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5644 * @cfg {String} valign Vertical aligns the content in a cell
5645 * @cfg {Number} width Specifies the width of a cell
5648 * Create a new TableCell
5649 * @param {Object} config The config object
5652 Roo.bootstrap.TableCell = function(config){
5653 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5656 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
5676 getAutoCreate : function(){
5677 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5697 cfg.align=this.align
5703 cfg.bgcolor=this.bgcolor
5706 cfg.charoff=this.charoff
5709 cfg.colspan=this.colspan
5712 cfg.headers=this.headers
5715 cfg.height=this.height
5718 cfg.nowrap=this.nowrap
5721 cfg.rowspan=this.rowspan
5724 cfg.scope=this.scope
5727 cfg.valign=this.valign
5730 cfg.width=this.width
5749 * @class Roo.bootstrap.TableRow
5750 * @extends Roo.bootstrap.Component
5751 * Bootstrap TableRow class
5752 * @cfg {String} cls row class
5753 * @cfg {String} align Aligns the content in a table row
5754 * @cfg {String} bgcolor Specifies a background color for a table row
5755 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5756 * @cfg {String} valign Vertical aligns the content in a table row
5759 * Create a new TableRow
5760 * @param {Object} config The config object
5763 Roo.bootstrap.TableRow = function(config){
5764 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5767 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
5775 getAutoCreate : function(){
5776 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5786 cfg.align = this.align;
5789 cfg.bgcolor = this.bgcolor;
5792 cfg.charoff = this.charoff;
5795 cfg.valign = this.valign;
5813 * @class Roo.bootstrap.TableBody
5814 * @extends Roo.bootstrap.Component
5815 * Bootstrap TableBody class
5816 * @cfg {String} cls element class
5817 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5818 * @cfg {String} align Aligns the content inside the element
5819 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5820 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5823 * Create a new TableBody
5824 * @param {Object} config The config object
5827 Roo.bootstrap.TableBody = function(config){
5828 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5831 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
5839 getAutoCreate : function(){
5840 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5854 cfg.align = this.align;
5857 cfg.charoff = this.charoff;
5860 cfg.valign = this.valign;
5867 // initEvents : function()
5874 // this.store = Roo.factory(this.store, Roo.data);
5875 // this.store.on('load', this.onLoad, this);
5877 // this.store.load();
5881 // onLoad: function ()
5883 // this.fireEvent('load', this);
5893 * Ext JS Library 1.1.1
5894 * Copyright(c) 2006-2007, Ext JS, LLC.
5896 * Originally Released Under LGPL - original licence link has changed is not relivant.
5899 * <script type="text/javascript">
5902 // as we use this in bootstrap.
5903 Roo.namespace('Roo.form');
5905 * @class Roo.form.Action
5906 * Internal Class used to handle form actions
5908 * @param {Roo.form.BasicForm} el The form element or its id
5909 * @param {Object} config Configuration options
5914 // define the action interface
5915 Roo.form.Action = function(form, options){
5917 this.options = options || {};
5920 * Client Validation Failed
5923 Roo.form.Action.CLIENT_INVALID = 'client';
5925 * Server Validation Failed
5928 Roo.form.Action.SERVER_INVALID = 'server';
5930 * Connect to Server Failed
5933 Roo.form.Action.CONNECT_FAILURE = 'connect';
5935 * Reading Data from Server Failed
5938 Roo.form.Action.LOAD_FAILURE = 'load';
5940 Roo.form.Action.prototype = {
5942 failureType : undefined,
5943 response : undefined,
5947 run : function(options){
5952 success : function(response){
5957 handleResponse : function(response){
5961 // default connection failure
5962 failure : function(response){
5964 this.response = response;
5965 this.failureType = Roo.form.Action.CONNECT_FAILURE;
5966 this.form.afterAction(this, false);
5969 processResponse : function(response){
5970 this.response = response;
5971 if(!response.responseText){
5974 this.result = this.handleResponse(response);
5978 // utility functions used internally
5979 getUrl : function(appendParams){
5980 var url = this.options.url || this.form.url || this.form.el.dom.action;
5982 var p = this.getParams();
5984 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5990 getMethod : function(){
5991 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5994 getParams : function(){
5995 var bp = this.form.baseParams;
5996 var p = this.options.params;
5998 if(typeof p == "object"){
5999 p = Roo.urlEncode(Roo.applyIf(p, bp));
6000 }else if(typeof p == 'string' && bp){
6001 p += '&' + Roo.urlEncode(bp);
6004 p = Roo.urlEncode(bp);
6009 createCallback : function(){
6011 success: this.success,
6012 failure: this.failure,
6014 timeout: (this.form.timeout*1000),
6015 upload: this.form.fileUpload ? this.success : undefined
6020 Roo.form.Action.Submit = function(form, options){
6021 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6024 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6027 haveProgress : false,
6028 uploadComplete : false,
6030 // uploadProgress indicator.
6031 uploadProgress : function()
6033 if (!this.form.progressUrl) {
6037 if (!this.haveProgress) {
6038 Roo.MessageBox.progress("Uploading", "Uploading");
6040 if (this.uploadComplete) {
6041 Roo.MessageBox.hide();
6045 this.haveProgress = true;
6047 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6049 var c = new Roo.data.Connection();
6051 url : this.form.progressUrl,
6056 success : function(req){
6057 //console.log(data);
6061 rdata = Roo.decode(req.responseText)
6063 Roo.log("Invalid data from server..");
6067 if (!rdata || !rdata.success) {
6069 Roo.MessageBox.alert(Roo.encode(rdata));
6072 var data = rdata.data;
6074 if (this.uploadComplete) {
6075 Roo.MessageBox.hide();
6080 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6081 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6084 this.uploadProgress.defer(2000,this);
6087 failure: function(data) {
6088 Roo.log('progress url failed ');
6099 // run get Values on the form, so it syncs any secondary forms.
6100 this.form.getValues();
6102 var o = this.options;
6103 var method = this.getMethod();
6104 var isPost = method == 'POST';
6105 if(o.clientValidation === false || this.form.isValid()){
6107 if (this.form.progressUrl) {
6108 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6109 (new Date() * 1) + '' + Math.random());
6114 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6115 form:this.form.el.dom,
6116 url:this.getUrl(!isPost),
6118 params:isPost ? this.getParams() : null,
6119 isUpload: this.form.fileUpload
6122 this.uploadProgress();
6124 }else if (o.clientValidation !== false){ // client validation failed
6125 this.failureType = Roo.form.Action.CLIENT_INVALID;
6126 this.form.afterAction(this, false);
6130 success : function(response)
6132 this.uploadComplete= true;
6133 if (this.haveProgress) {
6134 Roo.MessageBox.hide();
6138 var result = this.processResponse(response);
6139 if(result === true || result.success){
6140 this.form.afterAction(this, true);
6144 this.form.markInvalid(result.errors);
6145 this.failureType = Roo.form.Action.SERVER_INVALID;
6147 this.form.afterAction(this, false);
6149 failure : function(response)
6151 this.uploadComplete= true;
6152 if (this.haveProgress) {
6153 Roo.MessageBox.hide();
6156 this.response = response;
6157 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6158 this.form.afterAction(this, false);
6161 handleResponse : function(response){
6162 if(this.form.errorReader){
6163 var rs = this.form.errorReader.read(response);
6166 for(var i = 0, len = rs.records.length; i < len; i++) {
6167 var r = rs.records[i];
6171 if(errors.length < 1){
6175 success : rs.success,
6181 ret = Roo.decode(response.responseText);
6185 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6195 Roo.form.Action.Load = function(form, options){
6196 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6197 this.reader = this.form.reader;
6200 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6205 Roo.Ajax.request(Roo.apply(
6206 this.createCallback(), {
6207 method:this.getMethod(),
6208 url:this.getUrl(false),
6209 params:this.getParams()
6213 success : function(response){
6215 var result = this.processResponse(response);
6216 if(result === true || !result.success || !result.data){
6217 this.failureType = Roo.form.Action.LOAD_FAILURE;
6218 this.form.afterAction(this, false);
6221 this.form.clearInvalid();
6222 this.form.setValues(result.data);
6223 this.form.afterAction(this, true);
6226 handleResponse : function(response){
6227 if(this.form.reader){
6228 var rs = this.form.reader.read(response);
6229 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6231 success : rs.success,
6235 return Roo.decode(response.responseText);
6239 Roo.form.Action.ACTION_TYPES = {
6240 'load' : Roo.form.Action.Load,
6241 'submit' : Roo.form.Action.Submit
6250 * @class Roo.bootstrap.Form
6251 * @extends Roo.bootstrap.Component
6252 * Bootstrap Form class
6253 * @cfg {String} method GET | POST (default POST)
6254 * @cfg {String} labelAlign top | left (default top)
6255 * @cfg {String} align left | right - for navbars
6256 * @cfg {Boolean} loadMask load mask when submit (default true)
6261 * @param {Object} config The config object
6265 Roo.bootstrap.Form = function(config){
6266 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6269 * @event clientvalidation
6270 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6271 * @param {Form} this
6272 * @param {Boolean} valid true if the form has passed client-side validation
6274 clientvalidation: true,
6276 * @event beforeaction
6277 * Fires before any action is performed. Return false to cancel the action.
6278 * @param {Form} this
6279 * @param {Action} action The action to be performed
6283 * @event actionfailed
6284 * Fires when an action fails.
6285 * @param {Form} this
6286 * @param {Action} action The action that failed
6288 actionfailed : true,
6290 * @event actioncomplete
6291 * Fires when an action is completed.
6292 * @param {Form} this
6293 * @param {Action} action The action that completed
6295 actioncomplete : true
6300 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6303 * @cfg {String} method
6304 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6309 * The URL to use for form actions if one isn't supplied in the action options.
6312 * @cfg {Boolean} fileUpload
6313 * Set to true if this form is a file upload.
6317 * @cfg {Object} baseParams
6318 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6322 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6326 * @cfg {Sting} align (left|right) for navbar forms
6331 activeAction : null,
6334 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6335 * element by passing it or its id or mask the form itself by passing in true.
6338 waitMsgTarget : false,
6342 getAutoCreate : function(){
6346 method : this.method || 'POST',
6347 id : this.id || Roo.id(),
6350 if (this.parent().xtype.match(/^Nav/)) {
6351 cfg.cls = 'navbar-form navbar-' + this.align;
6355 if (this.labelAlign == 'left' ) {
6356 cfg.cls += ' form-horizontal';
6362 initEvents : function()
6364 this.el.on('submit', this.onSubmit, this);
6365 // this was added as random key presses on the form where triggering form submit.
6366 this.el.on('keypress', function(e) {
6367 if (e.getCharCode() != 13) {
6370 // we might need to allow it for textareas.. and some other items.
6371 // check e.getTarget().
6373 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6377 Roo.log("keypress blocked");
6385 onSubmit : function(e){
6390 * Returns true if client-side validation on the form is successful.
6393 isValid : function(){
6394 var items = this.getItems();
6396 items.each(function(f){
6405 * Returns true if any fields in this form have changed since their original load.
6408 isDirty : function(){
6410 var items = this.getItems();
6411 items.each(function(f){
6421 * Performs a predefined action (submit or load) or custom actions you define on this form.
6422 * @param {String} actionName The name of the action type
6423 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
6424 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6425 * accept other config options):
6427 Property Type Description
6428 ---------------- --------------- ----------------------------------------------------------------------------------
6429 url String The url for the action (defaults to the form's url)
6430 method String The form method to use (defaults to the form's method, or POST if not defined)
6431 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
6432 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
6433 validate the form on the client (defaults to false)
6435 * @return {BasicForm} this
6437 doAction : function(action, options){
6438 if(typeof action == 'string'){
6439 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6441 if(this.fireEvent('beforeaction', this, action) !== false){
6442 this.beforeAction(action);
6443 action.run.defer(100, action);
6449 beforeAction : function(action){
6450 var o = action.options;
6453 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6455 // not really supported yet.. ??
6457 //if(this.waitMsgTarget === true){
6458 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6459 //}else if(this.waitMsgTarget){
6460 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6461 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6463 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6469 afterAction : function(action, success){
6470 this.activeAction = null;
6471 var o = action.options;
6473 //if(this.waitMsgTarget === true){
6475 //}else if(this.waitMsgTarget){
6476 // this.waitMsgTarget.unmask();
6478 // Roo.MessageBox.updateProgress(1);
6479 // Roo.MessageBox.hide();
6486 Roo.callback(o.success, o.scope, [this, action]);
6487 this.fireEvent('actioncomplete', this, action);
6491 // failure condition..
6492 // we have a scenario where updates need confirming.
6493 // eg. if a locking scenario exists..
6494 // we look for { errors : { needs_confirm : true }} in the response.
6496 (typeof(action.result) != 'undefined') &&
6497 (typeof(action.result.errors) != 'undefined') &&
6498 (typeof(action.result.errors.needs_confirm) != 'undefined')
6501 Roo.log("not supported yet");
6504 Roo.MessageBox.confirm(
6505 "Change requires confirmation",
6506 action.result.errorMsg,
6511 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
6521 Roo.callback(o.failure, o.scope, [this, action]);
6522 // show an error message if no failed handler is set..
6523 if (!this.hasListener('actionfailed')) {
6524 Roo.log("need to add dialog support");
6526 Roo.MessageBox.alert("Error",
6527 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6528 action.result.errorMsg :
6529 "Saving Failed, please check your entries or try again"
6534 this.fireEvent('actionfailed', this, action);
6539 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6540 * @param {String} id The value to search for
6543 findField : function(id){
6544 var items = this.getItems();
6545 var field = items.get(id);
6547 items.each(function(f){
6548 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6555 return field || null;
6558 * Mark fields in this form invalid in bulk.
6559 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6560 * @return {BasicForm} this
6562 markInvalid : function(errors){
6563 if(errors instanceof Array){
6564 for(var i = 0, len = errors.length; i < len; i++){
6565 var fieldError = errors[i];
6566 var f = this.findField(fieldError.id);
6568 f.markInvalid(fieldError.msg);
6574 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6575 field.markInvalid(errors[id]);
6579 //Roo.each(this.childForms || [], function (f) {
6580 // f.markInvalid(errors);
6587 * Set values for fields in this form in bulk.
6588 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6589 * @return {BasicForm} this
6591 setValues : function(values){
6592 if(values instanceof Array){ // array of objects
6593 for(var i = 0, len = values.length; i < len; i++){
6595 var f = this.findField(v.id);
6597 f.setValue(v.value);
6598 if(this.trackResetOnLoad){
6599 f.originalValue = f.getValue();
6603 }else{ // object hash
6606 if(typeof values[id] != 'function' && (field = this.findField(id))){
6608 if (field.setFromData &&
6610 field.displayField &&
6611 // combos' with local stores can
6612 // be queried via setValue()
6613 // to set their value..
6614 (field.store && !field.store.isLocal)
6618 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6619 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6620 field.setFromData(sd);
6623 field.setValue(values[id]);
6627 if(this.trackResetOnLoad){
6628 field.originalValue = field.getValue();
6634 //Roo.each(this.childForms || [], function (f) {
6635 // f.setValues(values);
6642 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6643 * they are returned as an array.
6644 * @param {Boolean} asString
6647 getValues : function(asString){
6648 //if (this.childForms) {
6649 // copy values from the child forms
6650 // Roo.each(this.childForms, function (f) {
6651 // this.setValues(f.getValues());
6657 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6658 if(asString === true){
6661 return Roo.urlDecode(fs);
6665 * Returns the fields in this form as an object with key/value pairs.
6666 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6669 getFieldValues : function(with_hidden)
6671 var items = this.getItems();
6673 items.each(function(f){
6677 var v = f.getValue();
6678 if (f.inputType =='radio') {
6679 if (typeof(ret[f.getName()]) == 'undefined') {
6680 ret[f.getName()] = ''; // empty..
6683 if (!f.el.dom.checked) {
6691 // not sure if this supported any more..
6692 if ((typeof(v) == 'object') && f.getRawValue) {
6693 v = f.getRawValue() ; // dates..
6695 // combo boxes where name != hiddenName...
6696 if (f.name != f.getName()) {
6697 ret[f.name] = f.getRawValue();
6699 ret[f.getName()] = v;
6706 * Clears all invalid messages in this form.
6707 * @return {BasicForm} this
6709 clearInvalid : function(){
6710 var items = this.getItems();
6712 items.each(function(f){
6723 * @return {BasicForm} this
6726 var items = this.getItems();
6727 items.each(function(f){
6731 Roo.each(this.childForms || [], function (f) {
6738 getItems : function()
6740 var r=new Roo.util.MixedCollection(false, function(o){
6741 return o.id || (o.id = Roo.id());
6743 var iter = function(el) {
6750 Roo.each(el.items,function(e) {
6769 * Ext JS Library 1.1.1
6770 * Copyright(c) 2006-2007, Ext JS, LLC.
6772 * Originally Released Under LGPL - original licence link has changed is not relivant.
6775 * <script type="text/javascript">
6778 * @class Roo.form.VTypes
6779 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6782 Roo.form.VTypes = function(){
6783 // closure these in so they are only created once.
6784 var alpha = /^[a-zA-Z_]+$/;
6785 var alphanum = /^[a-zA-Z0-9_]+$/;
6786 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6787 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6789 // All these messages and functions are configurable
6792 * The function used to validate email addresses
6793 * @param {String} value The email address
6795 'email' : function(v){
6796 return email.test(v);
6799 * The error text to display when the email validation function returns false
6802 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6804 * The keystroke filter mask to be applied on email input
6807 'emailMask' : /[a-z0-9_\.\-@]/i,
6810 * The function used to validate URLs
6811 * @param {String} value The URL
6813 'url' : function(v){
6817 * The error text to display when the url validation function returns false
6820 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6823 * The function used to validate alpha values
6824 * @param {String} value The value
6826 'alpha' : function(v){
6827 return alpha.test(v);
6830 * The error text to display when the alpha validation function returns false
6833 'alphaText' : 'This field should only contain letters and _',
6835 * The keystroke filter mask to be applied on alpha input
6838 'alphaMask' : /[a-z_]/i,
6841 * The function used to validate alphanumeric values
6842 * @param {String} value The value
6844 'alphanum' : function(v){
6845 return alphanum.test(v);
6848 * The error text to display when the alphanumeric validation function returns false
6851 'alphanumText' : 'This field should only contain letters, numbers and _',
6853 * The keystroke filter mask to be applied on alphanumeric input
6856 'alphanumMask' : /[a-z0-9_]/i
6866 * @class Roo.bootstrap.Input
6867 * @extends Roo.bootstrap.Component
6868 * Bootstrap Input class
6869 * @cfg {Boolean} disabled is it disabled
6870 * @cfg {String} fieldLabel - the label associated
6871 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6872 * @cfg {String} name name of the input
6873 * @cfg {string} fieldLabel - the label associated
6874 * @cfg {string} inputType - input / file submit ...
6875 * @cfg {string} placeholder - placeholder to put in text.
6876 * @cfg {string} before - input group add on before
6877 * @cfg {string} after - input group add on after
6878 * @cfg {string} size - (lg|sm) or leave empty..
6879 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6880 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6881 * @cfg {Number} md colspan out of 12 for computer-sized screens
6882 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6883 * @cfg {string} value default value of the input
6884 * @cfg {Number} labelWidth set the width of label (0-12)
6885 * @cfg {String} labelAlign (top|left)
6886 * @cfg {Boolean} readOnly Specifies that the field should be read-only
6887 * @cfg {String} align (left|center|right) Default left
6891 * Create a new Input
6892 * @param {Object} config The config object
6895 Roo.bootstrap.Input = function(config){
6896 Roo.bootstrap.Input.superclass.constructor.call(this, config);
6901 * Fires when this field receives input focus.
6902 * @param {Roo.form.Field} this
6907 * Fires when this field loses input focus.
6908 * @param {Roo.form.Field} this
6913 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
6914 * {@link Roo.EventObject#getKey} to determine which key was pressed.
6915 * @param {Roo.form.Field} this
6916 * @param {Roo.EventObject} e The event object
6921 * Fires just before the field blurs if the field value has changed.
6922 * @param {Roo.form.Field} this
6923 * @param {Mixed} newValue The new value
6924 * @param {Mixed} oldValue The original value
6929 * Fires after the field has been marked as invalid.
6930 * @param {Roo.form.Field} this
6931 * @param {String} msg The validation message
6936 * Fires after the field has been validated with no errors.
6937 * @param {Roo.form.Field} this
6942 * Fires after the key up
6943 * @param {Roo.form.Field} this
6944 * @param {Roo.EventObject} e The event Object
6950 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
6952 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6953 automatic validation (defaults to "keyup").
6955 validationEvent : "keyup",
6957 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6959 validateOnBlur : true,
6961 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6963 validationDelay : 250,
6965 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6967 focusClass : "x-form-focus", // not needed???
6971 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6973 invalidClass : "has-error",
6976 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6978 selectOnFocus : false,
6981 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6985 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6990 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6992 disableKeyFilter : false,
6995 * @cfg {Boolean} disabled True to disable the field (defaults to false).
6999 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
7003 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7005 blankText : "This field is required",
7008 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7012 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7014 maxLength : Number.MAX_VALUE,
7016 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7018 minLengthText : "The minimum length for this field is {0}",
7020 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7022 maxLengthText : "The maximum length for this field is {0}",
7026 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7027 * If available, this function will be called only after the basic validators all return true, and will be passed the
7028 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7032 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7033 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7034 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7038 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7061 formatedValue : false,
7063 parentLabelAlign : function()
7066 while (parent.parent()) {
7067 parent = parent.parent();
7068 if (typeof(parent.labelAlign) !='undefined') {
7069 return parent.labelAlign;
7076 getAutoCreate : function(){
7078 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7084 if(this.inputType != 'hidden'){
7085 cfg.cls = 'form-group' //input-group
7091 type : this.inputType,
7093 cls : 'form-control',
7094 placeholder : this.placeholder || ''
7099 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7102 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7103 input.maxLength = this.maxLength;
7106 if (this.disabled) {
7107 input.disabled=true;
7110 if (this.readOnly) {
7111 input.readonly=true;
7115 input.name = this.name;
7118 input.cls += ' input-' + this.size;
7121 ['xs','sm','md','lg'].map(function(size){
7122 if (settings[size]) {
7123 cfg.cls += ' col-' + size + '-' + settings[size];
7127 var inputblock = input;
7129 if (this.before || this.after) {
7132 cls : 'input-group',
7135 if (this.before && typeof(this.before) == 'string') {
7137 inputblock.cn.push({
7139 cls : 'roo-input-before input-group-addon',
7143 if (this.before && typeof(this.before) == 'object') {
7144 this.before = Roo.factory(this.before);
7145 Roo.log(this.before);
7146 inputblock.cn.push({
7148 cls : 'roo-input-before input-group-' +
7149 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7153 inputblock.cn.push(input);
7155 if (this.after && typeof(this.after) == 'string') {
7156 inputblock.cn.push({
7158 cls : 'roo-input-after input-group-addon',
7162 if (this.after && typeof(this.after) == 'object') {
7163 this.after = Roo.factory(this.after);
7164 Roo.log(this.after);
7165 inputblock.cn.push({
7167 cls : 'roo-input-after input-group-' +
7168 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7173 if (align ==='left' && this.fieldLabel.length) {
7174 Roo.log("left and has label");
7180 cls : 'control-label col-sm-' + this.labelWidth,
7181 html : this.fieldLabel
7185 cls : "col-sm-" + (12 - this.labelWidth),
7192 } else if ( this.fieldLabel.length) {
7198 //cls : 'input-group-addon',
7199 html : this.fieldLabel
7209 Roo.log(" no label && no align");
7218 Roo.log('input-parentType: ' + this.parentType);
7220 if (this.parentType === 'Navbar' && this.parent().bar) {
7221 cfg.cls += ' navbar-form';
7229 * return the real input element.
7231 inputEl: function ()
7233 return this.el.select('input.form-control',true).first();
7236 tooltipEl : function()
7238 return this.inputEl();
7241 setDisabled : function(v)
7243 var i = this.inputEl().dom;
7245 i.removeAttribute('disabled');
7249 i.setAttribute('disabled','true');
7251 initEvents : function()
7254 this.inputEl().on("keydown" , this.fireKey, this);
7255 this.inputEl().on("focus", this.onFocus, this);
7256 this.inputEl().on("blur", this.onBlur, this);
7258 this.inputEl().relayEvent('keyup', this);
7260 // reference to original value for reset
7261 this.originalValue = this.getValue();
7262 //Roo.form.TextField.superclass.initEvents.call(this);
7263 if(this.validationEvent == 'keyup'){
7264 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7265 this.inputEl().on('keyup', this.filterValidation, this);
7267 else if(this.validationEvent !== false){
7268 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7271 if(this.selectOnFocus){
7272 this.on("focus", this.preFocus, this);
7275 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7276 this.inputEl().on("keypress", this.filterKeys, this);
7279 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7280 this.el.on("click", this.autoSize, this);
7283 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7284 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7287 if (typeof(this.before) == 'object') {
7288 this.before.render(this.el.select('.roo-input-before',true).first());
7290 if (typeof(this.after) == 'object') {
7291 this.after.render(this.el.select('.roo-input-after',true).first());
7296 filterValidation : function(e){
7297 if(!e.isNavKeyPress()){
7298 this.validationTask.delay(this.validationDelay);
7302 * Validates the field value
7303 * @return {Boolean} True if the value is valid, else false
7305 validate : function(){
7306 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7307 if(this.disabled || this.validateValue(this.getRawValue())){
7308 this.clearInvalid();
7316 * Validates a value according to the field's validation rules and marks the field as invalid
7317 * if the validation fails
7318 * @param {Mixed} value The value to validate
7319 * @return {Boolean} True if the value is valid, else false
7321 validateValue : function(value){
7322 if(value.length < 1) { // if it's blank
7323 if(this.allowBlank){
7324 this.clearInvalid();
7327 this.markInvalid(this.blankText);
7331 if(value.length < this.minLength){
7332 this.markInvalid(String.format(this.minLengthText, this.minLength));
7335 if(value.length > this.maxLength){
7336 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7340 var vt = Roo.form.VTypes;
7341 if(!vt[this.vtype](value, this)){
7342 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7346 if(typeof this.validator == "function"){
7347 var msg = this.validator(value);
7349 this.markInvalid(msg);
7353 if(this.regex && !this.regex.test(value)){
7354 this.markInvalid(this.regexText);
7363 fireKey : function(e){
7364 //Roo.log('field ' + e.getKey());
7365 if(e.isNavKeyPress()){
7366 this.fireEvent("specialkey", this, e);
7369 focus : function (selectText){
7371 this.inputEl().focus();
7372 if(selectText === true){
7373 this.inputEl().dom.select();
7379 onFocus : function(){
7380 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7381 // this.el.addClass(this.focusClass);
7384 this.hasFocus = true;
7385 this.startValue = this.getValue();
7386 this.fireEvent("focus", this);
7390 beforeBlur : Roo.emptyFn,
7394 onBlur : function(){
7396 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7397 //this.el.removeClass(this.focusClass);
7399 this.hasFocus = false;
7400 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7403 var v = this.getValue();
7404 if(String(v) !== String(this.startValue)){
7405 this.fireEvent('change', this, v, this.startValue);
7407 this.fireEvent("blur", this);
7411 * Resets the current field value to the originally loaded value and clears any validation messages
7414 this.setValue(this.originalValue);
7415 this.clearInvalid();
7418 * Returns the name of the field
7419 * @return {Mixed} name The name field
7421 getName: function(){
7425 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
7426 * @return {Mixed} value The field value
7428 getValue : function(){
7430 var v = this.inputEl().getValue();
7435 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
7436 * @return {Mixed} value The field value
7438 getRawValue : function(){
7439 var v = this.inputEl().getValue();
7445 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
7446 * @param {Mixed} value The value to set
7448 setRawValue : function(v){
7449 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7452 selectText : function(start, end){
7453 var v = this.getRawValue();
7455 start = start === undefined ? 0 : start;
7456 end = end === undefined ? v.length : end;
7457 var d = this.inputEl().dom;
7458 if(d.setSelectionRange){
7459 d.setSelectionRange(start, end);
7460 }else if(d.createTextRange){
7461 var range = d.createTextRange();
7462 range.moveStart("character", start);
7463 range.moveEnd("character", v.length-end);
7470 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
7471 * @param {Mixed} value The value to set
7473 setValue : function(v){
7476 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7482 processValue : function(value){
7483 if(this.stripCharsRe){
7484 var newValue = value.replace(this.stripCharsRe, '');
7485 if(newValue !== value){
7486 this.setRawValue(newValue);
7493 preFocus : function(){
7495 if(this.selectOnFocus){
7496 this.inputEl().dom.select();
7499 filterKeys : function(e){
7501 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7504 var c = e.getCharCode(), cc = String.fromCharCode(c);
7505 if(Roo.isIE && (e.isSpecialKey() || !cc)){
7508 if(!this.maskRe.test(cc)){
7513 * Clear any invalid styles/messages for this field
7515 clearInvalid : function(){
7517 if(!this.el || this.preventMark){ // not rendered
7520 this.el.removeClass(this.invalidClass);
7522 switch(this.msgTarget){
7524 this.el.dom.qtip = '';
7527 this.el.dom.title = '';
7531 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7536 this.errorIcon.dom.qtip = '';
7537 this.errorIcon.hide();
7538 this.un('resize', this.alignErrorIcon, this);
7542 var t = Roo.getDom(this.msgTarget);
7544 t.style.display = 'none';
7548 this.fireEvent('valid', this);
7551 * Mark this field as invalid
7552 * @param {String} msg The validation message
7554 markInvalid : function(msg){
7555 if(!this.el || this.preventMark){ // not rendered
7558 this.el.addClass(this.invalidClass);
7560 msg = msg || this.invalidText;
7561 switch(this.msgTarget){
7563 this.el.dom.qtip = msg;
7564 this.el.dom.qclass = 'x-form-invalid-tip';
7565 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7566 Roo.QuickTips.enable();
7570 this.el.dom.title = msg;
7574 var elp = this.el.findParent('.x-form-element', 5, true);
7575 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7576 this.errorEl.setWidth(elp.getWidth(true)-20);
7578 this.errorEl.update(msg);
7579 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7582 if(!this.errorIcon){
7583 var elp = this.el.findParent('.x-form-element', 5, true);
7584 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7586 this.alignErrorIcon();
7587 this.errorIcon.dom.qtip = msg;
7588 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7589 this.errorIcon.show();
7590 this.on('resize', this.alignErrorIcon, this);
7593 var t = Roo.getDom(this.msgTarget);
7595 t.style.display = this.msgDisplay;
7599 this.fireEvent('invalid', this, msg);
7602 SafariOnKeyDown : function(event)
7604 // this is a workaround for a password hang bug on chrome/ webkit.
7606 var isSelectAll = false;
7608 if(this.inputEl().dom.selectionEnd > 0){
7609 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7611 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7612 event.preventDefault();
7617 if(isSelectAll){ // backspace and delete key
7619 event.preventDefault();
7620 // this is very hacky as keydown always get's upper case.
7622 var cc = String.fromCharCode(event.getCharCode());
7623 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
7627 adjustWidth : function(tag, w){
7628 tag = tag.toLowerCase();
7629 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7630 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7634 if(tag == 'textarea'){
7637 }else if(Roo.isOpera){
7641 if(tag == 'textarea'){
7660 * @class Roo.bootstrap.TextArea
7661 * @extends Roo.bootstrap.Input
7662 * Bootstrap TextArea class
7663 * @cfg {Number} cols Specifies the visible width of a text area
7664 * @cfg {Number} rows Specifies the visible number of lines in a text area
7665 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7666 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7667 * @cfg {string} html text
7670 * Create a new TextArea
7671 * @param {Object} config The config object
7674 Roo.bootstrap.TextArea = function(config){
7675 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7679 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
7689 getAutoCreate : function(){
7691 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7702 value : this.value || '',
7703 html: this.html || '',
7704 cls : 'form-control',
7705 placeholder : this.placeholder || ''
7709 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7710 input.maxLength = this.maxLength;
7714 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7718 input.cols = this.cols;
7721 if (this.readOnly) {
7722 input.readonly = true;
7726 input.name = this.name;
7730 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7734 ['xs','sm','md','lg'].map(function(size){
7735 if (settings[size]) {
7736 cfg.cls += ' col-' + size + '-' + settings[size];
7740 var inputblock = input;
7742 if (this.before || this.after) {
7745 cls : 'input-group',
7749 inputblock.cn.push({
7751 cls : 'input-group-addon',
7755 inputblock.cn.push(input);
7757 inputblock.cn.push({
7759 cls : 'input-group-addon',
7766 if (align ==='left' && this.fieldLabel.length) {
7767 Roo.log("left and has label");
7773 cls : 'control-label col-sm-' + this.labelWidth,
7774 html : this.fieldLabel
7778 cls : "col-sm-" + (12 - this.labelWidth),
7785 } else if ( this.fieldLabel.length) {
7791 //cls : 'input-group-addon',
7792 html : this.fieldLabel
7802 Roo.log(" no label && no align");
7812 if (this.disabled) {
7813 input.disabled=true;
7820 * return the real textarea element.
7822 inputEl: function ()
7824 return this.el.select('textarea.form-control',true).first();
7832 * trigger field - base class for combo..
7837 * @class Roo.bootstrap.TriggerField
7838 * @extends Roo.bootstrap.Input
7839 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7840 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7841 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7842 * for which you can provide a custom implementation. For example:
7844 var trigger = new Roo.bootstrap.TriggerField();
7845 trigger.onTriggerClick = myTriggerFn;
7846 trigger.applyTo('my-field');
7849 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7850 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7851 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
7852 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7854 * Create a new TriggerField.
7855 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7856 * to the base TextField)
7858 Roo.bootstrap.TriggerField = function(config){
7859 this.mimicing = false;
7860 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7863 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
7865 * @cfg {String} triggerClass A CSS class to apply to the trigger
7868 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7872 /** @cfg {Boolean} grow @hide */
7873 /** @cfg {Number} growMin @hide */
7874 /** @cfg {Number} growMax @hide */
7880 autoSize: Roo.emptyFn,
7887 actionMode : 'wrap',
7891 getAutoCreate : function(){
7893 var align = this.labelAlign || this.parentLabelAlign();
7898 cls: 'form-group' //input-group
7905 type : this.inputType,
7906 cls : 'form-control',
7907 autocomplete: 'off',
7908 placeholder : this.placeholder || ''
7912 input.name = this.name;
7915 input.cls += ' input-' + this.size;
7918 if (this.disabled) {
7919 input.disabled=true;
7922 var inputblock = input;
7924 if (this.before || this.after) {
7927 cls : 'input-group',
7931 inputblock.cn.push({
7933 cls : 'input-group-addon',
7937 inputblock.cn.push(input);
7939 inputblock.cn.push({
7941 cls : 'input-group-addon',
7954 cls: 'form-hidden-field'
7962 Roo.log('multiple');
7970 cls: 'form-hidden-field'
7974 cls: 'select2-choices',
7978 cls: 'select2-search-field',
7991 cls: 'select2-container input-group',
7996 // cls: 'typeahead typeahead-long dropdown-menu',
7997 // style: 'display:none'
8002 if(!this.multiple && this.showToggleBtn){
8005 cls : 'input-group-addon btn dropdown-toggle',
8013 cls: 'combobox-clear',
8027 combobox.cls += ' select2-container-multi';
8030 if (align ==='left' && this.fieldLabel.length) {
8032 Roo.log("left and has label");
8038 cls : 'control-label col-sm-' + this.labelWidth,
8039 html : this.fieldLabel
8043 cls : "col-sm-" + (12 - this.labelWidth),
8050 } else if ( this.fieldLabel.length) {
8056 //cls : 'input-group-addon',
8057 html : this.fieldLabel
8067 Roo.log(" no label && no align");
8074 ['xs','sm','md','lg'].map(function(size){
8075 if (settings[size]) {
8076 cfg.cls += ' col-' + size + '-' + settings[size];
8087 onResize : function(w, h){
8088 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8089 // if(typeof w == 'number'){
8090 // var x = w - this.trigger.getWidth();
8091 // this.inputEl().setWidth(this.adjustWidth('input', x));
8092 // this.trigger.setStyle('left', x+'px');
8097 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8100 getResizeEl : function(){
8101 return this.inputEl();
8105 getPositionEl : function(){
8106 return this.inputEl();
8110 alignErrorIcon : function(){
8111 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8115 initEvents : function(){
8119 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8120 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8121 if(!this.multiple && this.showToggleBtn){
8122 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8123 if(this.hideTrigger){
8124 this.trigger.setDisplayed(false);
8126 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8130 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8133 //this.trigger.addClassOnOver('x-form-trigger-over');
8134 //this.trigger.addClassOnClick('x-form-trigger-click');
8137 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8141 createList : function()
8143 this.list = Roo.get(document.body).createChild({
8145 cls: 'typeahead typeahead-long dropdown-menu',
8146 style: 'display:none'
8149 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8154 initTrigger : function(){
8159 onDestroy : function(){
8161 this.trigger.removeAllListeners();
8162 // this.trigger.remove();
8165 // this.wrap.remove();
8167 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8171 onFocus : function(){
8172 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8175 this.wrap.addClass('x-trigger-wrap-focus');
8176 this.mimicing = true;
8177 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8178 if(this.monitorTab){
8179 this.el.on("keydown", this.checkTab, this);
8186 checkTab : function(e){
8187 if(e.getKey() == e.TAB){
8193 onBlur : function(){
8198 mimicBlur : function(e, t){
8200 if(!this.wrap.contains(t) && this.validateBlur()){
8207 triggerBlur : function(){
8208 this.mimicing = false;
8209 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8210 if(this.monitorTab){
8211 this.el.un("keydown", this.checkTab, this);
8213 //this.wrap.removeClass('x-trigger-wrap-focus');
8214 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8218 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8219 validateBlur : function(e, t){
8224 onDisable : function(){
8225 this.inputEl().dom.disabled = true;
8226 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8228 // this.wrap.addClass('x-item-disabled');
8233 onEnable : function(){
8234 this.inputEl().dom.disabled = false;
8235 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8237 // this.el.removeClass('x-item-disabled');
8242 onShow : function(){
8243 var ae = this.getActionEl();
8246 ae.dom.style.display = '';
8247 ae.dom.style.visibility = 'visible';
8253 onHide : function(){
8254 var ae = this.getActionEl();
8255 ae.dom.style.display = 'none';
8259 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8260 * by an implementing function.
8262 * @param {EventObject} e
8264 onTriggerClick : Roo.emptyFn
8268 * Ext JS Library 1.1.1
8269 * Copyright(c) 2006-2007, Ext JS, LLC.
8271 * Originally Released Under LGPL - original licence link has changed is not relivant.
8274 * <script type="text/javascript">
8279 * @class Roo.data.SortTypes
8281 * Defines the default sorting (casting?) comparison functions used when sorting data.
8283 Roo.data.SortTypes = {
8285 * Default sort that does nothing
8286 * @param {Mixed} s The value being converted
8287 * @return {Mixed} The comparison value
8294 * The regular expression used to strip tags
8298 stripTagsRE : /<\/?[^>]+>/gi,
8301 * Strips all HTML tags to sort on text only
8302 * @param {Mixed} s The value being converted
8303 * @return {String} The comparison value
8305 asText : function(s){
8306 return String(s).replace(this.stripTagsRE, "");
8310 * Strips all HTML tags to sort on text only - Case insensitive
8311 * @param {Mixed} s The value being converted
8312 * @return {String} The comparison value
8314 asUCText : function(s){
8315 return String(s).toUpperCase().replace(this.stripTagsRE, "");
8319 * Case insensitive string
8320 * @param {Mixed} s The value being converted
8321 * @return {String} The comparison value
8323 asUCString : function(s) {
8324 return String(s).toUpperCase();
8329 * @param {Mixed} s The value being converted
8330 * @return {Number} The comparison value
8332 asDate : function(s) {
8336 if(s instanceof Date){
8339 return Date.parse(String(s));
8344 * @param {Mixed} s The value being converted
8345 * @return {Float} The comparison value
8347 asFloat : function(s) {
8348 var val = parseFloat(String(s).replace(/,/g, ""));
8349 if(isNaN(val)) val = 0;
8355 * @param {Mixed} s The value being converted
8356 * @return {Number} The comparison value
8358 asInt : function(s) {
8359 var val = parseInt(String(s).replace(/,/g, ""));
8360 if(isNaN(val)) val = 0;
8365 * Ext JS Library 1.1.1
8366 * Copyright(c) 2006-2007, Ext JS, LLC.
8368 * Originally Released Under LGPL - original licence link has changed is not relivant.
8371 * <script type="text/javascript">
8375 * @class Roo.data.Record
8376 * Instances of this class encapsulate both record <em>definition</em> information, and record
8377 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8378 * to access Records cached in an {@link Roo.data.Store} object.<br>
8380 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8381 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8384 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8386 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8387 * {@link #create}. The parameters are the same.
8388 * @param {Array} data An associative Array of data values keyed by the field name.
8389 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8390 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8391 * not specified an integer id is generated.
8393 Roo.data.Record = function(data, id){
8394 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8399 * Generate a constructor for a specific record layout.
8400 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8401 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8402 * Each field definition object may contain the following properties: <ul>
8403 * <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,
8404 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8405 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8406 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8407 * is being used, then this is a string containing the javascript expression to reference the data relative to
8408 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8409 * to the data item relative to the record element. If the mapping expression is the same as the field name,
8410 * this may be omitted.</p></li>
8411 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8412 * <ul><li>auto (Default, implies no conversion)</li>
8417 * <li>date</li></ul></p></li>
8418 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8419 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8420 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8421 * by the Reader into an object that will be stored in the Record. It is passed the
8422 * following parameters:<ul>
8423 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8425 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8427 * <br>usage:<br><pre><code>
8428 var TopicRecord = Roo.data.Record.create(
8429 {name: 'title', mapping: 'topic_title'},
8430 {name: 'author', mapping: 'username'},
8431 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8432 {name: 'lastPost', mapping: 'post_time', type: 'date'},
8433 {name: 'lastPoster', mapping: 'user2'},
8434 {name: 'excerpt', mapping: 'post_text'}
8437 var myNewRecord = new TopicRecord({
8438 title: 'Do my job please',
8441 lastPost: new Date(),
8442 lastPoster: 'Animal',
8443 excerpt: 'No way dude!'
8445 myStore.add(myNewRecord);
8450 Roo.data.Record.create = function(o){
8452 f.superclass.constructor.apply(this, arguments);
8454 Roo.extend(f, Roo.data.Record);
8455 var p = f.prototype;
8456 p.fields = new Roo.util.MixedCollection(false, function(field){
8459 for(var i = 0, len = o.length; i < len; i++){
8460 p.fields.add(new Roo.data.Field(o[i]));
8462 f.getField = function(name){
8463 return p.fields.get(name);
8468 Roo.data.Record.AUTO_ID = 1000;
8469 Roo.data.Record.EDIT = 'edit';
8470 Roo.data.Record.REJECT = 'reject';
8471 Roo.data.Record.COMMIT = 'commit';
8473 Roo.data.Record.prototype = {
8475 * Readonly flag - true if this record has been modified.
8484 join : function(store){
8489 * Set the named field to the specified value.
8490 * @param {String} name The name of the field to set.
8491 * @param {Object} value The value to set the field to.
8493 set : function(name, value){
8494 if(this.data[name] == value){
8501 if(typeof this.modified[name] == 'undefined'){
8502 this.modified[name] = this.data[name];
8504 this.data[name] = value;
8505 if(!this.editing && this.store){
8506 this.store.afterEdit(this);
8511 * Get the value of the named field.
8512 * @param {String} name The name of the field to get the value of.
8513 * @return {Object} The value of the field.
8515 get : function(name){
8516 return this.data[name];
8520 beginEdit : function(){
8521 this.editing = true;
8526 cancelEdit : function(){
8527 this.editing = false;
8528 delete this.modified;
8532 endEdit : function(){
8533 this.editing = false;
8534 if(this.dirty && this.store){
8535 this.store.afterEdit(this);
8540 * Usually called by the {@link Roo.data.Store} which owns the Record.
8541 * Rejects all changes made to the Record since either creation, or the last commit operation.
8542 * Modified fields are reverted to their original values.
8544 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8545 * of reject operations.
8547 reject : function(){
8548 var m = this.modified;
8550 if(typeof m[n] != "function"){
8551 this.data[n] = m[n];
8555 delete this.modified;
8556 this.editing = false;
8558 this.store.afterReject(this);
8563 * Usually called by the {@link Roo.data.Store} which owns the Record.
8564 * Commits all changes made to the Record since either creation, or the last commit operation.
8566 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8567 * of commit operations.
8569 commit : function(){
8571 delete this.modified;
8572 this.editing = false;
8574 this.store.afterCommit(this);
8579 hasError : function(){
8580 return this.error != null;
8584 clearError : function(){
8589 * Creates a copy of this record.
8590 * @param {String} id (optional) A new record id if you don't want to use this record's id
8593 copy : function(newId) {
8594 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8598 * Ext JS Library 1.1.1
8599 * Copyright(c) 2006-2007, Ext JS, LLC.
8601 * Originally Released Under LGPL - original licence link has changed is not relivant.
8604 * <script type="text/javascript">
8610 * @class Roo.data.Store
8611 * @extends Roo.util.Observable
8612 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8613 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8615 * 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
8616 * has no knowledge of the format of the data returned by the Proxy.<br>
8618 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8619 * instances from the data object. These records are cached and made available through accessor functions.
8621 * Creates a new Store.
8622 * @param {Object} config A config object containing the objects needed for the Store to access data,
8623 * and read the data into Records.
8625 Roo.data.Store = function(config){
8626 this.data = new Roo.util.MixedCollection(false);
8627 this.data.getKey = function(o){
8630 this.baseParams = {};
8637 "multisort" : "_multisort"
8640 if(config && config.data){
8641 this.inlineData = config.data;
8645 Roo.apply(this, config);
8647 if(this.reader){ // reader passed
8648 this.reader = Roo.factory(this.reader, Roo.data);
8649 this.reader.xmodule = this.xmodule || false;
8650 if(!this.recordType){
8651 this.recordType = this.reader.recordType;
8653 if(this.reader.onMetaChange){
8654 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8658 if(this.recordType){
8659 this.fields = this.recordType.prototype.fields;
8665 * @event datachanged
8666 * Fires when the data cache has changed, and a widget which is using this Store
8667 * as a Record cache should refresh its view.
8668 * @param {Store} this
8673 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8674 * @param {Store} this
8675 * @param {Object} meta The JSON metadata
8680 * Fires when Records have been added to the Store
8681 * @param {Store} this
8682 * @param {Roo.data.Record[]} records The array of Records added
8683 * @param {Number} index The index at which the record(s) were added
8688 * Fires when a Record has been removed from the Store
8689 * @param {Store} this
8690 * @param {Roo.data.Record} record The Record that was removed
8691 * @param {Number} index The index at which the record was removed
8696 * Fires when a Record has been updated
8697 * @param {Store} this
8698 * @param {Roo.data.Record} record The Record that was updated
8699 * @param {String} operation The update operation being performed. Value may be one of:
8701 Roo.data.Record.EDIT
8702 Roo.data.Record.REJECT
8703 Roo.data.Record.COMMIT
8709 * Fires when the data cache has been cleared.
8710 * @param {Store} this
8715 * Fires before a request is made for a new data object. If the beforeload handler returns false
8716 * the load action will be canceled.
8717 * @param {Store} this
8718 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8722 * @event beforeloadadd
8723 * Fires after a new set of Records has been loaded.
8724 * @param {Store} this
8725 * @param {Roo.data.Record[]} records The Records that were loaded
8726 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8728 beforeloadadd : true,
8731 * Fires after a new set of Records has been loaded, before they are added to the store.
8732 * @param {Store} this
8733 * @param {Roo.data.Record[]} records The Records that were loaded
8734 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8735 * @params {Object} return from reader
8739 * @event loadexception
8740 * Fires if an exception occurs in the Proxy during loading.
8741 * Called with the signature of the Proxy's "loadexception" event.
8742 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8745 * @param {Object} return from JsonData.reader() - success, totalRecords, records
8746 * @param {Object} load options
8747 * @param {Object} jsonData from your request (normally this contains the Exception)
8749 loadexception : true
8753 this.proxy = Roo.factory(this.proxy, Roo.data);
8754 this.proxy.xmodule = this.xmodule || false;
8755 this.relayEvents(this.proxy, ["loadexception"]);
8757 this.sortToggle = {};
8758 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8760 Roo.data.Store.superclass.constructor.call(this);
8762 if(this.inlineData){
8763 this.loadData(this.inlineData);
8764 delete this.inlineData;
8768 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8770 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
8771 * without a remote query - used by combo/forms at present.
8775 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8778 * @cfg {Array} data Inline data to be loaded when the store is initialized.
8781 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8782 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8785 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8786 * on any HTTP request
8789 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8792 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8796 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8797 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8802 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8803 * loaded or when a record is removed. (defaults to false).
8805 pruneModifiedRecords : false,
8811 * Add Records to the Store and fires the add event.
8812 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8814 add : function(records){
8815 records = [].concat(records);
8816 for(var i = 0, len = records.length; i < len; i++){
8817 records[i].join(this);
8819 var index = this.data.length;
8820 this.data.addAll(records);
8821 this.fireEvent("add", this, records, index);
8825 * Remove a Record from the Store and fires the remove event.
8826 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8828 remove : function(record){
8829 var index = this.data.indexOf(record);
8830 this.data.removeAt(index);
8831 if(this.pruneModifiedRecords){
8832 this.modified.remove(record);
8834 this.fireEvent("remove", this, record, index);
8838 * Remove all Records from the Store and fires the clear event.
8840 removeAll : function(){
8842 if(this.pruneModifiedRecords){
8845 this.fireEvent("clear", this);
8849 * Inserts Records to the Store at the given index and fires the add event.
8850 * @param {Number} index The start index at which to insert the passed Records.
8851 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8853 insert : function(index, records){
8854 records = [].concat(records);
8855 for(var i = 0, len = records.length; i < len; i++){
8856 this.data.insert(index, records[i]);
8857 records[i].join(this);
8859 this.fireEvent("add", this, records, index);
8863 * Get the index within the cache of the passed Record.
8864 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8865 * @return {Number} The index of the passed Record. Returns -1 if not found.
8867 indexOf : function(record){
8868 return this.data.indexOf(record);
8872 * Get the index within the cache of the Record with the passed id.
8873 * @param {String} id The id of the Record to find.
8874 * @return {Number} The index of the Record. Returns -1 if not found.
8876 indexOfId : function(id){
8877 return this.data.indexOfKey(id);
8881 * Get the Record with the specified id.
8882 * @param {String} id The id of the Record to find.
8883 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8885 getById : function(id){
8886 return this.data.key(id);
8890 * Get the Record at the specified index.
8891 * @param {Number} index The index of the Record to find.
8892 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8894 getAt : function(index){
8895 return this.data.itemAt(index);
8899 * Returns a range of Records between specified indices.
8900 * @param {Number} startIndex (optional) The starting index (defaults to 0)
8901 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8902 * @return {Roo.data.Record[]} An array of Records
8904 getRange : function(start, end){
8905 return this.data.getRange(start, end);
8909 storeOptions : function(o){
8910 o = Roo.apply({}, o);
8913 this.lastOptions = o;
8917 * Loads the Record cache from the configured Proxy using the configured Reader.
8919 * If using remote paging, then the first load call must specify the <em>start</em>
8920 * and <em>limit</em> properties in the options.params property to establish the initial
8921 * position within the dataset, and the number of Records to cache on each read from the Proxy.
8923 * <strong>It is important to note that for remote data sources, loading is asynchronous,
8924 * and this call will return before the new data has been loaded. Perform any post-processing
8925 * in a callback function, or in a "load" event handler.</strong>
8927 * @param {Object} options An object containing properties which control loading options:<ul>
8928 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8929 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8930 * passed the following arguments:<ul>
8931 * <li>r : Roo.data.Record[]</li>
8932 * <li>options: Options object from the load call</li>
8933 * <li>success: Boolean success indicator</li></ul></li>
8934 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8935 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8938 load : function(options){
8939 options = options || {};
8940 if(this.fireEvent("beforeload", this, options) !== false){
8941 this.storeOptions(options);
8942 var p = Roo.apply(options.params || {}, this.baseParams);
8943 // if meta was not loaded from remote source.. try requesting it.
8944 if (!this.reader.metaFromRemote) {
8947 if(this.sortInfo && this.remoteSort){
8948 var pn = this.paramNames;
8949 p[pn["sort"]] = this.sortInfo.field;
8950 p[pn["dir"]] = this.sortInfo.direction;
8952 if (this.multiSort) {
8953 var pn = this.paramNames;
8954 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8957 this.proxy.load(p, this.reader, this.loadRecords, this, options);
8962 * Reloads the Record cache from the configured Proxy using the configured Reader and
8963 * the options from the last load operation performed.
8964 * @param {Object} options (optional) An object containing properties which may override the options
8965 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8966 * the most recently used options are reused).
8968 reload : function(options){
8969 this.load(Roo.applyIf(options||{}, this.lastOptions));
8973 // Called as a callback by the Reader during a load operation.
8974 loadRecords : function(o, options, success){
8975 if(!o || success === false){
8976 if(success !== false){
8977 this.fireEvent("load", this, [], options, o);
8979 if(options.callback){
8980 options.callback.call(options.scope || this, [], options, false);
8984 // if data returned failure - throw an exception.
8985 if (o.success === false) {
8986 // show a message if no listener is registered.
8987 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8988 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8990 // loadmask wil be hooked into this..
8991 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8994 var r = o.records, t = o.totalRecords || r.length;
8996 this.fireEvent("beforeloadadd", this, r, options, o);
8998 if(!options || options.add !== true){
8999 if(this.pruneModifiedRecords){
9002 for(var i = 0, len = r.length; i < len; i++){
9006 this.data = this.snapshot;
9007 delete this.snapshot;
9010 this.data.addAll(r);
9011 this.totalLength = t;
9013 this.fireEvent("datachanged", this);
9015 this.totalLength = Math.max(t, this.data.length+r.length);
9018 this.fireEvent("load", this, r, options, o);
9019 if(options.callback){
9020 options.callback.call(options.scope || this, r, options, true);
9026 * Loads data from a passed data block. A Reader which understands the format of the data
9027 * must have been configured in the constructor.
9028 * @param {Object} data The data block from which to read the Records. The format of the data expected
9029 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9030 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9032 loadData : function(o, append){
9033 var r = this.reader.readRecords(o);
9034 this.loadRecords(r, {add: append}, true);
9038 * Gets the number of cached records.
9040 * <em>If using paging, this may not be the total size of the dataset. If the data object
9041 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9042 * the data set size</em>
9044 getCount : function(){
9045 return this.data.length || 0;
9049 * Gets the total number of records in the dataset as returned by the server.
9051 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9052 * the dataset size</em>
9054 getTotalCount : function(){
9055 return this.totalLength || 0;
9059 * Returns the sort state of the Store as an object with two properties:
9061 field {String} The name of the field by which the Records are sorted
9062 direction {String} The sort order, "ASC" or "DESC"
9065 getSortState : function(){
9066 return this.sortInfo;
9070 applySort : function(){
9071 if(this.sortInfo && !this.remoteSort){
9072 var s = this.sortInfo, f = s.field;
9073 var st = this.fields.get(f).sortType;
9074 var fn = function(r1, r2){
9075 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9076 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9078 this.data.sort(s.direction, fn);
9079 if(this.snapshot && this.snapshot != this.data){
9080 this.snapshot.sort(s.direction, fn);
9086 * Sets the default sort column and order to be used by the next load operation.
9087 * @param {String} fieldName The name of the field to sort by.
9088 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9090 setDefaultSort : function(field, dir){
9091 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9096 * If remote sorting is used, the sort is performed on the server, and the cache is
9097 * reloaded. If local sorting is used, the cache is sorted internally.
9098 * @param {String} fieldName The name of the field to sort by.
9099 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9101 sort : function(fieldName, dir){
9102 var f = this.fields.get(fieldName);
9104 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9106 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9107 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9112 this.sortToggle[f.name] = dir;
9113 this.sortInfo = {field: f.name, direction: dir};
9114 if(!this.remoteSort){
9116 this.fireEvent("datachanged", this);
9118 this.load(this.lastOptions);
9123 * Calls the specified function for each of the Records in the cache.
9124 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9125 * Returning <em>false</em> aborts and exits the iteration.
9126 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9128 each : function(fn, scope){
9129 this.data.each(fn, scope);
9133 * Gets all records modified since the last commit. Modified records are persisted across load operations
9134 * (e.g., during paging).
9135 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9137 getModifiedRecords : function(){
9138 return this.modified;
9142 createFilterFn : function(property, value, anyMatch){
9143 if(!value.exec){ // not a regex
9144 value = String(value);
9145 if(value.length == 0){
9148 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9151 return value.test(r.data[property]);
9156 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9157 * @param {String} property A field on your records
9158 * @param {Number} start The record index to start at (defaults to 0)
9159 * @param {Number} end The last record index to include (defaults to length - 1)
9160 * @return {Number} The sum
9162 sum : function(property, start, end){
9163 var rs = this.data.items, v = 0;
9165 end = (end || end === 0) ? end : rs.length-1;
9167 for(var i = start; i <= end; i++){
9168 v += (rs[i].data[property] || 0);
9174 * Filter the records by a specified property.
9175 * @param {String} field A field on your records
9176 * @param {String/RegExp} value Either a string that the field
9177 * should start with or a RegExp to test against the field
9178 * @param {Boolean} anyMatch True to match any part not just the beginning
9180 filter : function(property, value, anyMatch){
9181 var fn = this.createFilterFn(property, value, anyMatch);
9182 return fn ? this.filterBy(fn) : this.clearFilter();
9186 * Filter by a function. The specified function will be called with each
9187 * record in this data source. If the function returns true the record is included,
9188 * otherwise it is filtered.
9189 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9190 * @param {Object} scope (optional) The scope of the function (defaults to this)
9192 filterBy : function(fn, scope){
9193 this.snapshot = this.snapshot || this.data;
9194 this.data = this.queryBy(fn, scope||this);
9195 this.fireEvent("datachanged", this);
9199 * Query the records by a specified property.
9200 * @param {String} field A field on your records
9201 * @param {String/RegExp} value Either a string that the field
9202 * should start with or a RegExp to test against the field
9203 * @param {Boolean} anyMatch True to match any part not just the beginning
9204 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9206 query : function(property, value, anyMatch){
9207 var fn = this.createFilterFn(property, value, anyMatch);
9208 return fn ? this.queryBy(fn) : this.data.clone();
9212 * Query by a function. The specified function will be called with each
9213 * record in this data source. If the function returns true the record is included
9215 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9216 * @param {Object} scope (optional) The scope of the function (defaults to this)
9217 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9219 queryBy : function(fn, scope){
9220 var data = this.snapshot || this.data;
9221 return data.filterBy(fn, scope||this);
9225 * Collects unique values for a particular dataIndex from this store.
9226 * @param {String} dataIndex The property to collect
9227 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9228 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9229 * @return {Array} An array of the unique values
9231 collect : function(dataIndex, allowNull, bypassFilter){
9232 var d = (bypassFilter === true && this.snapshot) ?
9233 this.snapshot.items : this.data.items;
9234 var v, sv, r = [], l = {};
9235 for(var i = 0, len = d.length; i < len; i++){
9236 v = d[i].data[dataIndex];
9238 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9247 * Revert to a view of the Record cache with no filtering applied.
9248 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9250 clearFilter : function(suppressEvent){
9251 if(this.snapshot && this.snapshot != this.data){
9252 this.data = this.snapshot;
9253 delete this.snapshot;
9254 if(suppressEvent !== true){
9255 this.fireEvent("datachanged", this);
9261 afterEdit : function(record){
9262 if(this.modified.indexOf(record) == -1){
9263 this.modified.push(record);
9265 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9269 afterReject : function(record){
9270 this.modified.remove(record);
9271 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9275 afterCommit : function(record){
9276 this.modified.remove(record);
9277 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9281 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9282 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9284 commitChanges : function(){
9285 var m = this.modified.slice(0);
9287 for(var i = 0, len = m.length; i < len; i++){
9293 * Cancel outstanding changes on all changed records.
9295 rejectChanges : function(){
9296 var m = this.modified.slice(0);
9298 for(var i = 0, len = m.length; i < len; i++){
9303 onMetaChange : function(meta, rtype, o){
9304 this.recordType = rtype;
9305 this.fields = rtype.prototype.fields;
9306 delete this.snapshot;
9307 this.sortInfo = meta.sortInfo || this.sortInfo;
9309 this.fireEvent('metachange', this, this.reader.meta);
9312 moveIndex : function(data, type)
9314 var index = this.indexOf(data);
9316 var newIndex = index + type;
9320 this.insert(newIndex, data);
9325 * Ext JS Library 1.1.1
9326 * Copyright(c) 2006-2007, Ext JS, LLC.
9328 * Originally Released Under LGPL - original licence link has changed is not relivant.
9331 * <script type="text/javascript">
9335 * @class Roo.data.SimpleStore
9336 * @extends Roo.data.Store
9337 * Small helper class to make creating Stores from Array data easier.
9338 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9339 * @cfg {Array} fields An array of field definition objects, or field name strings.
9340 * @cfg {Array} data The multi-dimensional array of data
9342 * @param {Object} config
9344 Roo.data.SimpleStore = function(config){
9345 Roo.data.SimpleStore.superclass.constructor.call(this, {
9347 reader: new Roo.data.ArrayReader({
9350 Roo.data.Record.create(config.fields)
9352 proxy : new Roo.data.MemoryProxy(config.data)
9356 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9358 * Ext JS Library 1.1.1
9359 * Copyright(c) 2006-2007, Ext JS, LLC.
9361 * Originally Released Under LGPL - original licence link has changed is not relivant.
9364 * <script type="text/javascript">
9369 * @extends Roo.data.Store
9370 * @class Roo.data.JsonStore
9371 * Small helper class to make creating Stores for JSON data easier. <br/>
9373 var store = new Roo.data.JsonStore({
9374 url: 'get-images.php',
9376 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9379 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9380 * JsonReader and HttpProxy (unless inline data is provided).</b>
9381 * @cfg {Array} fields An array of field definition objects, or field name strings.
9383 * @param {Object} config
9385 Roo.data.JsonStore = function(c){
9386 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9387 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9388 reader: new Roo.data.JsonReader(c, c.fields)
9391 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9393 * Ext JS Library 1.1.1
9394 * Copyright(c) 2006-2007, Ext JS, LLC.
9396 * Originally Released Under LGPL - original licence link has changed is not relivant.
9399 * <script type="text/javascript">
9403 Roo.data.Field = function(config){
9404 if(typeof config == "string"){
9405 config = {name: config};
9407 Roo.apply(this, config);
9413 var st = Roo.data.SortTypes;
9414 // named sortTypes are supported, here we look them up
9415 if(typeof this.sortType == "string"){
9416 this.sortType = st[this.sortType];
9419 // set default sortType for strings and dates
9423 this.sortType = st.asUCString;
9426 this.sortType = st.asDate;
9429 this.sortType = st.none;
9434 var stripRe = /[\$,%]/g;
9436 // prebuilt conversion function for this field, instead of
9437 // switching every time we're reading a value
9439 var cv, dateFormat = this.dateFormat;
9444 cv = function(v){ return v; };
9447 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9451 return v !== undefined && v !== null && v !== '' ?
9452 parseInt(String(v).replace(stripRe, ""), 10) : '';
9457 return v !== undefined && v !== null && v !== '' ?
9458 parseFloat(String(v).replace(stripRe, ""), 10) : '';
9463 cv = function(v){ return v === true || v === "true" || v == 1; };
9470 if(v instanceof Date){
9474 if(dateFormat == "timestamp"){
9475 return new Date(v*1000);
9477 return Date.parseDate(v, dateFormat);
9479 var parsed = Date.parse(v);
9480 return parsed ? new Date(parsed) : null;
9489 Roo.data.Field.prototype = {
9497 * Ext JS Library 1.1.1
9498 * Copyright(c) 2006-2007, Ext JS, LLC.
9500 * Originally Released Under LGPL - original licence link has changed is not relivant.
9503 * <script type="text/javascript">
9506 // Base class for reading structured data from a data source. This class is intended to be
9507 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9510 * @class Roo.data.DataReader
9511 * Base class for reading structured data from a data source. This class is intended to be
9512 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9515 Roo.data.DataReader = function(meta, recordType){
9519 this.recordType = recordType instanceof Array ?
9520 Roo.data.Record.create(recordType) : recordType;
9523 Roo.data.DataReader.prototype = {
9525 * Create an empty record
9526 * @param {Object} data (optional) - overlay some values
9527 * @return {Roo.data.Record} record created.
9529 newRow : function(d) {
9531 this.recordType.prototype.fields.each(function(c) {
9533 case 'int' : da[c.name] = 0; break;
9534 case 'date' : da[c.name] = new Date(); break;
9535 case 'float' : da[c.name] = 0.0; break;
9536 case 'boolean' : da[c.name] = false; break;
9537 default : da[c.name] = ""; break;
9541 return new this.recordType(Roo.apply(da, d));
9546 * Ext JS Library 1.1.1
9547 * Copyright(c) 2006-2007, Ext JS, LLC.
9549 * Originally Released Under LGPL - original licence link has changed is not relivant.
9552 * <script type="text/javascript">
9556 * @class Roo.data.DataProxy
9557 * @extends Roo.data.Observable
9558 * This class is an abstract base class for implementations which provide retrieval of
9559 * unformatted data objects.<br>
9561 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9562 * (of the appropriate type which knows how to parse the data object) to provide a block of
9563 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9565 * Custom implementations must implement the load method as described in
9566 * {@link Roo.data.HttpProxy#load}.
9568 Roo.data.DataProxy = function(){
9572 * Fires before a network request is made to retrieve a data object.
9573 * @param {Object} This DataProxy object.
9574 * @param {Object} params The params parameter to the load function.
9579 * Fires before the load method's callback is called.
9580 * @param {Object} This DataProxy object.
9581 * @param {Object} o The data object.
9582 * @param {Object} arg The callback argument object passed to the load function.
9586 * @event loadexception
9587 * Fires if an Exception occurs during data retrieval.
9588 * @param {Object} This DataProxy object.
9589 * @param {Object} o The data object.
9590 * @param {Object} arg The callback argument object passed to the load function.
9591 * @param {Object} e The Exception.
9593 loadexception : true
9595 Roo.data.DataProxy.superclass.constructor.call(this);
9598 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9601 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9605 * Ext JS Library 1.1.1
9606 * Copyright(c) 2006-2007, Ext JS, LLC.
9608 * Originally Released Under LGPL - original licence link has changed is not relivant.
9611 * <script type="text/javascript">
9614 * @class Roo.data.MemoryProxy
9615 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9616 * to the Reader when its load method is called.
9618 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9620 Roo.data.MemoryProxy = function(data){
9624 Roo.data.MemoryProxy.superclass.constructor.call(this);
9628 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9630 * Load data from the requested source (in this case an in-memory
9631 * data object passed to the constructor), read the data object into
9632 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9633 * process that block using the passed callback.
9634 * @param {Object} params This parameter is not used by the MemoryProxy class.
9635 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9636 * object into a block of Roo.data.Records.
9637 * @param {Function} callback The function into which to pass the block of Roo.data.records.
9638 * The function must be passed <ul>
9639 * <li>The Record block object</li>
9640 * <li>The "arg" argument from the load function</li>
9641 * <li>A boolean success indicator</li>
9643 * @param {Object} scope The scope in which to call the callback
9644 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9646 load : function(params, reader, callback, scope, arg){
9647 params = params || {};
9650 result = reader.readRecords(this.data);
9652 this.fireEvent("loadexception", this, arg, null, e);
9653 callback.call(scope, null, arg, false);
9656 callback.call(scope, result, arg, true);
9660 update : function(params, records){
9665 * Ext JS Library 1.1.1
9666 * Copyright(c) 2006-2007, Ext JS, LLC.
9668 * Originally Released Under LGPL - original licence link has changed is not relivant.
9671 * <script type="text/javascript">
9674 * @class Roo.data.HttpProxy
9675 * @extends Roo.data.DataProxy
9676 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9677 * configured to reference a certain URL.<br><br>
9679 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9680 * from which the running page was served.<br><br>
9682 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9684 * Be aware that to enable the browser to parse an XML document, the server must set
9685 * the Content-Type header in the HTTP response to "text/xml".
9687 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9688 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
9689 * will be used to make the request.
9691 Roo.data.HttpProxy = function(conn){
9692 Roo.data.HttpProxy.superclass.constructor.call(this);
9693 // is conn a conn config or a real conn?
9695 this.useAjax = !conn || !conn.events;
9699 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9700 // thse are take from connection...
9703 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9706 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9707 * extra parameters to each request made by this object. (defaults to undefined)
9710 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9711 * to each request made by this object. (defaults to undefined)
9714 * @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)
9717 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9720 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9726 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9730 * Return the {@link Roo.data.Connection} object being used by this Proxy.
9731 * @return {Connection} The Connection object. This object may be used to subscribe to events on
9732 * a finer-grained basis than the DataProxy events.
9734 getConnection : function(){
9735 return this.useAjax ? Roo.Ajax : this.conn;
9739 * Load data from the configured {@link Roo.data.Connection}, read the data object into
9740 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9741 * process that block using the passed callback.
9742 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9743 * for the request to the remote server.
9744 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9745 * object into a block of Roo.data.Records.
9746 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9747 * The function must be passed <ul>
9748 * <li>The Record block object</li>
9749 * <li>The "arg" argument from the load function</li>
9750 * <li>A boolean success indicator</li>
9752 * @param {Object} scope The scope in which to call the callback
9753 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9755 load : function(params, reader, callback, scope, arg){
9756 if(this.fireEvent("beforeload", this, params) !== false){
9758 params : params || {},
9760 callback : callback,
9765 callback : this.loadResponse,
9769 Roo.applyIf(o, this.conn);
9770 if(this.activeRequest){
9771 Roo.Ajax.abort(this.activeRequest);
9773 this.activeRequest = Roo.Ajax.request(o);
9775 this.conn.request(o);
9778 callback.call(scope||this, null, arg, false);
9783 loadResponse : function(o, success, response){
9784 delete this.activeRequest;
9786 this.fireEvent("loadexception", this, o, response);
9787 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9792 result = o.reader.read(response);
9794 this.fireEvent("loadexception", this, o, response, e);
9795 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9799 this.fireEvent("load", this, o, o.request.arg);
9800 o.request.callback.call(o.request.scope, result, o.request.arg, true);
9804 update : function(dataSet){
9809 updateResponse : function(dataSet){
9814 * Ext JS Library 1.1.1
9815 * Copyright(c) 2006-2007, Ext JS, LLC.
9817 * Originally Released Under LGPL - original licence link has changed is not relivant.
9820 * <script type="text/javascript">
9824 * @class Roo.data.ScriptTagProxy
9825 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9826 * other than the originating domain of the running page.<br><br>
9828 * <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
9829 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9831 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9832 * source code that is used as the source inside a <script> tag.<br><br>
9834 * In order for the browser to process the returned data, the server must wrap the data object
9835 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9836 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9837 * depending on whether the callback name was passed:
9840 boolean scriptTag = false;
9841 String cb = request.getParameter("callback");
9844 response.setContentType("text/javascript");
9846 response.setContentType("application/x-json");
9848 Writer out = response.getWriter();
9850 out.write(cb + "(");
9852 out.print(dataBlock.toJsonString());
9859 * @param {Object} config A configuration object.
9861 Roo.data.ScriptTagProxy = function(config){
9862 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9863 Roo.apply(this, config);
9864 this.head = document.getElementsByTagName("head")[0];
9867 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9869 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9871 * @cfg {String} url The URL from which to request the data object.
9874 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9878 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9879 * the server the name of the callback function set up by the load call to process the returned data object.
9880 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9881 * javascript output which calls this named function passing the data object as its only parameter.
9883 callbackParam : "callback",
9885 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9886 * name to the request.
9891 * Load data from the configured URL, read the data object into
9892 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9893 * process that block using the passed callback.
9894 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9895 * for the request to the remote server.
9896 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9897 * object into a block of Roo.data.Records.
9898 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9899 * The function must be passed <ul>
9900 * <li>The Record block object</li>
9901 * <li>The "arg" argument from the load function</li>
9902 * <li>A boolean success indicator</li>
9904 * @param {Object} scope The scope in which to call the callback
9905 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9907 load : function(params, reader, callback, scope, arg){
9908 if(this.fireEvent("beforeload", this, params) !== false){
9910 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9913 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9915 url += "&_dc=" + (new Date().getTime());
9917 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9920 cb : "stcCallback"+transId,
9921 scriptId : "stcScript"+transId,
9925 callback : callback,
9931 window[trans.cb] = function(o){
9932 conn.handleResponse(o, trans);
9935 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9937 if(this.autoAbort !== false){
9941 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9943 var script = document.createElement("script");
9944 script.setAttribute("src", url);
9945 script.setAttribute("type", "text/javascript");
9946 script.setAttribute("id", trans.scriptId);
9947 this.head.appendChild(script);
9951 callback.call(scope||this, null, arg, false);
9956 isLoading : function(){
9957 return this.trans ? true : false;
9961 * Abort the current server request.
9964 if(this.isLoading()){
9965 this.destroyTrans(this.trans);
9970 destroyTrans : function(trans, isLoaded){
9971 this.head.removeChild(document.getElementById(trans.scriptId));
9972 clearTimeout(trans.timeoutId);
9974 window[trans.cb] = undefined;
9976 delete window[trans.cb];
9979 // if hasn't been loaded, wait for load to remove it to prevent script error
9980 window[trans.cb] = function(){
9981 window[trans.cb] = undefined;
9983 delete window[trans.cb];
9990 handleResponse : function(o, trans){
9992 this.destroyTrans(trans, true);
9995 result = trans.reader.readRecords(o);
9997 this.fireEvent("loadexception", this, o, trans.arg, e);
9998 trans.callback.call(trans.scope||window, null, trans.arg, false);
10001 this.fireEvent("load", this, o, trans.arg);
10002 trans.callback.call(trans.scope||window, result, trans.arg, true);
10006 handleFailure : function(trans){
10007 this.trans = false;
10008 this.destroyTrans(trans, false);
10009 this.fireEvent("loadexception", this, null, trans.arg);
10010 trans.callback.call(trans.scope||window, null, trans.arg, false);
10014 * Ext JS Library 1.1.1
10015 * Copyright(c) 2006-2007, Ext JS, LLC.
10017 * Originally Released Under LGPL - original licence link has changed is not relivant.
10020 * <script type="text/javascript">
10024 * @class Roo.data.JsonReader
10025 * @extends Roo.data.DataReader
10026 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10027 * based on mappings in a provided Roo.data.Record constructor.
10029 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10030 * in the reply previously.
10035 var RecordDef = Roo.data.Record.create([
10036 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10037 {name: 'occupation'} // This field will use "occupation" as the mapping.
10039 var myReader = new Roo.data.JsonReader({
10040 totalProperty: "results", // The property which contains the total dataset size (optional)
10041 root: "rows", // The property which contains an Array of row objects
10042 id: "id" // The property within each row object that provides an ID for the record (optional)
10046 * This would consume a JSON file like this:
10048 { 'results': 2, 'rows': [
10049 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10050 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10053 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10054 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10055 * paged from the remote server.
10056 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10057 * @cfg {String} root name of the property which contains the Array of row objects.
10058 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10060 * Create a new JsonReader
10061 * @param {Object} meta Metadata configuration options
10062 * @param {Object} recordType Either an Array of field definition objects,
10063 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10065 Roo.data.JsonReader = function(meta, recordType){
10068 // set some defaults:
10069 Roo.applyIf(meta, {
10070 totalProperty: 'total',
10071 successProperty : 'success',
10076 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10078 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10081 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10082 * Used by Store query builder to append _requestMeta to params.
10085 metaFromRemote : false,
10087 * This method is only used by a DataProxy which has retrieved data from a remote server.
10088 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10089 * @return {Object} data A data block which is used by an Roo.data.Store object as
10090 * a cache of Roo.data.Records.
10092 read : function(response){
10093 var json = response.responseText;
10095 var o = /* eval:var:o */ eval("("+json+")");
10097 throw {message: "JsonReader.read: Json object not found"};
10103 this.metaFromRemote = true;
10104 this.meta = o.metaData;
10105 this.recordType = Roo.data.Record.create(o.metaData.fields);
10106 this.onMetaChange(this.meta, this.recordType, o);
10108 return this.readRecords(o);
10111 // private function a store will implement
10112 onMetaChange : function(meta, recordType, o){
10119 simpleAccess: function(obj, subsc) {
10126 getJsonAccessor: function(){
10128 return function(expr) {
10130 return(re.test(expr))
10131 ? new Function("obj", "return obj." + expr)
10136 return Roo.emptyFn;
10141 * Create a data block containing Roo.data.Records from an XML document.
10142 * @param {Object} o An object which contains an Array of row objects in the property specified
10143 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10144 * which contains the total size of the dataset.
10145 * @return {Object} data A data block which is used by an Roo.data.Store object as
10146 * a cache of Roo.data.Records.
10148 readRecords : function(o){
10150 * After any data loads, the raw JSON data is available for further custom processing.
10154 var s = this.meta, Record = this.recordType,
10155 f = Record.prototype.fields, fi = f.items, fl = f.length;
10157 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10159 if(s.totalProperty) {
10160 this.getTotal = this.getJsonAccessor(s.totalProperty);
10162 if(s.successProperty) {
10163 this.getSuccess = this.getJsonAccessor(s.successProperty);
10165 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10167 var g = this.getJsonAccessor(s.id);
10168 this.getId = function(rec) {
10170 return (r === undefined || r === "") ? null : r;
10173 this.getId = function(){return null;};
10176 for(var jj = 0; jj < fl; jj++){
10178 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10179 this.ef[jj] = this.getJsonAccessor(map);
10183 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10184 if(s.totalProperty){
10185 var vt = parseInt(this.getTotal(o), 10);
10190 if(s.successProperty){
10191 var vs = this.getSuccess(o);
10192 if(vs === false || vs === 'false'){
10197 for(var i = 0; i < c; i++){
10200 var id = this.getId(n);
10201 for(var j = 0; j < fl; j++){
10203 var v = this.ef[j](n);
10205 Roo.log('missing convert for ' + f.name);
10209 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10211 var record = new Record(values, id);
10213 records[i] = record;
10219 totalRecords : totalRecords
10224 * Ext JS Library 1.1.1
10225 * Copyright(c) 2006-2007, Ext JS, LLC.
10227 * Originally Released Under LGPL - original licence link has changed is not relivant.
10230 * <script type="text/javascript">
10234 * @class Roo.data.ArrayReader
10235 * @extends Roo.data.DataReader
10236 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10237 * Each element of that Array represents a row of data fields. The
10238 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10239 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10243 var RecordDef = Roo.data.Record.create([
10244 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10245 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10247 var myReader = new Roo.data.ArrayReader({
10248 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10252 * This would consume an Array like this:
10254 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10256 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10258 * Create a new JsonReader
10259 * @param {Object} meta Metadata configuration options.
10260 * @param {Object} recordType Either an Array of field definition objects
10261 * as specified to {@link Roo.data.Record#create},
10262 * or an {@link Roo.data.Record} object
10263 * created using {@link Roo.data.Record#create}.
10265 Roo.data.ArrayReader = function(meta, recordType){
10266 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10269 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10271 * Create a data block containing Roo.data.Records from an XML document.
10272 * @param {Object} o An Array of row objects which represents the dataset.
10273 * @return {Object} data A data block which is used by an Roo.data.Store object as
10274 * a cache of Roo.data.Records.
10276 readRecords : function(o){
10277 var sid = this.meta ? this.meta.id : null;
10278 var recordType = this.recordType, fields = recordType.prototype.fields;
10281 for(var i = 0; i < root.length; i++){
10284 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10285 for(var j = 0, jlen = fields.length; j < jlen; j++){
10286 var f = fields.items[j];
10287 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10288 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10290 values[f.name] = v;
10292 var record = new recordType(values, id);
10294 records[records.length] = record;
10298 totalRecords : records.length
10307 * @class Roo.bootstrap.ComboBox
10308 * @extends Roo.bootstrap.TriggerField
10309 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10310 * @cfg {Boolean} append (true|false) default false
10311 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10312 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10313 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10314 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10315 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10317 * Create a new ComboBox.
10318 * @param {Object} config Configuration options
10320 Roo.bootstrap.ComboBox = function(config){
10321 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10325 * Fires when the dropdown list is expanded
10326 * @param {Roo.bootstrap.ComboBox} combo This combo box
10331 * Fires when the dropdown list is collapsed
10332 * @param {Roo.bootstrap.ComboBox} combo This combo box
10336 * @event beforeselect
10337 * Fires before a list item is selected. Return false to cancel the selection.
10338 * @param {Roo.bootstrap.ComboBox} combo This combo box
10339 * @param {Roo.data.Record} record The data record returned from the underlying store
10340 * @param {Number} index The index of the selected item in the dropdown list
10342 'beforeselect' : true,
10345 * Fires when a list item is selected
10346 * @param {Roo.bootstrap.ComboBox} combo This combo box
10347 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10348 * @param {Number} index The index of the selected item in the dropdown list
10352 * @event beforequery
10353 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10354 * The event object passed has these properties:
10355 * @param {Roo.bootstrap.ComboBox} combo This combo box
10356 * @param {String} query The query
10357 * @param {Boolean} forceAll true to force "all" query
10358 * @param {Boolean} cancel true to cancel the query
10359 * @param {Object} e The query event object
10361 'beforequery': true,
10364 * Fires when the 'add' icon is pressed (add a listener to enable add button)
10365 * @param {Roo.bootstrap.ComboBox} combo This combo box
10370 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10371 * @param {Roo.bootstrap.ComboBox} combo This combo box
10372 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10377 * Fires when the remove value from the combobox array
10378 * @param {Roo.bootstrap.ComboBox} combo This combo box
10385 this.tickItems = [];
10387 this.selectedIndex = -1;
10388 if(this.mode == 'local'){
10389 if(config.queryDelay === undefined){
10390 this.queryDelay = 10;
10392 if(config.minChars === undefined){
10398 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10401 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10402 * rendering into an Roo.Editor, defaults to false)
10405 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10406 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10409 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10412 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10413 * the dropdown list (defaults to undefined, with no header element)
10417 * @cfg {String/Roo.Template} tpl The template to use to render the output
10421 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10423 listWidth: undefined,
10425 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10426 * mode = 'remote' or 'text' if mode = 'local')
10428 displayField: undefined,
10430 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10431 * mode = 'remote' or 'value' if mode = 'local').
10432 * Note: use of a valueField requires the user make a selection
10433 * in order for a value to be mapped.
10435 valueField: undefined,
10439 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10440 * field's data value (defaults to the underlying DOM element's name)
10442 hiddenName: undefined,
10444 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10448 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10450 selectedClass: 'active',
10453 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10457 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10458 * anchor positions (defaults to 'tl-bl')
10460 listAlign: 'tl-bl?',
10462 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10466 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
10467 * query specified by the allQuery config option (defaults to 'query')
10469 triggerAction: 'query',
10471 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10472 * (defaults to 4, does not apply if editable = false)
10476 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10477 * delay (typeAheadDelay) if it matches a known value (defaults to false)
10481 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10482 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10486 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10487 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
10491 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
10492 * when editable = true (defaults to false)
10494 selectOnFocus:false,
10496 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10498 queryParam: 'query',
10500 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
10501 * when mode = 'remote' (defaults to 'Loading...')
10503 loadingText: 'Loading...',
10505 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10509 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10513 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10514 * traditional select (defaults to true)
10518 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10522 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10526 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10527 * listWidth has a higher value)
10531 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10532 * allow the user to set arbitrary text into the field (defaults to false)
10534 forceSelection:false,
10536 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10537 * if typeAhead = true (defaults to 250)
10539 typeAheadDelay : 250,
10541 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10542 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10544 valueNotFoundText : undefined,
10546 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10548 blockFocus : false,
10551 * @cfg {Boolean} disableClear Disable showing of clear button.
10553 disableClear : false,
10555 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
10557 alwaysQuery : false,
10560 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
10574 btnPosition : 'right',
10575 triggerList : true,
10576 showToggleBtn : true,
10577 // element that contains real text value.. (when hidden is used..)
10579 getAutoCreate : function()
10586 if(!this.tickable){
10587 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10592 * ComboBox with tickable selections
10595 var align = this.labelAlign || this.parentLabelAlign();
10598 cls : 'form-group roo-combobox-tickable' //input-group
10604 cls : 'tickable-buttons',
10609 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10616 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10623 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10630 Roo.each(buttons.cn, function(c){
10632 c.cls += ' btn-' + _this.size;
10635 if (_this.disabled) {
10646 cls: 'form-hidden-field'
10650 cls: 'select2-choices',
10654 cls: 'select2-search-field',
10666 cls: 'select2-container input-group select2-container-multi',
10671 // cls: 'typeahead typeahead-long dropdown-menu',
10672 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
10677 if (align ==='left' && this.fieldLabel.length) {
10679 Roo.log("left and has label");
10685 cls : 'control-label col-sm-' + this.labelWidth,
10686 html : this.fieldLabel
10690 cls : "col-sm-" + (12 - this.labelWidth),
10697 } else if ( this.fieldLabel.length) {
10703 //cls : 'input-group-addon',
10704 html : this.fieldLabel
10714 Roo.log(" no label && no align");
10721 ['xs','sm','md','lg'].map(function(size){
10722 if (settings[size]) {
10723 cfg.cls += ' col-' + size + '-' + settings[size];
10732 initEvents: function()
10736 throw "can not find store for combo";
10738 this.store = Roo.factory(this.store, Roo.data);
10741 this.initTickableEvents();
10745 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10747 if(this.hiddenName){
10749 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10751 this.hiddenField.dom.value =
10752 this.hiddenValue !== undefined ? this.hiddenValue :
10753 this.value !== undefined ? this.value : '';
10755 // prevent input submission
10756 this.el.dom.removeAttribute('name');
10757 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10762 // this.el.dom.setAttribute('autocomplete', 'off');
10765 var cls = 'x-combo-list';
10767 //this.list = new Roo.Layer({
10768 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10774 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10775 _this.list.setWidth(lw);
10778 this.list.on('mouseover', this.onViewOver, this);
10779 this.list.on('mousemove', this.onViewMove, this);
10781 this.list.on('scroll', this.onViewScroll, this);
10784 this.list.swallowEvent('mousewheel');
10785 this.assetHeight = 0;
10788 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10789 this.assetHeight += this.header.getHeight();
10792 this.innerList = this.list.createChild({cls:cls+'-inner'});
10793 this.innerList.on('mouseover', this.onViewOver, this);
10794 this.innerList.on('mousemove', this.onViewMove, this);
10795 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10797 if(this.allowBlank && !this.pageSize && !this.disableClear){
10798 this.footer = this.list.createChild({cls:cls+'-ft'});
10799 this.pageTb = new Roo.Toolbar(this.footer);
10803 this.footer = this.list.createChild({cls:cls+'-ft'});
10804 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10805 {pageSize: this.pageSize});
10809 if (this.pageTb && this.allowBlank && !this.disableClear) {
10811 this.pageTb.add(new Roo.Toolbar.Fill(), {
10812 cls: 'x-btn-icon x-btn-clear',
10814 handler: function()
10817 _this.clearValue();
10818 _this.onSelect(false, -1);
10823 this.assetHeight += this.footer.getHeight();
10828 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10831 this.view = new Roo.View(this.list, this.tpl, {
10832 singleSelect:true, store: this.store, selectedClass: this.selectedClass
10834 //this.view.wrapEl.setDisplayed(false);
10835 this.view.on('click', this.onViewClick, this);
10839 this.store.on('beforeload', this.onBeforeLoad, this);
10840 this.store.on('load', this.onLoad, this);
10841 this.store.on('loadexception', this.onLoadException, this);
10843 if(this.resizable){
10844 this.resizer = new Roo.Resizable(this.list, {
10845 pinned:true, handles:'se'
10847 this.resizer.on('resize', function(r, w, h){
10848 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10849 this.listWidth = w;
10850 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10851 this.restrictHeight();
10853 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10856 if(!this.editable){
10857 this.editable = true;
10858 this.setEditable(false);
10863 if (typeof(this.events.add.listeners) != 'undefined') {
10865 this.addicon = this.wrap.createChild(
10866 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
10868 this.addicon.on('click', function(e) {
10869 this.fireEvent('add', this);
10872 if (typeof(this.events.edit.listeners) != 'undefined') {
10874 this.editicon = this.wrap.createChild(
10875 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
10876 if (this.addicon) {
10877 this.editicon.setStyle('margin-left', '40px');
10879 this.editicon.on('click', function(e) {
10881 // we fire even if inothing is selected..
10882 this.fireEvent('edit', this, this.lastData );
10888 this.keyNav = new Roo.KeyNav(this.inputEl(), {
10889 "up" : function(e){
10890 this.inKeyMode = true;
10894 "down" : function(e){
10895 if(!this.isExpanded()){
10896 this.onTriggerClick();
10898 this.inKeyMode = true;
10903 "enter" : function(e){
10904 // this.onViewClick();
10908 if(this.fireEvent("specialkey", this, e)){
10909 this.onViewClick(false);
10915 "esc" : function(e){
10919 "tab" : function(e){
10922 if(this.fireEvent("specialkey", this, e)){
10923 this.onViewClick(false);
10931 doRelay : function(foo, bar, hname){
10932 if(hname == 'down' || this.scope.isExpanded()){
10933 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10942 this.queryDelay = Math.max(this.queryDelay || 10,
10943 this.mode == 'local' ? 10 : 250);
10946 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10948 if(this.typeAhead){
10949 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10951 if(this.editable !== false){
10952 this.inputEl().on("keyup", this.onKeyUp, this);
10954 if(this.forceSelection){
10955 this.inputEl().on('blur', this.doForce, this);
10959 this.choices = this.el.select('ul.select2-choices', true).first();
10960 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10964 initTickableEvents: function()
10968 if(this.hiddenName){
10970 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10972 this.hiddenField.dom.value =
10973 this.hiddenValue !== undefined ? this.hiddenValue :
10974 this.value !== undefined ? this.value : '';
10976 // prevent input submission
10977 this.el.dom.removeAttribute('name');
10978 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10983 // this.list = this.el.select('ul.dropdown-menu',true).first();
10985 this.choices = this.el.select('ul.select2-choices', true).first();
10986 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10987 if(this.triggerList){
10988 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10991 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10992 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10994 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10995 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10997 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10998 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
11000 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
11001 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
11002 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11005 this.cancelBtn.hide();
11010 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11011 _this.list.setWidth(lw);
11014 this.list.on('mouseover', this.onViewOver, this);
11015 this.list.on('mousemove', this.onViewMove, this);
11017 this.list.on('scroll', this.onViewScroll, this);
11020 this.tpl = '<li class="select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></li>';
11023 this.view = new Roo.View(this.list, this.tpl, {
11024 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11027 //this.view.wrapEl.setDisplayed(false);
11028 this.view.on('click', this.onViewClick, this);
11032 this.store.on('beforeload', this.onBeforeLoad, this);
11033 this.store.on('load', this.onLoad, this);
11034 this.store.on('loadexception', this.onLoadException, this);
11036 // this.keyNav = new Roo.KeyNav(this.inputEl(), {
11037 // "up" : function(e){
11038 // this.inKeyMode = true;
11039 // this.selectPrev();
11042 // "down" : function(e){
11043 // if(!this.isExpanded()){
11044 // this.onTriggerClick();
11046 // this.inKeyMode = true;
11047 // this.selectNext();
11051 // "enter" : function(e){
11052 //// this.onViewClick();
11054 // this.collapse();
11056 // if(this.fireEvent("specialkey", this, e)){
11057 // this.onViewClick(false);
11063 // "esc" : function(e){
11064 // this.collapse();
11067 // "tab" : function(e){
11068 // this.collapse();
11070 // if(this.fireEvent("specialkey", this, e)){
11071 // this.onViewClick(false);
11079 // doRelay : function(foo, bar, hname){
11080 // if(hname == 'down' || this.scope.isExpanded()){
11081 // return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11086 // forceKeyDown: true
11090 this.queryDelay = Math.max(this.queryDelay || 10,
11091 this.mode == 'local' ? 10 : 250);
11094 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11096 if(this.typeAhead){
11097 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11101 onDestroy : function(){
11103 this.view.setStore(null);
11104 this.view.el.removeAllListeners();
11105 this.view.el.remove();
11106 this.view.purgeListeners();
11109 this.list.dom.innerHTML = '';
11113 this.store.un('beforeload', this.onBeforeLoad, this);
11114 this.store.un('load', this.onLoad, this);
11115 this.store.un('loadexception', this.onLoadException, this);
11117 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11121 fireKey : function(e){
11122 if(e.isNavKeyPress() && !this.list.isVisible()){
11123 this.fireEvent("specialkey", this, e);
11128 onResize: function(w, h){
11129 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11131 // if(typeof w != 'number'){
11132 // // we do not handle it!?!?
11135 // var tw = this.trigger.getWidth();
11136 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11137 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11139 // this.inputEl().setWidth( this.adjustWidth('input', x));
11141 // //this.trigger.setStyle('left', x+'px');
11143 // if(this.list && this.listWidth === undefined){
11144 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11145 // this.list.setWidth(lw);
11146 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11154 * Allow or prevent the user from directly editing the field text. If false is passed,
11155 * the user will only be able to select from the items defined in the dropdown list. This method
11156 * is the runtime equivalent of setting the 'editable' config option at config time.
11157 * @param {Boolean} value True to allow the user to directly edit the field text
11159 setEditable : function(value){
11160 if(value == this.editable){
11163 this.editable = value;
11165 this.inputEl().dom.setAttribute('readOnly', true);
11166 this.inputEl().on('mousedown', this.onTriggerClick, this);
11167 this.inputEl().addClass('x-combo-noedit');
11169 this.inputEl().dom.setAttribute('readOnly', false);
11170 this.inputEl().un('mousedown', this.onTriggerClick, this);
11171 this.inputEl().removeClass('x-combo-noedit');
11177 onBeforeLoad : function(combo,opts){
11178 if(!this.hasFocus){
11182 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11184 this.restrictHeight();
11185 this.selectedIndex = -1;
11189 onLoad : function(){
11191 this.hasQuery = false;
11193 if(!this.hasFocus){
11197 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11198 this.loading.hide();
11201 if(this.store.getCount() > 0){
11203 // this.restrictHeight();
11204 if(this.lastQuery == this.allQuery){
11205 if(this.editable && !this.tickable){
11206 this.inputEl().dom.select();
11210 !this.selectByValue(this.value, true) &&
11211 this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' ||
11212 this.store.lastOptions.add != true)
11214 this.select(0, true);
11217 if(this.autoFocus){
11220 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11221 this.taTask.delay(this.typeAheadDelay);
11225 this.onEmptyResults();
11231 onLoadException : function()
11233 this.hasQuery = false;
11235 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11236 this.loading.hide();
11240 Roo.log(this.store.reader.jsonData);
11241 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11243 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11249 onTypeAhead : function(){
11250 if(this.store.getCount() > 0){
11251 var r = this.store.getAt(0);
11252 var newValue = r.data[this.displayField];
11253 var len = newValue.length;
11254 var selStart = this.getRawValue().length;
11256 if(selStart != len){
11257 this.setRawValue(newValue);
11258 this.selectText(selStart, newValue.length);
11264 onSelect : function(record, index){
11266 if(this.fireEvent('beforeselect', this, record, index) !== false){
11268 this.setFromData(index > -1 ? record.data : false);
11271 this.fireEvent('select', this, record, index);
11276 * Returns the currently selected field value or empty string if no value is set.
11277 * @return {String} value The selected value
11279 getValue : function(){
11282 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11285 if(this.valueField){
11286 return typeof this.value != 'undefined' ? this.value : '';
11288 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11293 * Clears any text/value currently set in the field
11295 clearValue : function(){
11296 if(this.hiddenField){
11297 this.hiddenField.dom.value = '';
11300 this.setRawValue('');
11301 this.lastSelectionText = '';
11306 * Sets the specified value into the field. If the value finds a match, the corresponding record text
11307 * will be displayed in the field. If the value does not match the data value of an existing item,
11308 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11309 * Otherwise the field will be blank (although the value will still be set).
11310 * @param {String} value The value to match
11312 setValue : function(v){
11319 if(this.valueField){
11320 var r = this.findRecord(this.valueField, v);
11322 text = r.data[this.displayField];
11323 }else if(this.valueNotFoundText !== undefined){
11324 text = this.valueNotFoundText;
11327 this.lastSelectionText = text;
11328 if(this.hiddenField){
11329 this.hiddenField.dom.value = v;
11331 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11335 * @property {Object} the last set data for the element
11340 * Sets the value of the field based on a object which is related to the record format for the store.
11341 * @param {Object} value the value to set as. or false on reset?
11343 setFromData : function(o){
11350 var dv = ''; // display value
11351 var vv = ''; // value value..
11353 if (this.displayField) {
11354 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11356 // this is an error condition!!!
11357 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11360 if(this.valueField){
11361 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11364 if(this.hiddenField){
11365 this.hiddenField.dom.value = vv;
11367 this.lastSelectionText = dv;
11368 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11372 // no hidden field.. - we store the value in 'value', but still display
11373 // display field!!!!
11374 this.lastSelectionText = dv;
11375 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11381 reset : function(){
11382 // overridden so that last data is reset..
11383 this.setValue(this.originalValue);
11384 this.clearInvalid();
11385 this.lastData = false;
11387 this.view.clearSelections();
11391 findRecord : function(prop, value){
11393 if(this.store.getCount() > 0){
11394 this.store.each(function(r){
11395 if(r.data[prop] == value){
11405 getName: function()
11407 // returns hidden if it's set..
11408 if (!this.rendered) {return ''};
11409 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11413 onViewMove : function(e, t){
11414 this.inKeyMode = false;
11418 onViewOver : function(e, t){
11419 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11422 var item = this.view.findItemFromChild(t);
11425 var index = this.view.indexOf(item);
11426 this.select(index, false);
11431 onViewClick : function(view, doFocus, el, e)
11433 var index = this.view.getSelectedIndexes()[0];
11435 var r = this.store.getAt(index);
11439 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11446 Roo.each(this.tickItems, function(v,k){
11448 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11449 _this.tickItems.splice(k, 1);
11459 this.tickItems.push(r.data);
11464 this.onSelect(r, index);
11466 if(doFocus !== false && !this.blockFocus){
11467 this.inputEl().focus();
11472 restrictHeight : function(){
11473 //this.innerList.dom.style.height = '';
11474 //var inner = this.innerList.dom;
11475 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11476 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11477 //this.list.beginUpdate();
11478 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11479 this.list.alignTo(this.inputEl(), this.listAlign);
11480 this.list.alignTo(this.inputEl(), this.listAlign);
11481 //this.list.endUpdate();
11485 onEmptyResults : function(){
11490 * Returns true if the dropdown list is expanded, else false.
11492 isExpanded : function(){
11493 return this.list.isVisible();
11497 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11498 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11499 * @param {String} value The data value of the item to select
11500 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11501 * selected item if it is not currently in view (defaults to true)
11502 * @return {Boolean} True if the value matched an item in the list, else false
11504 selectByValue : function(v, scrollIntoView){
11505 if(v !== undefined && v !== null){
11506 var r = this.findRecord(this.valueField || this.displayField, v);
11508 this.select(this.store.indexOf(r), scrollIntoView);
11516 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11517 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11518 * @param {Number} index The zero-based index of the list item to select
11519 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11520 * selected item if it is not currently in view (defaults to true)
11522 select : function(index, scrollIntoView){
11523 this.selectedIndex = index;
11524 this.view.select(index);
11525 if(scrollIntoView !== false){
11526 var el = this.view.getNode(index);
11527 if(el && !this.multiple && !this.tickable){
11528 this.list.scrollChildIntoView(el, false);
11534 selectNext : function(){
11535 var ct = this.store.getCount();
11537 if(this.selectedIndex == -1){
11539 }else if(this.selectedIndex < ct-1){
11540 this.select(this.selectedIndex+1);
11546 selectPrev : function(){
11547 var ct = this.store.getCount();
11549 if(this.selectedIndex == -1){
11551 }else if(this.selectedIndex != 0){
11552 this.select(this.selectedIndex-1);
11558 onKeyUp : function(e){
11559 if(this.editable !== false && !e.isSpecialKey()){
11560 this.lastKey = e.getKey();
11561 this.dqTask.delay(this.queryDelay);
11566 validateBlur : function(){
11567 return !this.list || !this.list.isVisible();
11571 initQuery : function(){
11572 this.doQuery(this.getRawValue());
11576 doForce : function(){
11577 if(this.inputEl().dom.value.length > 0){
11578 this.inputEl().dom.value =
11579 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11585 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11586 * query allowing the query action to be canceled if needed.
11587 * @param {String} query The SQL query to execute
11588 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11589 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11590 * saved in the current store (defaults to false)
11592 doQuery : function(q, forceAll){
11594 if(q === undefined || q === null){
11599 forceAll: forceAll,
11603 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11608 forceAll = qe.forceAll;
11609 if(forceAll === true || (q.length >= this.minChars)){
11611 this.hasQuery = true;
11613 if(this.lastQuery != q || this.alwaysQuery){
11614 this.lastQuery = q;
11615 if(this.mode == 'local'){
11616 this.selectedIndex = -1;
11618 this.store.clearFilter();
11620 this.store.filter(this.displayField, q);
11624 this.store.baseParams[this.queryParam] = q;
11626 var options = {params : this.getParams(q)};
11629 options.add = true;
11630 options.params.start = this.page * this.pageSize;
11633 this.store.load(options);
11635 * this code will make the page width larger, at the beginning, the list not align correctly,
11636 * we should expand the list on onLoad
11637 * so command out it
11642 this.selectedIndex = -1;
11647 this.loadNext = false;
11651 getParams : function(q){
11653 //p[this.queryParam] = q;
11657 p.limit = this.pageSize;
11663 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11665 collapse : function(){
11666 if(!this.isExpanded()){
11674 this.cancelBtn.hide();
11675 this.trigger.show();
11678 Roo.get(document).un('mousedown', this.collapseIf, this);
11679 Roo.get(document).un('mousewheel', this.collapseIf, this);
11680 if (!this.editable) {
11681 Roo.get(document).un('keydown', this.listKeyPress, this);
11683 this.fireEvent('collapse', this);
11687 collapseIf : function(e){
11688 var in_combo = e.within(this.el);
11689 var in_list = e.within(this.list);
11690 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11692 if (in_combo || in_list || is_list) {
11693 //e.stopPropagation();
11698 this.onTickableFooterButtonClick(e, false, false);
11706 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11708 expand : function(){
11710 if(this.isExpanded() || !this.hasFocus){
11714 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11715 this.list.setWidth(lw);
11722 this.restrictHeight();
11726 this.tickItems = Roo.apply([], this.item);
11729 this.cancelBtn.show();
11730 this.trigger.hide();
11734 Roo.get(document).on('mousedown', this.collapseIf, this);
11735 Roo.get(document).on('mousewheel', this.collapseIf, this);
11736 if (!this.editable) {
11737 Roo.get(document).on('keydown', this.listKeyPress, this);
11740 this.fireEvent('expand', this);
11744 // Implements the default empty TriggerField.onTriggerClick function
11745 onTriggerClick : function(e)
11747 Roo.log('trigger click');
11749 if(this.disabled || !this.triggerList){
11754 this.loadNext = false;
11756 if(this.isExpanded()){
11758 if (!this.blockFocus) {
11759 this.inputEl().focus();
11763 this.hasFocus = true;
11764 if(this.triggerAction == 'all') {
11765 this.doQuery(this.allQuery, true);
11767 this.doQuery(this.getRawValue());
11769 if (!this.blockFocus) {
11770 this.inputEl().focus();
11775 onTickableTriggerClick : function(e)
11782 this.loadNext = false;
11783 this.hasFocus = true;
11785 if(this.triggerAction == 'all') {
11786 this.doQuery(this.allQuery, true);
11788 this.doQuery(this.getRawValue());
11792 onSearchFieldClick : function(e)
11794 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11799 this.loadNext = false;
11800 this.hasFocus = true;
11802 if(this.triggerAction == 'all') {
11803 this.doQuery(this.allQuery, true);
11805 this.doQuery(this.getRawValue());
11809 listKeyPress : function(e)
11811 //Roo.log('listkeypress');
11812 // scroll to first matching element based on key pres..
11813 if (e.isSpecialKey()) {
11816 var k = String.fromCharCode(e.getKey()).toUpperCase();
11819 var csel = this.view.getSelectedNodes();
11820 var cselitem = false;
11822 var ix = this.view.indexOf(csel[0]);
11823 cselitem = this.store.getAt(ix);
11824 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11830 this.store.each(function(v) {
11832 // start at existing selection.
11833 if (cselitem.id == v.id) {
11839 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11840 match = this.store.indexOf(v);
11846 if (match === false) {
11847 return true; // no more action?
11850 this.view.select(match);
11851 var sn = Roo.get(this.view.getSelectedNodes()[0])
11852 sn.scrollIntoView(sn.dom.parentNode, false);
11855 onViewScroll : function(e, t){
11857 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){
11861 this.hasQuery = true;
11863 this.loading = this.list.select('.loading', true).first();
11865 if(this.loading === null){
11866 this.list.createChild({
11868 cls: 'loading select2-more-results select2-active',
11869 html: 'Loading more results...'
11872 this.loading = this.list.select('.loading', true).first();
11874 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11876 this.loading.hide();
11879 this.loading.show();
11884 this.loadNext = true;
11886 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11891 addItem : function(o)
11893 var dv = ''; // display value
11895 if (this.displayField) {
11896 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11898 // this is an error condition!!!
11899 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11906 var choice = this.choices.createChild({
11908 cls: 'select2-search-choice',
11917 cls: 'select2-search-choice-close',
11922 }, this.searchField);
11924 var close = choice.select('a.select2-search-choice-close', true).first()
11926 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11934 this.inputEl().dom.value = '';
11938 onRemoveItem : function(e, _self, o)
11940 e.preventDefault();
11941 var index = this.item.indexOf(o.data) * 1;
11944 Roo.log('not this item?!');
11948 this.item.splice(index, 1);
11953 this.fireEvent('remove', this, e);
11957 syncValue : function()
11959 if(!this.item.length){
11966 Roo.each(this.item, function(i){
11967 if(_this.valueField){
11968 value.push(i[_this.valueField]);
11975 this.value = value.join(',');
11977 if(this.hiddenField){
11978 this.hiddenField.dom.value = this.value;
11982 clearItem : function()
11984 if(!this.multiple){
11990 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11997 inputEl: function ()
12000 return this.searchField;
12002 return this.el.select('input.form-control',true).first();
12006 onTickableFooterButtonClick : function(e, btn, el)
12008 e.preventDefault();
12010 if(btn && btn.name == 'cancel'){
12011 this.tickItems = Roo.apply([], this.item);
12020 Roo.each(this.tickItems, function(o){
12028 validate : function()
12030 var v = this.getRawValue();
12033 v = this.getValue();
12036 if(this.disabled || this.validateValue(v)){
12037 this.clearInvalid();
12046 * @cfg {Boolean} grow
12050 * @cfg {Number} growMin
12054 * @cfg {Number} growMax
12064 * Ext JS Library 1.1.1
12065 * Copyright(c) 2006-2007, Ext JS, LLC.
12067 * Originally Released Under LGPL - original licence link has changed is not relivant.
12070 * <script type="text/javascript">
12075 * @extends Roo.util.Observable
12076 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
12077 * This class also supports single and multi selection modes. <br>
12078 * Create a data model bound view:
12080 var store = new Roo.data.Store(...);
12082 var view = new Roo.View({
12084 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
12086 singleSelect: true,
12087 selectedClass: "ydataview-selected",
12091 // listen for node click?
12092 view.on("click", function(vw, index, node, e){
12093 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12097 dataModel.load("foobar.xml");
12099 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12101 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12102 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12104 * Note: old style constructor is still suported (container, template, config)
12107 * Create a new View
12108 * @param {Object} config The config object
12111 Roo.View = function(config, depreciated_tpl, depreciated_config){
12113 this.parent = false;
12115 if (typeof(depreciated_tpl) == 'undefined') {
12116 // new way.. - universal constructor.
12117 Roo.apply(this, config);
12118 this.el = Roo.get(this.el);
12121 this.el = Roo.get(config);
12122 this.tpl = depreciated_tpl;
12123 Roo.apply(this, depreciated_config);
12125 this.wrapEl = this.el.wrap().wrap();
12126 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12129 if(typeof(this.tpl) == "string"){
12130 this.tpl = new Roo.Template(this.tpl);
12132 // support xtype ctors..
12133 this.tpl = new Roo.factory(this.tpl, Roo);
12137 this.tpl.compile();
12142 * @event beforeclick
12143 * Fires before a click is processed. Returns false to cancel the default action.
12144 * @param {Roo.View} this
12145 * @param {Number} index The index of the target node
12146 * @param {HTMLElement} node The target node
12147 * @param {Roo.EventObject} e The raw event object
12149 "beforeclick" : true,
12152 * Fires when a template node is clicked.
12153 * @param {Roo.View} this
12154 * @param {Number} index The index of the target node
12155 * @param {HTMLElement} node The target node
12156 * @param {Roo.EventObject} e The raw event object
12161 * Fires when a template node is double clicked.
12162 * @param {Roo.View} this
12163 * @param {Number} index The index of the target node
12164 * @param {HTMLElement} node The target node
12165 * @param {Roo.EventObject} e The raw event object
12169 * @event contextmenu
12170 * Fires when a template node is right clicked.
12171 * @param {Roo.View} this
12172 * @param {Number} index The index of the target node
12173 * @param {HTMLElement} node The target node
12174 * @param {Roo.EventObject} e The raw event object
12176 "contextmenu" : true,
12178 * @event selectionchange
12179 * Fires when the selected nodes change.
12180 * @param {Roo.View} this
12181 * @param {Array} selections Array of the selected nodes
12183 "selectionchange" : true,
12186 * @event beforeselect
12187 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12188 * @param {Roo.View} this
12189 * @param {HTMLElement} node The node to be selected
12190 * @param {Array} selections Array of currently selected nodes
12192 "beforeselect" : true,
12194 * @event preparedata
12195 * Fires on every row to render, to allow you to change the data.
12196 * @param {Roo.View} this
12197 * @param {Object} data to be rendered (change this)
12199 "preparedata" : true
12207 "click": this.onClick,
12208 "dblclick": this.onDblClick,
12209 "contextmenu": this.onContextMenu,
12213 this.selections = [];
12215 this.cmp = new Roo.CompositeElementLite([]);
12217 this.store = Roo.factory(this.store, Roo.data);
12218 this.setStore(this.store, true);
12221 if ( this.footer && this.footer.xtype) {
12223 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12225 this.footer.dataSource = this.store
12226 this.footer.container = fctr;
12227 this.footer = Roo.factory(this.footer, Roo);
12228 fctr.insertFirst(this.el);
12230 // this is a bit insane - as the paging toolbar seems to detach the el..
12231 // dom.parentNode.parentNode.parentNode
12232 // they get detached?
12236 Roo.View.superclass.constructor.call(this);
12241 Roo.extend(Roo.View, Roo.util.Observable, {
12244 * @cfg {Roo.data.Store} store Data store to load data from.
12249 * @cfg {String|Roo.Element} el The container element.
12254 * @cfg {String|Roo.Template} tpl The template used by this View
12258 * @cfg {String} dataName the named area of the template to use as the data area
12259 * Works with domtemplates roo-name="name"
12263 * @cfg {String} selectedClass The css class to add to selected nodes
12265 selectedClass : "x-view-selected",
12267 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12272 * @cfg {String} text to display on mask (default Loading)
12276 * @cfg {Boolean} multiSelect Allow multiple selection
12278 multiSelect : false,
12280 * @cfg {Boolean} singleSelect Allow single selection
12282 singleSelect: false,
12285 * @cfg {Boolean} toggleSelect - selecting
12287 toggleSelect : false,
12290 * @cfg {Boolean} tickable - selecting
12295 * Returns the element this view is bound to.
12296 * @return {Roo.Element}
12298 getEl : function(){
12299 return this.wrapEl;
12305 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12307 refresh : function(){
12308 Roo.log('refresh');
12311 // if we are using something like 'domtemplate', then
12312 // the what gets used is:
12313 // t.applySubtemplate(NAME, data, wrapping data..)
12314 // the outer template then get' applied with
12315 // the store 'extra data'
12316 // and the body get's added to the
12317 // roo-name="data" node?
12318 // <span class='roo-tpl-{name}'></span> ?????
12322 this.clearSelections();
12323 this.el.update("");
12325 var records = this.store.getRange();
12326 if(records.length < 1) {
12328 // is this valid?? = should it render a template??
12330 this.el.update(this.emptyText);
12334 if (this.dataName) {
12335 this.el.update(t.apply(this.store.meta)); //????
12336 el = this.el.child('.roo-tpl-' + this.dataName);
12339 for(var i = 0, len = records.length; i < len; i++){
12340 var data = this.prepareData(records[i].data, i, records[i]);
12341 this.fireEvent("preparedata", this, data, i, records[i]);
12343 var d = Roo.apply({}, data);
12346 Roo.apply(d, {'roo-id' : Roo.id()});
12350 Roo.each(this.parent.item, function(item){
12351 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12354 Roo.apply(d, {'roo-data-checked' : 'checked'});
12358 html[html.length] = Roo.util.Format.trim(
12360 t.applySubtemplate(this.dataName, d, this.store.meta) :
12367 el.update(html.join(""));
12368 this.nodes = el.dom.childNodes;
12369 this.updateIndexes(0);
12374 * Function to override to reformat the data that is sent to
12375 * the template for each node.
12376 * DEPRICATED - use the preparedata event handler.
12377 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12378 * a JSON object for an UpdateManager bound view).
12380 prepareData : function(data, index, record)
12382 this.fireEvent("preparedata", this, data, index, record);
12386 onUpdate : function(ds, record){
12387 Roo.log('on update');
12388 this.clearSelections();
12389 var index = this.store.indexOf(record);
12390 var n = this.nodes[index];
12391 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12392 n.parentNode.removeChild(n);
12393 this.updateIndexes(index, index);
12399 onAdd : function(ds, records, index)
12401 Roo.log(['on Add', ds, records, index] );
12402 this.clearSelections();
12403 if(this.nodes.length == 0){
12407 var n = this.nodes[index];
12408 for(var i = 0, len = records.length; i < len; i++){
12409 var d = this.prepareData(records[i].data, i, records[i]);
12411 this.tpl.insertBefore(n, d);
12414 this.tpl.append(this.el, d);
12417 this.updateIndexes(index);
12420 onRemove : function(ds, record, index){
12421 Roo.log('onRemove');
12422 this.clearSelections();
12423 var el = this.dataName ?
12424 this.el.child('.roo-tpl-' + this.dataName) :
12427 el.dom.removeChild(this.nodes[index]);
12428 this.updateIndexes(index);
12432 * Refresh an individual node.
12433 * @param {Number} index
12435 refreshNode : function(index){
12436 this.onUpdate(this.store, this.store.getAt(index));
12439 updateIndexes : function(startIndex, endIndex){
12440 var ns = this.nodes;
12441 startIndex = startIndex || 0;
12442 endIndex = endIndex || ns.length - 1;
12443 for(var i = startIndex; i <= endIndex; i++){
12444 ns[i].nodeIndex = i;
12449 * Changes the data store this view uses and refresh the view.
12450 * @param {Store} store
12452 setStore : function(store, initial){
12453 if(!initial && this.store){
12454 this.store.un("datachanged", this.refresh);
12455 this.store.un("add", this.onAdd);
12456 this.store.un("remove", this.onRemove);
12457 this.store.un("update", this.onUpdate);
12458 this.store.un("clear", this.refresh);
12459 this.store.un("beforeload", this.onBeforeLoad);
12460 this.store.un("load", this.onLoad);
12461 this.store.un("loadexception", this.onLoad);
12465 store.on("datachanged", this.refresh, this);
12466 store.on("add", this.onAdd, this);
12467 store.on("remove", this.onRemove, this);
12468 store.on("update", this.onUpdate, this);
12469 store.on("clear", this.refresh, this);
12470 store.on("beforeload", this.onBeforeLoad, this);
12471 store.on("load", this.onLoad, this);
12472 store.on("loadexception", this.onLoad, this);
12480 * onbeforeLoad - masks the loading area.
12483 onBeforeLoad : function(store,opts)
12485 Roo.log('onBeforeLoad');
12487 this.el.update("");
12489 this.el.mask(this.mask ? this.mask : "Loading" );
12491 onLoad : function ()
12498 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12499 * @param {HTMLElement} node
12500 * @return {HTMLElement} The template node
12502 findItemFromChild : function(node){
12503 var el = this.dataName ?
12504 this.el.child('.roo-tpl-' + this.dataName,true) :
12507 if(!node || node.parentNode == el){
12510 var p = node.parentNode;
12511 while(p && p != el){
12512 if(p.parentNode == el){
12521 onClick : function(e){
12522 var item = this.findItemFromChild(e.getTarget());
12524 var index = this.indexOf(item);
12525 if(this.onItemClick(item, index, e) !== false){
12526 this.fireEvent("click", this, index, item, e);
12529 this.clearSelections();
12534 onContextMenu : function(e){
12535 var item = this.findItemFromChild(e.getTarget());
12537 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12542 onDblClick : function(e){
12543 var item = this.findItemFromChild(e.getTarget());
12545 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12549 onItemClick : function(item, index, e)
12551 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12554 if (this.toggleSelect) {
12555 var m = this.isSelected(item) ? 'unselect' : 'select';
12558 _t[m](item, true, false);
12561 if(this.multiSelect || this.singleSelect){
12562 if(this.multiSelect && e.shiftKey && this.lastSelection){
12563 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12565 this.select(item, this.multiSelect && e.ctrlKey);
12566 this.lastSelection = item;
12569 if(!this.tickable){
12570 e.preventDefault();
12578 * Get the number of selected nodes.
12581 getSelectionCount : function(){
12582 return this.selections.length;
12586 * Get the currently selected nodes.
12587 * @return {Array} An array of HTMLElements
12589 getSelectedNodes : function(){
12590 return this.selections;
12594 * Get the indexes of the selected nodes.
12597 getSelectedIndexes : function(){
12598 var indexes = [], s = this.selections;
12599 for(var i = 0, len = s.length; i < len; i++){
12600 indexes.push(s[i].nodeIndex);
12606 * Clear all selections
12607 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12609 clearSelections : function(suppressEvent){
12610 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12611 this.cmp.elements = this.selections;
12612 this.cmp.removeClass(this.selectedClass);
12613 this.selections = [];
12614 if(!suppressEvent){
12615 this.fireEvent("selectionchange", this, this.selections);
12621 * Returns true if the passed node is selected
12622 * @param {HTMLElement/Number} node The node or node index
12623 * @return {Boolean}
12625 isSelected : function(node){
12626 var s = this.selections;
12630 node = this.getNode(node);
12631 return s.indexOf(node) !== -1;
12636 * @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
12637 * @param {Boolean} keepExisting (optional) true to keep existing selections
12638 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12640 select : function(nodeInfo, keepExisting, suppressEvent){
12641 if(nodeInfo instanceof Array){
12643 this.clearSelections(true);
12645 for(var i = 0, len = nodeInfo.length; i < len; i++){
12646 this.select(nodeInfo[i], true, true);
12650 var node = this.getNode(nodeInfo);
12651 if(!node || this.isSelected(node)){
12652 return; // already selected.
12655 this.clearSelections(true);
12658 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12659 Roo.fly(node).addClass(this.selectedClass);
12660 this.selections.push(node);
12661 if(!suppressEvent){
12662 this.fireEvent("selectionchange", this, this.selections);
12670 * @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
12671 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12672 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12674 unselect : function(nodeInfo, keepExisting, suppressEvent)
12676 if(nodeInfo instanceof Array){
12677 Roo.each(this.selections, function(s) {
12678 this.unselect(s, nodeInfo);
12682 var node = this.getNode(nodeInfo);
12683 if(!node || !this.isSelected(node)){
12684 Roo.log("not selected");
12685 return; // not selected.
12689 Roo.each(this.selections, function(s) {
12691 Roo.fly(node).removeClass(this.selectedClass);
12698 this.selections= ns;
12699 this.fireEvent("selectionchange", this, this.selections);
12703 * Gets a template node.
12704 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12705 * @return {HTMLElement} The node or null if it wasn't found
12707 getNode : function(nodeInfo){
12708 if(typeof nodeInfo == "string"){
12709 return document.getElementById(nodeInfo);
12710 }else if(typeof nodeInfo == "number"){
12711 return this.nodes[nodeInfo];
12717 * Gets a range template nodes.
12718 * @param {Number} startIndex
12719 * @param {Number} endIndex
12720 * @return {Array} An array of nodes
12722 getNodes : function(start, end){
12723 var ns = this.nodes;
12724 start = start || 0;
12725 end = typeof end == "undefined" ? ns.length - 1 : end;
12728 for(var i = start; i <= end; i++){
12732 for(var i = start; i >= end; i--){
12740 * Finds the index of the passed node
12741 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12742 * @return {Number} The index of the node or -1
12744 indexOf : function(node){
12745 node = this.getNode(node);
12746 if(typeof node.nodeIndex == "number"){
12747 return node.nodeIndex;
12749 var ns = this.nodes;
12750 for(var i = 0, len = ns.length; i < len; i++){
12761 * based on jquery fullcalendar
12765 Roo.bootstrap = Roo.bootstrap || {};
12767 * @class Roo.bootstrap.Calendar
12768 * @extends Roo.bootstrap.Component
12769 * Bootstrap Calendar class
12770 * @cfg {Boolean} loadMask (true|false) default false
12771 * @cfg {Object} header generate the user specific header of the calendar, default false
12774 * Create a new Container
12775 * @param {Object} config The config object
12780 Roo.bootstrap.Calendar = function(config){
12781 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12785 * Fires when a date is selected
12786 * @param {DatePicker} this
12787 * @param {Date} date The selected date
12791 * @event monthchange
12792 * Fires when the displayed month changes
12793 * @param {DatePicker} this
12794 * @param {Date} date The selected month
12796 'monthchange': true,
12798 * @event evententer
12799 * Fires when mouse over an event
12800 * @param {Calendar} this
12801 * @param {event} Event
12803 'evententer': true,
12805 * @event eventleave
12806 * Fires when the mouse leaves an
12807 * @param {Calendar} this
12810 'eventleave': true,
12812 * @event eventclick
12813 * Fires when the mouse click an
12814 * @param {Calendar} this
12823 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12826 * @cfg {Number} startDay
12827 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12835 getAutoCreate : function(){
12838 var fc_button = function(name, corner, style, content ) {
12839 return Roo.apply({},{
12841 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12843 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12846 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12857 style : 'width:100%',
12864 cls : 'fc-header-left',
12866 fc_button('prev', 'left', 'arrow', '‹' ),
12867 fc_button('next', 'right', 'arrow', '›' ),
12868 { tag: 'span', cls: 'fc-header-space' },
12869 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12877 cls : 'fc-header-center',
12881 cls: 'fc-header-title',
12884 html : 'month / year'
12892 cls : 'fc-header-right',
12894 /* fc_button('month', 'left', '', 'month' ),
12895 fc_button('week', '', '', 'week' ),
12896 fc_button('day', 'right', '', 'day' )
12908 header = this.header;
12911 var cal_heads = function() {
12913 // fixme - handle this.
12915 for (var i =0; i < Date.dayNames.length; i++) {
12916 var d = Date.dayNames[i];
12919 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12920 html : d.substring(0,3)
12924 ret[0].cls += ' fc-first';
12925 ret[6].cls += ' fc-last';
12928 var cal_cell = function(n) {
12931 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12936 cls: 'fc-day-number',
12940 cls: 'fc-day-content',
12944 style: 'position: relative;' // height: 17px;
12956 var cal_rows = function() {
12959 for (var r = 0; r < 6; r++) {
12966 for (var i =0; i < Date.dayNames.length; i++) {
12967 var d = Date.dayNames[i];
12968 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12971 row.cn[0].cls+=' fc-first';
12972 row.cn[0].cn[0].style = 'min-height:90px';
12973 row.cn[6].cls+=' fc-last';
12977 ret[0].cls += ' fc-first';
12978 ret[4].cls += ' fc-prev-last';
12979 ret[5].cls += ' fc-last';
12986 cls: 'fc-border-separate',
12987 style : 'width:100%',
12995 cls : 'fc-first fc-last',
13013 cls : 'fc-content',
13014 style : "position: relative;",
13017 cls : 'fc-view fc-view-month fc-grid',
13018 style : 'position: relative',
13019 unselectable : 'on',
13022 cls : 'fc-event-container',
13023 style : 'position:absolute;z-index:8;top:0;left:0;'
13041 initEvents : function()
13044 throw "can not find store for calendar";
13050 style: "text-align:center",
13054 style: "background-color:white;width:50%;margin:250 auto",
13058 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
13069 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13071 var size = this.el.select('.fc-content', true).first().getSize();
13072 this.maskEl.setSize(size.width, size.height);
13073 this.maskEl.enableDisplayMode("block");
13074 if(!this.loadMask){
13075 this.maskEl.hide();
13078 this.store = Roo.factory(this.store, Roo.data);
13079 this.store.on('load', this.onLoad, this);
13080 this.store.on('beforeload', this.onBeforeLoad, this);
13084 this.cells = this.el.select('.fc-day',true);
13085 //Roo.log(this.cells);
13086 this.textNodes = this.el.query('.fc-day-number');
13087 this.cells.addClassOnOver('fc-state-hover');
13089 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13090 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13091 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13092 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13094 this.on('monthchange', this.onMonthChange, this);
13096 this.update(new Date().clearTime());
13099 resize : function() {
13100 var sz = this.el.getSize();
13102 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13103 this.el.select('.fc-day-content div',true).setHeight(34);
13108 showPrevMonth : function(e){
13109 this.update(this.activeDate.add("mo", -1));
13111 showToday : function(e){
13112 this.update(new Date().clearTime());
13115 showNextMonth : function(e){
13116 this.update(this.activeDate.add("mo", 1));
13120 showPrevYear : function(){
13121 this.update(this.activeDate.add("y", -1));
13125 showNextYear : function(){
13126 this.update(this.activeDate.add("y", 1));
13131 update : function(date)
13133 var vd = this.activeDate;
13134 this.activeDate = date;
13135 // if(vd && this.el){
13136 // var t = date.getTime();
13137 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13138 // Roo.log('using add remove');
13140 // this.fireEvent('monthchange', this, date);
13142 // this.cells.removeClass("fc-state-highlight");
13143 // this.cells.each(function(c){
13144 // if(c.dateValue == t){
13145 // c.addClass("fc-state-highlight");
13146 // setTimeout(function(){
13147 // try{c.dom.firstChild.focus();}catch(e){}
13157 var days = date.getDaysInMonth();
13159 var firstOfMonth = date.getFirstDateOfMonth();
13160 var startingPos = firstOfMonth.getDay()-this.startDay;
13162 if(startingPos < this.startDay){
13166 var pm = date.add(Date.MONTH, -1);
13167 var prevStart = pm.getDaysInMonth()-startingPos;
13169 this.cells = this.el.select('.fc-day',true);
13170 this.textNodes = this.el.query('.fc-day-number');
13171 this.cells.addClassOnOver('fc-state-hover');
13173 var cells = this.cells.elements;
13174 var textEls = this.textNodes;
13176 Roo.each(cells, function(cell){
13177 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13180 days += startingPos;
13182 // convert everything to numbers so it's fast
13183 var day = 86400000;
13184 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13187 //Roo.log(prevStart);
13189 var today = new Date().clearTime().getTime();
13190 var sel = date.clearTime().getTime();
13191 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13192 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13193 var ddMatch = this.disabledDatesRE;
13194 var ddText = this.disabledDatesText;
13195 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13196 var ddaysText = this.disabledDaysText;
13197 var format = this.format;
13199 var setCellClass = function(cal, cell){
13203 //Roo.log('set Cell Class');
13205 var t = d.getTime();
13209 cell.dateValue = t;
13211 cell.className += " fc-today";
13212 cell.className += " fc-state-highlight";
13213 cell.title = cal.todayText;
13216 // disable highlight in other month..
13217 //cell.className += " fc-state-highlight";
13222 cell.className = " fc-state-disabled";
13223 cell.title = cal.minText;
13227 cell.className = " fc-state-disabled";
13228 cell.title = cal.maxText;
13232 if(ddays.indexOf(d.getDay()) != -1){
13233 cell.title = ddaysText;
13234 cell.className = " fc-state-disabled";
13237 if(ddMatch && format){
13238 var fvalue = d.dateFormat(format);
13239 if(ddMatch.test(fvalue)){
13240 cell.title = ddText.replace("%0", fvalue);
13241 cell.className = " fc-state-disabled";
13245 if (!cell.initialClassName) {
13246 cell.initialClassName = cell.dom.className;
13249 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13254 for(; i < startingPos; i++) {
13255 textEls[i].innerHTML = (++prevStart);
13256 d.setDate(d.getDate()+1);
13258 cells[i].className = "fc-past fc-other-month";
13259 setCellClass(this, cells[i]);
13264 for(; i < days; i++){
13265 intDay = i - startingPos + 1;
13266 textEls[i].innerHTML = (intDay);
13267 d.setDate(d.getDate()+1);
13269 cells[i].className = ''; // "x-date-active";
13270 setCellClass(this, cells[i]);
13274 for(; i < 42; i++) {
13275 textEls[i].innerHTML = (++extraDays);
13276 d.setDate(d.getDate()+1);
13278 cells[i].className = "fc-future fc-other-month";
13279 setCellClass(this, cells[i]);
13282 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13284 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13286 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13287 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13289 if(totalRows != 6){
13290 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13291 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13294 this.fireEvent('monthchange', this, date);
13298 if(!this.internalRender){
13299 var main = this.el.dom.firstChild;
13300 var w = main.offsetWidth;
13301 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13302 Roo.fly(main).setWidth(w);
13303 this.internalRender = true;
13304 // opera does not respect the auto grow header center column
13305 // then, after it gets a width opera refuses to recalculate
13306 // without a second pass
13307 if(Roo.isOpera && !this.secondPass){
13308 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13309 this.secondPass = true;
13310 this.update.defer(10, this, [date]);
13317 findCell : function(dt) {
13318 dt = dt.clearTime().getTime();
13320 this.cells.each(function(c){
13321 //Roo.log("check " +c.dateValue + '?=' + dt);
13322 if(c.dateValue == dt){
13332 findCells : function(ev) {
13333 var s = ev.start.clone().clearTime().getTime();
13335 var e= ev.end.clone().clearTime().getTime();
13338 this.cells.each(function(c){
13339 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13341 if(c.dateValue > e){
13344 if(c.dateValue < s){
13353 // findBestRow: function(cells)
13357 // for (var i =0 ; i < cells.length;i++) {
13358 // ret = Math.max(cells[i].rows || 0,ret);
13365 addItem : function(ev)
13367 // look for vertical location slot in
13368 var cells = this.findCells(ev);
13370 // ev.row = this.findBestRow(cells);
13372 // work out the location.
13376 for(var i =0; i < cells.length; i++) {
13378 cells[i].row = cells[0].row;
13381 cells[i].row = cells[i].row + 1;
13391 if (crow.start.getY() == cells[i].getY()) {
13393 crow.end = cells[i];
13410 cells[0].events.push(ev);
13412 this.calevents.push(ev);
13415 clearEvents: function() {
13417 if(!this.calevents){
13421 Roo.each(this.cells.elements, function(c){
13427 Roo.each(this.calevents, function(e) {
13428 Roo.each(e.els, function(el) {
13429 el.un('mouseenter' ,this.onEventEnter, this);
13430 el.un('mouseleave' ,this.onEventLeave, this);
13435 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13441 renderEvents: function()
13445 this.cells.each(function(c) {
13454 if(c.row != c.events.length){
13455 r = 4 - (4 - (c.row - c.events.length));
13458 c.events = ev.slice(0, r);
13459 c.more = ev.slice(r);
13461 if(c.more.length && c.more.length == 1){
13462 c.events.push(c.more.pop());
13465 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13469 this.cells.each(function(c) {
13471 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13474 for (var e = 0; e < c.events.length; e++){
13475 var ev = c.events[e];
13476 var rows = ev.rows;
13478 for(var i = 0; i < rows.length; i++) {
13480 // how many rows should it span..
13483 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13484 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13486 unselectable : "on",
13489 cls: 'fc-event-inner',
13493 // cls: 'fc-event-time',
13494 // html : cells.length > 1 ? '' : ev.time
13498 cls: 'fc-event-title',
13499 html : String.format('{0}', ev.title)
13506 cls: 'ui-resizable-handle ui-resizable-e',
13507 html : '  '
13514 cfg.cls += ' fc-event-start';
13516 if ((i+1) == rows.length) {
13517 cfg.cls += ' fc-event-end';
13520 var ctr = _this.el.select('.fc-event-container',true).first();
13521 var cg = ctr.createChild(cfg);
13523 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13524 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13526 var r = (c.more.length) ? 1 : 0;
13527 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13528 cg.setWidth(ebox.right - sbox.x -2);
13530 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13531 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13532 cg.on('click', _this.onEventClick, _this, ev);
13543 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13544 style : 'position: absolute',
13545 unselectable : "on",
13548 cls: 'fc-event-inner',
13552 cls: 'fc-event-title',
13560 cls: 'ui-resizable-handle ui-resizable-e',
13561 html : '  '
13567 var ctr = _this.el.select('.fc-event-container',true).first();
13568 var cg = ctr.createChild(cfg);
13570 var sbox = c.select('.fc-day-content',true).first().getBox();
13571 var ebox = c.select('.fc-day-content',true).first().getBox();
13573 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13574 cg.setWidth(ebox.right - sbox.x -2);
13576 cg.on('click', _this.onMoreEventClick, _this, c.more);
13586 onEventEnter: function (e, el,event,d) {
13587 this.fireEvent('evententer', this, el, event);
13590 onEventLeave: function (e, el,event,d) {
13591 this.fireEvent('eventleave', this, el, event);
13594 onEventClick: function (e, el,event,d) {
13595 this.fireEvent('eventclick', this, el, event);
13598 onMonthChange: function () {
13602 onMoreEventClick: function(e, el, more)
13606 this.calpopover.placement = 'right';
13607 this.calpopover.setTitle('More');
13609 this.calpopover.setContent('');
13611 var ctr = this.calpopover.el.select('.popover-content', true).first();
13613 Roo.each(more, function(m){
13615 cls : 'fc-event-hori fc-event-draggable',
13618 var cg = ctr.createChild(cfg);
13620 cg.on('click', _this.onEventClick, _this, m);
13623 this.calpopover.show(el);
13628 onLoad: function ()
13630 this.calevents = [];
13633 if(this.store.getCount() > 0){
13634 this.store.data.each(function(d){
13637 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13638 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13639 time : d.data.start_time,
13640 title : d.data.title,
13641 description : d.data.description,
13642 venue : d.data.venue
13647 this.renderEvents();
13649 if(this.calevents.length && this.loadMask){
13650 this.maskEl.hide();
13654 onBeforeLoad: function()
13656 this.clearEvents();
13658 this.maskEl.show();
13672 * @class Roo.bootstrap.Popover
13673 * @extends Roo.bootstrap.Component
13674 * Bootstrap Popover class
13675 * @cfg {String} html contents of the popover (or false to use children..)
13676 * @cfg {String} title of popover (or false to hide)
13677 * @cfg {String} placement how it is placed
13678 * @cfg {String} trigger click || hover (or false to trigger manually)
13679 * @cfg {String} over what (parent or false to trigger manually.)
13680 * @cfg {Number} delay - delay before showing
13683 * Create a new Popover
13684 * @param {Object} config The config object
13687 Roo.bootstrap.Popover = function(config){
13688 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13691 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13693 title: 'Fill in a title',
13696 placement : 'right',
13697 trigger : 'hover', // hover
13703 can_build_overlaid : false,
13705 getChildContainer : function()
13707 return this.el.select('.popover-content',true).first();
13710 getAutoCreate : function(){
13711 Roo.log('make popover?');
13713 cls : 'popover roo-dynamic',
13714 style: 'display:block',
13720 cls : 'popover-inner',
13724 cls: 'popover-title',
13728 cls : 'popover-content',
13739 setTitle: function(str)
13741 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13743 setContent: function(str)
13745 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13747 // as it get's added to the bottom of the page.
13748 onRender : function(ct, position)
13750 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13752 var cfg = Roo.apply({}, this.getAutoCreate());
13756 cfg.cls += ' ' + this.cls;
13759 cfg.style = this.style;
13761 Roo.log("adding to ")
13762 this.el = Roo.get(document.body).createChild(cfg, position);
13768 initEvents : function()
13770 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13771 this.el.enableDisplayMode('block');
13773 if (this.over === false) {
13776 if (this.triggers === false) {
13779 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13780 var triggers = this.trigger ? this.trigger.split(' ') : [];
13781 Roo.each(triggers, function(trigger) {
13783 if (trigger == 'click') {
13784 on_el.on('click', this.toggle, this);
13785 } else if (trigger != 'manual') {
13786 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13787 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13789 on_el.on(eventIn ,this.enter, this);
13790 on_el.on(eventOut, this.leave, this);
13801 toggle : function () {
13802 this.hoverState == 'in' ? this.leave() : this.enter();
13805 enter : function () {
13808 clearTimeout(this.timeout);
13810 this.hoverState = 'in'
13812 if (!this.delay || !this.delay.show) {
13817 this.timeout = setTimeout(function () {
13818 if (_t.hoverState == 'in') {
13821 }, this.delay.show)
13823 leave : function() {
13824 clearTimeout(this.timeout);
13826 this.hoverState = 'out'
13828 if (!this.delay || !this.delay.hide) {
13833 this.timeout = setTimeout(function () {
13834 if (_t.hoverState == 'out') {
13837 }, this.delay.hide)
13840 show : function (on_el)
13843 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13846 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13847 if (this.html !== false) {
13848 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13850 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13851 if (!this.title.length) {
13852 this.el.select('.popover-title',true).hide();
13855 var placement = typeof this.placement == 'function' ?
13856 this.placement.call(this, this.el, on_el) :
13859 var autoToken = /\s?auto?\s?/i;
13860 var autoPlace = autoToken.test(placement);
13862 placement = placement.replace(autoToken, '') || 'top';
13866 //this.el.setXY([0,0]);
13868 this.el.dom.style.display='block';
13869 this.el.addClass(placement);
13871 //this.el.appendTo(on_el);
13873 var p = this.getPosition();
13874 var box = this.el.getBox();
13879 var align = Roo.bootstrap.Popover.alignment[placement]
13880 this.el.alignTo(on_el, align[0],align[1]);
13881 //var arrow = this.el.select('.arrow',true).first();
13882 //arrow.set(align[2],
13884 this.el.addClass('in');
13885 this.hoverState = null;
13887 if (this.el.hasClass('fade')) {
13894 this.el.setXY([0,0]);
13895 this.el.removeClass('in');
13902 Roo.bootstrap.Popover.alignment = {
13903 'left' : ['r-l', [-10,0], 'right'],
13904 'right' : ['l-r', [10,0], 'left'],
13905 'bottom' : ['t-b', [0,10], 'top'],
13906 'top' : [ 'b-t', [0,-10], 'bottom']
13917 * @class Roo.bootstrap.Progress
13918 * @extends Roo.bootstrap.Component
13919 * Bootstrap Progress class
13920 * @cfg {Boolean} striped striped of the progress bar
13921 * @cfg {Boolean} active animated of the progress bar
13925 * Create a new Progress
13926 * @param {Object} config The config object
13929 Roo.bootstrap.Progress = function(config){
13930 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13933 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13938 getAutoCreate : function(){
13946 cfg.cls += ' progress-striped';
13950 cfg.cls += ' active';
13969 * @class Roo.bootstrap.ProgressBar
13970 * @extends Roo.bootstrap.Component
13971 * Bootstrap ProgressBar class
13972 * @cfg {Number} aria_valuenow aria-value now
13973 * @cfg {Number} aria_valuemin aria-value min
13974 * @cfg {Number} aria_valuemax aria-value max
13975 * @cfg {String} label label for the progress bar
13976 * @cfg {String} panel (success | info | warning | danger )
13977 * @cfg {String} role role of the progress bar
13978 * @cfg {String} sr_only text
13982 * Create a new ProgressBar
13983 * @param {Object} config The config object
13986 Roo.bootstrap.ProgressBar = function(config){
13987 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13990 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
13994 aria_valuemax : 100,
14000 getAutoCreate : function()
14005 cls: 'progress-bar',
14006 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14018 cfg.role = this.role;
14021 if(this.aria_valuenow){
14022 cfg['aria-valuenow'] = this.aria_valuenow;
14025 if(this.aria_valuemin){
14026 cfg['aria-valuemin'] = this.aria_valuemin;
14029 if(this.aria_valuemax){
14030 cfg['aria-valuemax'] = this.aria_valuemax;
14033 if(this.label && !this.sr_only){
14034 cfg.html = this.label;
14038 cfg.cls += ' progress-bar-' + this.panel;
14044 update : function(aria_valuenow)
14046 this.aria_valuenow = aria_valuenow;
14048 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14063 * @class Roo.bootstrap.TabGroup
14064 * @extends Roo.bootstrap.Column
14065 * Bootstrap Column class
14066 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14067 * @cfg {Boolean} carousel true to make the group behave like a carousel
14070 * Create a new TabGroup
14071 * @param {Object} config The config object
14074 Roo.bootstrap.TabGroup = function(config){
14075 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14077 this.navId = Roo.id();
14080 Roo.bootstrap.TabGroup.register(this);
14084 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
14087 transition : false,
14089 getAutoCreate : function()
14091 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14093 cfg.cls += ' tab-content';
14095 if (this.carousel) {
14096 cfg.cls += ' carousel slide';
14098 cls : 'carousel-inner'
14105 getChildContainer : function()
14107 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14111 * register a Navigation item
14112 * @param {Roo.bootstrap.NavItem} the navitem to add
14114 register : function(item)
14116 this.tabs.push( item);
14117 item.navId = this.navId; // not really needed..
14121 getActivePanel : function()
14124 Roo.each(this.tabs, function(t) {
14134 getPanelByName : function(n)
14137 Roo.each(this.tabs, function(t) {
14138 if (t.tabId == n) {
14146 indexOfPanel : function(p)
14149 Roo.each(this.tabs, function(t,i) {
14150 if (t.tabId == p.tabId) {
14159 * show a specific panel
14160 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14161 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14163 showPanel : function (pan)
14166 if (typeof(pan) == 'number') {
14167 pan = this.tabs[pan];
14169 if (typeof(pan) == 'string') {
14170 pan = this.getPanelByName(pan);
14172 if (pan.tabId == this.getActivePanel().tabId) {
14175 var cur = this.getActivePanel();
14177 if (false === cur.fireEvent('beforedeactivate')) {
14181 if (this.carousel) {
14182 this.transition = true;
14183 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14184 var lr = dir == 'next' ? 'left' : 'right';
14185 pan.el.addClass(dir); // or prev
14186 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14187 cur.el.addClass(lr); // or right
14188 pan.el.addClass(lr);
14191 cur.el.on('transitionend', function() {
14192 Roo.log("trans end?");
14194 pan.el.removeClass([lr,dir]);
14195 pan.setActive(true);
14197 cur.el.removeClass([lr]);
14198 cur.setActive(false);
14200 _this.transition = false;
14202 }, this, { single: true } );
14206 cur.setActive(false);
14207 pan.setActive(true);
14211 showPanelNext : function()
14213 var i = this.indexOfPanel(this.getActivePanel());
14214 if (i > this.tabs.length) {
14217 this.showPanel(this.tabs[i+1]);
14219 showPanelPrev : function()
14221 var i = this.indexOfPanel(this.getActivePanel());
14225 this.showPanel(this.tabs[i-1]);
14236 Roo.apply(Roo.bootstrap.TabGroup, {
14240 * register a Navigation Group
14241 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14243 register : function(navgrp)
14245 this.groups[navgrp.navId] = navgrp;
14249 * fetch a Navigation Group based on the navigation ID
14250 * if one does not exist , it will get created.
14251 * @param {string} the navgroup to add
14252 * @returns {Roo.bootstrap.NavGroup} the navgroup
14254 get: function(navId) {
14255 if (typeof(this.groups[navId]) == 'undefined') {
14256 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14258 return this.groups[navId] ;
14273 * @class Roo.bootstrap.TabPanel
14274 * @extends Roo.bootstrap.Component
14275 * Bootstrap TabPanel class
14276 * @cfg {Boolean} active panel active
14277 * @cfg {String} html panel content
14278 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14279 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14283 * Create a new TabPanel
14284 * @param {Object} config The config object
14287 Roo.bootstrap.TabPanel = function(config){
14288 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14292 * Fires when the active status changes
14293 * @param {Roo.bootstrap.TabPanel} this
14294 * @param {Boolean} state the new state
14299 * @event beforedeactivate
14300 * Fires before a tab is de-activated - can be used to do validation on a form.
14301 * @param {Roo.bootstrap.TabPanel} this
14302 * @return {Boolean} false if there is an error
14305 'beforedeactivate': true
14308 this.tabId = this.tabId || Roo.id();
14312 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14319 getAutoCreate : function(){
14322 // item is needed for carousel - not sure if it has any effect otherwise
14323 cls: 'tab-pane item',
14324 html: this.html || ''
14328 cfg.cls += ' active';
14332 cfg.tabId = this.tabId;
14339 initEvents: function()
14341 Roo.log('-------- init events on tab panel ---------');
14343 var p = this.parent();
14344 this.navId = this.navId || p.navId;
14346 if (typeof(this.navId) != 'undefined') {
14347 // not really needed.. but just in case.. parent should be a NavGroup.
14348 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14349 Roo.log(['register', tg, this]);
14355 onRender : function(ct, position)
14357 // Roo.log("Call onRender: " + this.xtype);
14359 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14367 setActive: function(state)
14369 Roo.log("panel - set active " + this.tabId + "=" + state);
14371 this.active = state;
14373 this.el.removeClass('active');
14375 } else if (!this.el.hasClass('active')) {
14376 this.el.addClass('active');
14378 this.fireEvent('changed', this, state);
14395 * @class Roo.bootstrap.DateField
14396 * @extends Roo.bootstrap.Input
14397 * Bootstrap DateField class
14398 * @cfg {Number} weekStart default 0
14399 * @cfg {String} viewMode default empty, (months|years)
14400 * @cfg {String} minViewMode default empty, (months|years)
14401 * @cfg {Number} startDate default -Infinity
14402 * @cfg {Number} endDate default Infinity
14403 * @cfg {Boolean} todayHighlight default false
14404 * @cfg {Boolean} todayBtn default false
14405 * @cfg {Boolean} calendarWeeks default false
14406 * @cfg {Object} daysOfWeekDisabled default empty
14407 * @cfg {Boolean} singleMode default false (true | false)
14409 * @cfg {Boolean} keyboardNavigation default true
14410 * @cfg {String} language default en
14413 * Create a new DateField
14414 * @param {Object} config The config object
14417 Roo.bootstrap.DateField = function(config){
14418 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14422 * Fires when this field show.
14423 * @param {Roo.bootstrap.DateField} this
14424 * @param {Mixed} date The date value
14429 * Fires when this field hide.
14430 * @param {Roo.bootstrap.DateField} this
14431 * @param {Mixed} date The date value
14436 * Fires when select a date.
14437 * @param {Roo.bootstrap.DateField} this
14438 * @param {Mixed} date The date value
14444 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14447 * @cfg {String} format
14448 * The default date format string which can be overriden for localization support. The format must be
14449 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14453 * @cfg {String} altFormats
14454 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14455 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14457 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14465 todayHighlight : false,
14471 keyboardNavigation: true,
14473 calendarWeeks: false,
14475 startDate: -Infinity,
14479 daysOfWeekDisabled: [],
14483 singleMode : false,
14485 UTCDate: function()
14487 return new Date(Date.UTC.apply(Date, arguments));
14490 UTCToday: function()
14492 var today = new Date();
14493 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14496 getDate: function() {
14497 var d = this.getUTCDate();
14498 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14501 getUTCDate: function() {
14505 setDate: function(d) {
14506 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14509 setUTCDate: function(d) {
14511 this.setValue(this.formatDate(this.date));
14514 onRender: function(ct, position)
14517 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14519 this.language = this.language || 'en';
14520 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14521 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14523 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14524 this.format = this.format || 'm/d/y';
14525 this.isInline = false;
14526 this.isInput = true;
14527 this.component = this.el.select('.add-on', true).first() || false;
14528 this.component = (this.component && this.component.length === 0) ? false : this.component;
14529 this.hasInput = this.component && this.inputEL().length;
14531 if (typeof(this.minViewMode === 'string')) {
14532 switch (this.minViewMode) {
14534 this.minViewMode = 1;
14537 this.minViewMode = 2;
14540 this.minViewMode = 0;
14545 if (typeof(this.viewMode === 'string')) {
14546 switch (this.viewMode) {
14559 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14561 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14563 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14565 this.picker().on('mousedown', this.onMousedown, this);
14566 this.picker().on('click', this.onClick, this);
14568 this.picker().addClass('datepicker-dropdown');
14570 this.startViewMode = this.viewMode;
14572 if(this.singleMode){
14573 Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
14574 v.setVisibilityMode(Roo.Element.DISPLAY)
14578 Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
14579 v.setStyle('width', '189px');
14583 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14584 if(!this.calendarWeeks){
14589 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14590 v.attr('colspan', function(i, val){
14591 return parseInt(val) + 1;
14596 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14598 this.setStartDate(this.startDate);
14599 this.setEndDate(this.endDate);
14601 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14608 if(this.isInline) {
14613 picker : function()
14615 return this.pickerEl;
14616 // return this.el.select('.datepicker', true).first();
14619 fillDow: function()
14621 var dowCnt = this.weekStart;
14630 if(this.calendarWeeks){
14638 while (dowCnt < this.weekStart + 7) {
14642 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14646 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14649 fillMonths: function()
14652 var months = this.picker().select('>.datepicker-months td', true).first();
14654 months.dom.innerHTML = '';
14660 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14663 months.createChild(month);
14670 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;
14672 if (this.date < this.startDate) {
14673 this.viewDate = new Date(this.startDate);
14674 } else if (this.date > this.endDate) {
14675 this.viewDate = new Date(this.endDate);
14677 this.viewDate = new Date(this.date);
14685 var d = new Date(this.viewDate),
14686 year = d.getUTCFullYear(),
14687 month = d.getUTCMonth(),
14688 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14689 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14690 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14691 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14692 currentDate = this.date && this.date.valueOf(),
14693 today = this.UTCToday();
14695 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14697 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14699 // this.picker.select('>tfoot th.today').
14700 // .text(dates[this.language].today)
14701 // .toggle(this.todayBtn !== false);
14703 this.updateNavArrows();
14706 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14708 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14710 prevMonth.setUTCDate(day);
14712 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14714 var nextMonth = new Date(prevMonth);
14716 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14718 nextMonth = nextMonth.valueOf();
14720 var fillMonths = false;
14722 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14724 while(prevMonth.valueOf() < nextMonth) {
14727 if (prevMonth.getUTCDay() === this.weekStart) {
14729 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14737 if(this.calendarWeeks){
14738 // ISO 8601: First week contains first thursday.
14739 // ISO also states week starts on Monday, but we can be more abstract here.
14741 // Start of current week: based on weekstart/current date
14742 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14743 // Thursday of this week
14744 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14745 // First Thursday of year, year from thursday
14746 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14747 // Calendar week: ms between thursdays, div ms per day, div 7 days
14748 calWeek = (th - yth) / 864e5 / 7 + 1;
14750 fillMonths.cn.push({
14758 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14760 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14763 if (this.todayHighlight &&
14764 prevMonth.getUTCFullYear() == today.getFullYear() &&
14765 prevMonth.getUTCMonth() == today.getMonth() &&
14766 prevMonth.getUTCDate() == today.getDate()) {
14767 clsName += ' today';
14770 if (currentDate && prevMonth.valueOf() === currentDate) {
14771 clsName += ' active';
14774 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14775 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14776 clsName += ' disabled';
14779 fillMonths.cn.push({
14781 cls: 'day ' + clsName,
14782 html: prevMonth.getDate()
14785 prevMonth.setDate(prevMonth.getDate()+1);
14788 var currentYear = this.date && this.date.getUTCFullYear();
14789 var currentMonth = this.date && this.date.getUTCMonth();
14791 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14793 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14794 v.removeClass('active');
14796 if(currentYear === year && k === currentMonth){
14797 v.addClass('active');
14800 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14801 v.addClass('disabled');
14807 year = parseInt(year/10, 10) * 10;
14809 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14811 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14814 for (var i = -1; i < 11; i++) {
14815 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14817 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14825 showMode: function(dir)
14828 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14831 Roo.each(this.picker().select('>div',true).elements, function(v){
14832 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14835 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14840 if(this.isInline) return;
14842 this.picker().removeClass(['bottom', 'top']);
14844 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14846 * place to the top of element!
14850 this.picker().addClass('top');
14851 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14856 this.picker().addClass('bottom');
14858 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14861 parseDate : function(value)
14863 if(!value || value instanceof Date){
14866 var v = Date.parseDate(value, this.format);
14867 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14868 v = Date.parseDate(value, 'Y-m-d');
14870 if(!v && this.altFormats){
14871 if(!this.altFormatsArray){
14872 this.altFormatsArray = this.altFormats.split("|");
14874 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14875 v = Date.parseDate(value, this.altFormatsArray[i]);
14881 formatDate : function(date, fmt)
14883 return (!date || !(date instanceof Date)) ?
14884 date : date.dateFormat(fmt || this.format);
14887 onFocus : function()
14889 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14893 onBlur : function()
14895 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14897 var d = this.inputEl().getValue();
14906 this.picker().show();
14910 this.fireEvent('show', this, this.date);
14915 if(this.isInline) return;
14916 this.picker().hide();
14917 this.viewMode = this.startViewMode;
14920 this.fireEvent('hide', this, this.date);
14924 onMousedown: function(e)
14926 e.stopPropagation();
14927 e.preventDefault();
14932 Roo.bootstrap.DateField.superclass.keyup.call(this);
14936 setValue: function(v)
14939 // v can be a string or a date..
14942 var d = new Date(this.parseDate(v) ).clearTime();
14944 if(isNaN(d.getTime())){
14945 this.date = this.viewDate = '';
14946 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14950 v = this.formatDate(d);
14952 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14954 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14958 this.fireEvent('select', this, this.date);
14962 getValue: function()
14964 return this.formatDate(this.date);
14967 fireKey: function(e)
14969 if (!this.picker().isVisible()){
14970 if (e.keyCode == 27) // allow escape to hide and re-show picker
14975 var dateChanged = false,
14977 newDate, newViewDate;
14982 e.preventDefault();
14986 if (!this.keyboardNavigation) break;
14987 dir = e.keyCode == 37 ? -1 : 1;
14990 newDate = this.moveYear(this.date, dir);
14991 newViewDate = this.moveYear(this.viewDate, dir);
14992 } else if (e.shiftKey){
14993 newDate = this.moveMonth(this.date, dir);
14994 newViewDate = this.moveMonth(this.viewDate, dir);
14996 newDate = new Date(this.date);
14997 newDate.setUTCDate(this.date.getUTCDate() + dir);
14998 newViewDate = new Date(this.viewDate);
14999 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
15001 if (this.dateWithinRange(newDate)){
15002 this.date = newDate;
15003 this.viewDate = newViewDate;
15004 this.setValue(this.formatDate(this.date));
15006 e.preventDefault();
15007 dateChanged = true;
15012 if (!this.keyboardNavigation) break;
15013 dir = e.keyCode == 38 ? -1 : 1;
15015 newDate = this.moveYear(this.date, dir);
15016 newViewDate = this.moveYear(this.viewDate, dir);
15017 } else if (e.shiftKey){
15018 newDate = this.moveMonth(this.date, dir);
15019 newViewDate = this.moveMonth(this.viewDate, dir);
15021 newDate = new Date(this.date);
15022 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15023 newViewDate = new Date(this.viewDate);
15024 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15026 if (this.dateWithinRange(newDate)){
15027 this.date = newDate;
15028 this.viewDate = newViewDate;
15029 this.setValue(this.formatDate(this.date));
15031 e.preventDefault();
15032 dateChanged = true;
15036 this.setValue(this.formatDate(this.date));
15038 e.preventDefault();
15041 this.setValue(this.formatDate(this.date));
15055 onClick: function(e)
15057 e.stopPropagation();
15058 e.preventDefault();
15060 var target = e.getTarget();
15062 if(target.nodeName.toLowerCase() === 'i'){
15063 target = Roo.get(target).dom.parentNode;
15066 var nodeName = target.nodeName;
15067 var className = target.className;
15068 var html = target.innerHTML;
15069 //Roo.log(nodeName);
15071 switch(nodeName.toLowerCase()) {
15073 switch(className) {
15079 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15080 switch(this.viewMode){
15082 this.viewDate = this.moveMonth(this.viewDate, dir);
15086 this.viewDate = this.moveYear(this.viewDate, dir);
15092 var date = new Date();
15093 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15095 this.setValue(this.formatDate(this.date));
15102 if (className.indexOf('disabled') < 0) {
15103 this.viewDate.setUTCDate(1);
15104 if (className.indexOf('month') > -1) {
15105 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15107 var year = parseInt(html, 10) || 0;
15108 this.viewDate.setUTCFullYear(year);
15112 if(this.singleMode){
15113 this.setValue(this.formatDate(this.viewDate));
15124 //Roo.log(className);
15125 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15126 var day = parseInt(html, 10) || 1;
15127 var year = this.viewDate.getUTCFullYear(),
15128 month = this.viewDate.getUTCMonth();
15130 if (className.indexOf('old') > -1) {
15137 } else if (className.indexOf('new') > -1) {
15145 //Roo.log([year,month,day]);
15146 this.date = this.UTCDate(year, month, day,0,0,0,0);
15147 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15149 //Roo.log(this.formatDate(this.date));
15150 this.setValue(this.formatDate(this.date));
15157 setStartDate: function(startDate)
15159 this.startDate = startDate || -Infinity;
15160 if (this.startDate !== -Infinity) {
15161 this.startDate = this.parseDate(this.startDate);
15164 this.updateNavArrows();
15167 setEndDate: function(endDate)
15169 this.endDate = endDate || Infinity;
15170 if (this.endDate !== Infinity) {
15171 this.endDate = this.parseDate(this.endDate);
15174 this.updateNavArrows();
15177 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15179 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15180 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15181 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15183 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15184 return parseInt(d, 10);
15187 this.updateNavArrows();
15190 updateNavArrows: function()
15192 if(this.singleMode){
15196 var d = new Date(this.viewDate),
15197 year = d.getUTCFullYear(),
15198 month = d.getUTCMonth();
15200 Roo.each(this.picker().select('.prev', true).elements, function(v){
15202 switch (this.viewMode) {
15205 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15211 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15218 Roo.each(this.picker().select('.next', true).elements, function(v){
15220 switch (this.viewMode) {
15223 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15229 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15237 moveMonth: function(date, dir)
15239 if (!dir) return date;
15240 var new_date = new Date(date.valueOf()),
15241 day = new_date.getUTCDate(),
15242 month = new_date.getUTCMonth(),
15243 mag = Math.abs(dir),
15245 dir = dir > 0 ? 1 : -1;
15248 // If going back one month, make sure month is not current month
15249 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15251 return new_date.getUTCMonth() == month;
15253 // If going forward one month, make sure month is as expected
15254 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15256 return new_date.getUTCMonth() != new_month;
15258 new_month = month + dir;
15259 new_date.setUTCMonth(new_month);
15260 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15261 if (new_month < 0 || new_month > 11)
15262 new_month = (new_month + 12) % 12;
15264 // For magnitudes >1, move one month at a time...
15265 for (var i=0; i<mag; i++)
15266 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15267 new_date = this.moveMonth(new_date, dir);
15268 // ...then reset the day, keeping it in the new month
15269 new_month = new_date.getUTCMonth();
15270 new_date.setUTCDate(day);
15272 return new_month != new_date.getUTCMonth();
15275 // Common date-resetting loop -- if date is beyond end of month, make it
15278 new_date.setUTCDate(--day);
15279 new_date.setUTCMonth(new_month);
15284 moveYear: function(date, dir)
15286 return this.moveMonth(date, dir*12);
15289 dateWithinRange: function(date)
15291 return date >= this.startDate && date <= this.endDate;
15297 this.picker().remove();
15302 Roo.apply(Roo.bootstrap.DateField, {
15313 html: '<i class="fa fa-arrow-left"/>'
15323 html: '<i class="fa fa-arrow-right"/>'
15365 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15366 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15367 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15368 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15369 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15382 navFnc: 'FullYear',
15387 navFnc: 'FullYear',
15392 Roo.apply(Roo.bootstrap.DateField, {
15396 cls: 'datepicker dropdown-menu',
15400 cls: 'datepicker-days',
15404 cls: 'table-condensed',
15406 Roo.bootstrap.DateField.head,
15410 Roo.bootstrap.DateField.footer
15417 cls: 'datepicker-months',
15421 cls: 'table-condensed',
15423 Roo.bootstrap.DateField.head,
15424 Roo.bootstrap.DateField.content,
15425 Roo.bootstrap.DateField.footer
15432 cls: 'datepicker-years',
15436 cls: 'table-condensed',
15438 Roo.bootstrap.DateField.head,
15439 Roo.bootstrap.DateField.content,
15440 Roo.bootstrap.DateField.footer
15459 * @class Roo.bootstrap.TimeField
15460 * @extends Roo.bootstrap.Input
15461 * Bootstrap DateField class
15465 * Create a new TimeField
15466 * @param {Object} config The config object
15469 Roo.bootstrap.TimeField = function(config){
15470 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15474 * Fires when this field show.
15475 * @param {Roo.bootstrap.DateField} this
15476 * @param {Mixed} date The date value
15481 * Fires when this field hide.
15482 * @param {Roo.bootstrap.DateField} this
15483 * @param {Mixed} date The date value
15488 * Fires when select a date.
15489 * @param {Roo.bootstrap.DateField} this
15490 * @param {Mixed} date The date value
15496 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15499 * @cfg {String} format
15500 * The default time format string which can be overriden for localization support. The format must be
15501 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15505 onRender: function(ct, position)
15508 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15510 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15512 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15514 this.pop = this.picker().select('>.datepicker-time',true).first();
15515 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15517 this.picker().on('mousedown', this.onMousedown, this);
15518 this.picker().on('click', this.onClick, this);
15520 this.picker().addClass('datepicker-dropdown');
15525 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15526 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15527 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15528 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15529 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15530 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15534 fireKey: function(e){
15535 if (!this.picker().isVisible()){
15536 if (e.keyCode == 27) // allow escape to hide and re-show picker
15541 e.preventDefault();
15549 this.onTogglePeriod();
15552 this.onIncrementMinutes();
15555 this.onDecrementMinutes();
15564 onClick: function(e) {
15565 e.stopPropagation();
15566 e.preventDefault();
15569 picker : function()
15571 return this.el.select('.datepicker', true).first();
15574 fillTime: function()
15576 var time = this.pop.select('tbody', true).first();
15578 time.dom.innerHTML = '';
15593 cls: 'hours-up glyphicon glyphicon-chevron-up'
15613 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15634 cls: 'timepicker-hour',
15649 cls: 'timepicker-minute',
15664 cls: 'btn btn-primary period',
15686 cls: 'hours-down glyphicon glyphicon-chevron-down'
15706 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15724 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15731 var hours = this.time.getHours();
15732 var minutes = this.time.getMinutes();
15745 hours = hours - 12;
15749 hours = '0' + hours;
15753 minutes = '0' + minutes;
15756 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15757 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15758 this.pop.select('button', true).first().dom.innerHTML = period;
15764 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15766 var cls = ['bottom'];
15768 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15775 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15780 this.picker().addClass(cls.join('-'));
15784 Roo.each(cls, function(c){
15786 _this.picker().setTop(_this.inputEl().getHeight());
15790 _this.picker().setTop(0 - _this.picker().getHeight());
15795 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15799 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15806 onFocus : function()
15808 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15812 onBlur : function()
15814 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15820 this.picker().show();
15825 this.fireEvent('show', this, this.date);
15830 this.picker().hide();
15833 this.fireEvent('hide', this, this.date);
15836 setTime : function()
15839 this.setValue(this.time.format(this.format));
15841 this.fireEvent('select', this, this.date);
15846 onMousedown: function(e){
15847 e.stopPropagation();
15848 e.preventDefault();
15851 onIncrementHours: function()
15853 Roo.log('onIncrementHours');
15854 this.time = this.time.add(Date.HOUR, 1);
15859 onDecrementHours: function()
15861 Roo.log('onDecrementHours');
15862 this.time = this.time.add(Date.HOUR, -1);
15866 onIncrementMinutes: function()
15868 Roo.log('onIncrementMinutes');
15869 this.time = this.time.add(Date.MINUTE, 1);
15873 onDecrementMinutes: function()
15875 Roo.log('onDecrementMinutes');
15876 this.time = this.time.add(Date.MINUTE, -1);
15880 onTogglePeriod: function()
15882 Roo.log('onTogglePeriod');
15883 this.time = this.time.add(Date.HOUR, 12);
15890 Roo.apply(Roo.bootstrap.TimeField, {
15920 cls: 'btn btn-info ok',
15932 Roo.apply(Roo.bootstrap.TimeField, {
15936 cls: 'datepicker dropdown-menu',
15940 cls: 'datepicker-time',
15944 cls: 'table-condensed',
15946 Roo.bootstrap.TimeField.content,
15947 Roo.bootstrap.TimeField.footer
15966 * @class Roo.bootstrap.CheckBox
15967 * @extends Roo.bootstrap.Input
15968 * Bootstrap CheckBox class
15970 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15971 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15972 * @cfg {String} boxLabel The text that appears beside the checkbox
15973 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15974 * @cfg {Boolean} checked initnal the element
15978 * Create a new CheckBox
15979 * @param {Object} config The config object
15982 Roo.bootstrap.CheckBox = function(config){
15983 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15988 * Fires when the element is checked or unchecked.
15989 * @param {Roo.bootstrap.CheckBox} this This input
15990 * @param {Boolean} checked The new checked value
15996 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
15998 inputType: 'checkbox',
16005 getAutoCreate : function()
16007 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16013 cfg.cls = 'form-group checkbox' //input-group
16021 type : this.inputType,
16022 value : (!this.checked) ? this.valueOff : this.inputValue,
16023 cls : 'roo-checkbox', //'form-box',
16024 placeholder : this.placeholder || ''
16028 if (this.weight) { // Validity check?
16029 cfg.cls += " checkbox-" + this.weight;
16032 if (this.disabled) {
16033 input.disabled=true;
16037 input.checked = this.checked;
16041 input.name = this.name;
16045 input.cls += ' input-' + this.size;
16049 ['xs','sm','md','lg'].map(function(size){
16050 if (settings[size]) {
16051 cfg.cls += ' col-' + size + '-' + settings[size];
16057 var inputblock = input;
16062 if (this.before || this.after) {
16065 cls : 'input-group',
16069 inputblock.cn.push({
16071 cls : 'input-group-addon',
16075 inputblock.cn.push(input);
16077 inputblock.cn.push({
16079 cls : 'input-group-addon',
16086 if (align ==='left' && this.fieldLabel.length) {
16087 Roo.log("left and has label");
16093 cls : 'control-label col-md-' + this.labelWidth,
16094 html : this.fieldLabel
16098 cls : "col-md-" + (12 - this.labelWidth),
16105 } else if ( this.fieldLabel.length) {
16110 tag: this.boxLabel ? 'span' : 'label',
16112 cls: 'control-label box-input-label',
16113 //cls : 'input-group-addon',
16114 html : this.fieldLabel
16124 Roo.log(" no label && no align");
16125 cfg.cn = [ inputblock ] ;
16134 html: this.boxLabel
16146 * return the real input element.
16148 inputEl: function ()
16150 return this.el.select('input.roo-checkbox',true).first();
16155 return this.el.select('label.control-label',true).first();
16158 initEvents : function()
16160 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16162 this.inputEl().on('click', this.onClick, this);
16166 onClick : function()
16168 this.setChecked(!this.checked);
16171 setChecked : function(state,suppressEvent)
16173 this.checked = state;
16175 this.inputEl().dom.checked = state;
16177 if(suppressEvent !== true){
16178 this.fireEvent('check', this, state);
16181 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16185 setValue : function(v,suppressEvent)
16187 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16201 * @class Roo.bootstrap.Radio
16202 * @extends Roo.bootstrap.CheckBox
16203 * Bootstrap Radio class
16206 * Create a new Radio
16207 * @param {Object} config The config object
16210 Roo.bootstrap.Radio = function(config){
16211 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16215 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16217 inputType: 'radio',
16221 getAutoCreate : function()
16223 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16229 cfg.cls = 'form-group radio' //input-group
16234 type : this.inputType,
16235 value : (!this.checked) ? this.valueOff : this.inputValue,
16237 placeholder : this.placeholder || ''
16240 if (this.weight) { // Validity check?
16241 cfg.cls += " radio-" + this.weight;
16243 if (this.disabled) {
16244 input.disabled=true;
16248 input.checked = this.checked;
16252 input.name = this.name;
16256 input.cls += ' input-' + this.size;
16260 ['xs','sm','md','lg'].map(function(size){
16261 if (settings[size]) {
16262 cfg.cls += ' col-' + size + '-' + settings[size];
16266 var inputblock = input;
16268 if (this.before || this.after) {
16271 cls : 'input-group',
16275 inputblock.cn.push({
16277 cls : 'input-group-addon',
16281 inputblock.cn.push(input);
16283 inputblock.cn.push({
16285 cls : 'input-group-addon',
16292 if (align ==='left' && this.fieldLabel.length) {
16293 Roo.log("left and has label");
16299 cls : 'control-label col-md-' + this.labelWidth,
16300 html : this.fieldLabel
16304 cls : "col-md-" + (12 - this.labelWidth),
16311 } else if ( this.fieldLabel.length) {
16318 cls: 'control-label box-input-label',
16319 //cls : 'input-group-addon',
16320 html : this.fieldLabel
16330 Roo.log(" no label && no align");
16345 html: this.boxLabel
16352 inputEl: function ()
16354 return this.el.select('input.roo-radio',true).first();
16356 onClick : function()
16358 this.setChecked(true);
16361 setChecked : function(state,suppressEvent)
16364 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16365 v.dom.checked = false;
16369 this.checked = state;
16370 this.inputEl().dom.checked = state;
16372 if(suppressEvent !== true){
16373 this.fireEvent('check', this, state);
16376 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16380 getGroupValue : function()
16383 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16384 if(v.dom.checked == true){
16385 value = v.dom.value;
16393 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16394 * @return {Mixed} value The field value
16396 getValue : function(){
16397 return this.getGroupValue();
16403 //<script type="text/javascript">
16406 * Based Ext JS Library 1.1.1
16407 * Copyright(c) 2006-2007, Ext JS, LLC.
16413 * @class Roo.HtmlEditorCore
16414 * @extends Roo.Component
16415 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16417 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16420 Roo.HtmlEditorCore = function(config){
16423 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16428 * @event initialize
16429 * Fires when the editor is fully initialized (including the iframe)
16430 * @param {Roo.HtmlEditorCore} this
16435 * Fires when the editor is first receives the focus. Any insertion must wait
16436 * until after this event.
16437 * @param {Roo.HtmlEditorCore} this
16441 * @event beforesync
16442 * Fires before the textarea is updated with content from the editor iframe. Return false
16443 * to cancel the sync.
16444 * @param {Roo.HtmlEditorCore} this
16445 * @param {String} html
16449 * @event beforepush
16450 * Fires before the iframe editor is updated with content from the textarea. Return false
16451 * to cancel the push.
16452 * @param {Roo.HtmlEditorCore} this
16453 * @param {String} html
16458 * Fires when the textarea is updated with content from the editor iframe.
16459 * @param {Roo.HtmlEditorCore} this
16460 * @param {String} html
16465 * Fires when the iframe editor is updated with content from the textarea.
16466 * @param {Roo.HtmlEditorCore} this
16467 * @param {String} html
16472 * @event editorevent
16473 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16474 * @param {Roo.HtmlEditorCore} this
16479 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16481 // defaults : white / black...
16482 this.applyBlacklists();
16489 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16493 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16499 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16504 * @cfg {Number} height (in pixels)
16508 * @cfg {Number} width (in pixels)
16513 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16516 stylesheets: false,
16521 // private properties
16522 validationEvent : false,
16524 initialized : false,
16526 sourceEditMode : false,
16527 onFocus : Roo.emptyFn,
16529 hideMode:'offsets',
16533 // blacklist + whitelisted elements..
16540 * Protected method that will not generally be called directly. It
16541 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16542 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16544 getDocMarkup : function(){
16547 Roo.log(this.stylesheets);
16549 // inherit styels from page...??
16550 if (this.stylesheets === false) {
16552 Roo.get(document.head).select('style').each(function(node) {
16553 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16556 Roo.get(document.head).select('link').each(function(node) {
16557 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16560 } else if (!this.stylesheets.length) {
16562 st = '<style type="text/css">' +
16563 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16566 Roo.each(this.stylesheets, function(s) {
16567 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16572 st += '<style type="text/css">' +
16573 'IMG { cursor: pointer } ' +
16577 return '<html><head>' + st +
16578 //<style type="text/css">' +
16579 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16581 ' </head><body class="roo-htmleditor-body"></body></html>';
16585 onRender : function(ct, position)
16588 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16589 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16592 this.el.dom.style.border = '0 none';
16593 this.el.dom.setAttribute('tabIndex', -1);
16594 this.el.addClass('x-hidden hide');
16598 if(Roo.isIE){ // fix IE 1px bogus margin
16599 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16603 this.frameId = Roo.id();
16607 var iframe = this.owner.wrap.createChild({
16609 cls: 'form-control', // bootstrap..
16611 name: this.frameId,
16612 frameBorder : 'no',
16613 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16618 this.iframe = iframe.dom;
16620 this.assignDocWin();
16622 this.doc.designMode = 'on';
16625 this.doc.write(this.getDocMarkup());
16629 var task = { // must defer to wait for browser to be ready
16631 //console.log("run task?" + this.doc.readyState);
16632 this.assignDocWin();
16633 if(this.doc.body || this.doc.readyState == 'complete'){
16635 this.doc.designMode="on";
16639 Roo.TaskMgr.stop(task);
16640 this.initEditor.defer(10, this);
16647 Roo.TaskMgr.start(task);
16654 onResize : function(w, h)
16656 Roo.log('resize: ' +w + ',' + h );
16657 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16661 if(typeof w == 'number'){
16663 this.iframe.style.width = w + 'px';
16665 if(typeof h == 'number'){
16667 this.iframe.style.height = h + 'px';
16669 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16676 * Toggles the editor between standard and source edit mode.
16677 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16679 toggleSourceEdit : function(sourceEditMode){
16681 this.sourceEditMode = sourceEditMode === true;
16683 if(this.sourceEditMode){
16685 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16688 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16689 //this.iframe.className = '';
16692 //this.setSize(this.owner.wrap.getSize());
16693 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16700 * Protected method that will not generally be called directly. If you need/want
16701 * custom HTML cleanup, this is the method you should override.
16702 * @param {String} html The HTML to be cleaned
16703 * return {String} The cleaned HTML
16705 cleanHtml : function(html){
16706 html = String(html);
16707 if(html.length > 5){
16708 if(Roo.isSafari){ // strip safari nonsense
16709 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16712 if(html == ' '){
16719 * HTML Editor -> Textarea
16720 * Protected method that will not generally be called directly. Syncs the contents
16721 * of the editor iframe with the textarea.
16723 syncValue : function(){
16724 if(this.initialized){
16725 var bd = (this.doc.body || this.doc.documentElement);
16726 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16727 var html = bd.innerHTML;
16729 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16730 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16732 html = '<div style="'+m[0]+'">' + html + '</div>';
16735 html = this.cleanHtml(html);
16736 // fix up the special chars.. normaly like back quotes in word...
16737 // however we do not want to do this with chinese..
16738 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16739 var cc = b.charCodeAt();
16741 (cc >= 0x4E00 && cc < 0xA000 ) ||
16742 (cc >= 0x3400 && cc < 0x4E00 ) ||
16743 (cc >= 0xf900 && cc < 0xfb00 )
16749 if(this.owner.fireEvent('beforesync', this, html) !== false){
16750 this.el.dom.value = html;
16751 this.owner.fireEvent('sync', this, html);
16757 * Protected method that will not generally be called directly. Pushes the value of the textarea
16758 * into the iframe editor.
16760 pushValue : function(){
16761 if(this.initialized){
16762 var v = this.el.dom.value.trim();
16764 // if(v.length < 1){
16768 if(this.owner.fireEvent('beforepush', this, v) !== false){
16769 var d = (this.doc.body || this.doc.documentElement);
16771 this.cleanUpPaste();
16772 this.el.dom.value = d.innerHTML;
16773 this.owner.fireEvent('push', this, v);
16779 deferFocus : function(){
16780 this.focus.defer(10, this);
16784 focus : function(){
16785 if(this.win && !this.sourceEditMode){
16792 assignDocWin: function()
16794 var iframe = this.iframe;
16797 this.doc = iframe.contentWindow.document;
16798 this.win = iframe.contentWindow;
16800 // if (!Roo.get(this.frameId)) {
16803 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16804 // this.win = Roo.get(this.frameId).dom.contentWindow;
16806 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16810 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16811 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16816 initEditor : function(){
16817 //console.log("INIT EDITOR");
16818 this.assignDocWin();
16822 this.doc.designMode="on";
16824 this.doc.write(this.getDocMarkup());
16827 var dbody = (this.doc.body || this.doc.documentElement);
16828 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16829 // this copies styles from the containing element into thsi one..
16830 // not sure why we need all of this..
16831 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16833 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16834 //ss['background-attachment'] = 'fixed'; // w3c
16835 dbody.bgProperties = 'fixed'; // ie
16836 //Roo.DomHelper.applyStyles(dbody, ss);
16837 Roo.EventManager.on(this.doc, {
16838 //'mousedown': this.onEditorEvent,
16839 'mouseup': this.onEditorEvent,
16840 'dblclick': this.onEditorEvent,
16841 'click': this.onEditorEvent,
16842 'keyup': this.onEditorEvent,
16847 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16849 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16850 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16852 this.initialized = true;
16854 this.owner.fireEvent('initialize', this);
16859 onDestroy : function(){
16865 //for (var i =0; i < this.toolbars.length;i++) {
16866 // // fixme - ask toolbars for heights?
16867 // this.toolbars[i].onDestroy();
16870 //this.wrap.dom.innerHTML = '';
16871 //this.wrap.remove();
16876 onFirstFocus : function(){
16878 this.assignDocWin();
16881 this.activated = true;
16884 if(Roo.isGecko){ // prevent silly gecko errors
16886 var s = this.win.getSelection();
16887 if(!s.focusNode || s.focusNode.nodeType != 3){
16888 var r = s.getRangeAt(0);
16889 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16894 this.execCmd('useCSS', true);
16895 this.execCmd('styleWithCSS', false);
16898 this.owner.fireEvent('activate', this);
16902 adjustFont: function(btn){
16903 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16904 //if(Roo.isSafari){ // safari
16907 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16908 if(Roo.isSafari){ // safari
16909 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16910 v = (v < 10) ? 10 : v;
16911 v = (v > 48) ? 48 : v;
16912 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16917 v = Math.max(1, v+adjust);
16919 this.execCmd('FontSize', v );
16922 onEditorEvent : function(e){
16923 this.owner.fireEvent('editorevent', this, e);
16924 // this.updateToolbar();
16925 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16928 insertTag : function(tg)
16930 // could be a bit smarter... -> wrap the current selected tRoo..
16931 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16933 range = this.createRange(this.getSelection());
16934 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16935 wrappingNode.appendChild(range.extractContents());
16936 range.insertNode(wrappingNode);
16943 this.execCmd("formatblock", tg);
16947 insertText : function(txt)
16951 var range = this.createRange();
16952 range.deleteContents();
16953 //alert(Sender.getAttribute('label'));
16955 range.insertNode(this.doc.createTextNode(txt));
16961 * Executes a Midas editor command on the editor document and performs necessary focus and
16962 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16963 * @param {String} cmd The Midas command
16964 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16966 relayCmd : function(cmd, value){
16968 this.execCmd(cmd, value);
16969 this.owner.fireEvent('editorevent', this);
16970 //this.updateToolbar();
16971 this.owner.deferFocus();
16975 * Executes a Midas editor command directly on the editor document.
16976 * For visual commands, you should use {@link #relayCmd} instead.
16977 * <b>This should only be called after the editor is initialized.</b>
16978 * @param {String} cmd The Midas command
16979 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16981 execCmd : function(cmd, value){
16982 this.doc.execCommand(cmd, false, value === undefined ? null : value);
16989 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16991 * @param {String} text | dom node..
16993 insertAtCursor : function(text)
16998 if(!this.activated){
17004 var r = this.doc.selection.createRange();
17015 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
17019 // from jquery ui (MIT licenced)
17021 var win = this.win;
17023 if (win.getSelection && win.getSelection().getRangeAt) {
17024 range = win.getSelection().getRangeAt(0);
17025 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
17026 range.insertNode(node);
17027 } else if (win.document.selection && win.document.selection.createRange) {
17028 // no firefox support
17029 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17030 win.document.selection.createRange().pasteHTML(txt);
17032 // no firefox support
17033 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17034 this.execCmd('InsertHTML', txt);
17043 mozKeyPress : function(e){
17045 var c = e.getCharCode(), cmd;
17048 c = String.fromCharCode(c).toLowerCase();
17062 this.cleanUpPaste.defer(100, this);
17070 e.preventDefault();
17078 fixKeys : function(){ // load time branching for fastest keydown performance
17080 return function(e){
17081 var k = e.getKey(), r;
17084 r = this.doc.selection.createRange();
17087 r.pasteHTML('    ');
17094 r = this.doc.selection.createRange();
17096 var target = r.parentElement();
17097 if(!target || target.tagName.toLowerCase() != 'li'){
17099 r.pasteHTML('<br />');
17105 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17106 this.cleanUpPaste.defer(100, this);
17112 }else if(Roo.isOpera){
17113 return function(e){
17114 var k = e.getKey();
17118 this.execCmd('InsertHTML','    ');
17121 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17122 this.cleanUpPaste.defer(100, this);
17127 }else if(Roo.isSafari){
17128 return function(e){
17129 var k = e.getKey();
17133 this.execCmd('InsertText','\t');
17137 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17138 this.cleanUpPaste.defer(100, this);
17146 getAllAncestors: function()
17148 var p = this.getSelectedNode();
17151 a.push(p); // push blank onto stack..
17152 p = this.getParentElement();
17156 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17160 a.push(this.doc.body);
17164 lastSelNode : false,
17167 getSelection : function()
17169 this.assignDocWin();
17170 return Roo.isIE ? this.doc.selection : this.win.getSelection();
17173 getSelectedNode: function()
17175 // this may only work on Gecko!!!
17177 // should we cache this!!!!
17182 var range = this.createRange(this.getSelection()).cloneRange();
17185 var parent = range.parentElement();
17187 var testRange = range.duplicate();
17188 testRange.moveToElementText(parent);
17189 if (testRange.inRange(range)) {
17192 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17195 parent = parent.parentElement;
17200 // is ancestor a text element.
17201 var ac = range.commonAncestorContainer;
17202 if (ac.nodeType == 3) {
17203 ac = ac.parentNode;
17206 var ar = ac.childNodes;
17209 var other_nodes = [];
17210 var has_other_nodes = false;
17211 for (var i=0;i<ar.length;i++) {
17212 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17215 // fullly contained node.
17217 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17222 // probably selected..
17223 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17224 other_nodes.push(ar[i]);
17228 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17233 has_other_nodes = true;
17235 if (!nodes.length && other_nodes.length) {
17236 nodes= other_nodes;
17238 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17244 createRange: function(sel)
17246 // this has strange effects when using with
17247 // top toolbar - not sure if it's a great idea.
17248 //this.editor.contentWindow.focus();
17249 if (typeof sel != "undefined") {
17251 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17253 return this.doc.createRange();
17256 return this.doc.createRange();
17259 getParentElement: function()
17262 this.assignDocWin();
17263 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17265 var range = this.createRange(sel);
17268 var p = range.commonAncestorContainer;
17269 while (p.nodeType == 3) { // text node
17280 * Range intersection.. the hard stuff...
17284 * [ -- selected range --- ]
17288 * if end is before start or hits it. fail.
17289 * if start is after end or hits it fail.
17291 * if either hits (but other is outside. - then it's not
17297 // @see http://www.thismuchiknow.co.uk/?p=64.
17298 rangeIntersectsNode : function(range, node)
17300 var nodeRange = node.ownerDocument.createRange();
17302 nodeRange.selectNode(node);
17304 nodeRange.selectNodeContents(node);
17307 var rangeStartRange = range.cloneRange();
17308 rangeStartRange.collapse(true);
17310 var rangeEndRange = range.cloneRange();
17311 rangeEndRange.collapse(false);
17313 var nodeStartRange = nodeRange.cloneRange();
17314 nodeStartRange.collapse(true);
17316 var nodeEndRange = nodeRange.cloneRange();
17317 nodeEndRange.collapse(false);
17319 return rangeStartRange.compareBoundaryPoints(
17320 Range.START_TO_START, nodeEndRange) == -1 &&
17321 rangeEndRange.compareBoundaryPoints(
17322 Range.START_TO_START, nodeStartRange) == 1;
17326 rangeCompareNode : function(range, node)
17328 var nodeRange = node.ownerDocument.createRange();
17330 nodeRange.selectNode(node);
17332 nodeRange.selectNodeContents(node);
17336 range.collapse(true);
17338 nodeRange.collapse(true);
17340 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17341 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17343 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17345 var nodeIsBefore = ss == 1;
17346 var nodeIsAfter = ee == -1;
17348 if (nodeIsBefore && nodeIsAfter)
17350 if (!nodeIsBefore && nodeIsAfter)
17351 return 1; //right trailed.
17353 if (nodeIsBefore && !nodeIsAfter)
17354 return 2; // left trailed.
17359 // private? - in a new class?
17360 cleanUpPaste : function()
17362 // cleans up the whole document..
17363 Roo.log('cleanuppaste');
17365 this.cleanUpChildren(this.doc.body);
17366 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17367 if (clean != this.doc.body.innerHTML) {
17368 this.doc.body.innerHTML = clean;
17373 cleanWordChars : function(input) {// change the chars to hex code
17374 var he = Roo.HtmlEditorCore;
17376 var output = input;
17377 Roo.each(he.swapCodes, function(sw) {
17378 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17380 output = output.replace(swapper, sw[1]);
17387 cleanUpChildren : function (n)
17389 if (!n.childNodes.length) {
17392 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17393 this.cleanUpChild(n.childNodes[i]);
17400 cleanUpChild : function (node)
17403 //console.log(node);
17404 if (node.nodeName == "#text") {
17405 // clean up silly Windows -- stuff?
17408 if (node.nodeName == "#comment") {
17409 node.parentNode.removeChild(node);
17410 // clean up silly Windows -- stuff?
17413 var lcname = node.tagName.toLowerCase();
17414 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17415 // whitelist of tags..
17417 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17419 node.parentNode.removeChild(node);
17424 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17426 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17427 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17429 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17430 // remove_keep_children = true;
17433 if (remove_keep_children) {
17434 this.cleanUpChildren(node);
17435 // inserts everything just before this node...
17436 while (node.childNodes.length) {
17437 var cn = node.childNodes[0];
17438 node.removeChild(cn);
17439 node.parentNode.insertBefore(cn, node);
17441 node.parentNode.removeChild(node);
17445 if (!node.attributes || !node.attributes.length) {
17446 this.cleanUpChildren(node);
17450 function cleanAttr(n,v)
17453 if (v.match(/^\./) || v.match(/^\//)) {
17456 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17459 if (v.match(/^#/)) {
17462 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17463 node.removeAttribute(n);
17467 var cwhite = this.cwhite;
17468 var cblack = this.cblack;
17470 function cleanStyle(n,v)
17472 if (v.match(/expression/)) { //XSS?? should we even bother..
17473 node.removeAttribute(n);
17477 var parts = v.split(/;/);
17480 Roo.each(parts, function(p) {
17481 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17485 var l = p.split(':').shift().replace(/\s+/g,'');
17486 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17488 if ( cwhite.length && cblack.indexOf(l) > -1) {
17489 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17490 //node.removeAttribute(n);
17494 // only allow 'c whitelisted system attributes'
17495 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17496 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17497 //node.removeAttribute(n);
17507 if (clean.length) {
17508 node.setAttribute(n, clean.join(';'));
17510 node.removeAttribute(n);
17516 for (var i = node.attributes.length-1; i > -1 ; i--) {
17517 var a = node.attributes[i];
17520 if (a.name.toLowerCase().substr(0,2)=='on') {
17521 node.removeAttribute(a.name);
17524 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17525 node.removeAttribute(a.name);
17528 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17529 cleanAttr(a.name,a.value); // fixme..
17532 if (a.name == 'style') {
17533 cleanStyle(a.name,a.value);
17536 /// clean up MS crap..
17537 // tecnically this should be a list of valid class'es..
17540 if (a.name == 'class') {
17541 if (a.value.match(/^Mso/)) {
17542 node.className = '';
17545 if (a.value.match(/body/)) {
17546 node.className = '';
17557 this.cleanUpChildren(node);
17562 * Clean up MS wordisms...
17564 cleanWord : function(node)
17567 var cleanWordChildren = function()
17569 if (!node.childNodes.length) {
17572 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17573 _t.cleanWord(node.childNodes[i]);
17579 this.cleanWord(this.doc.body);
17582 if (node.nodeName == "#text") {
17583 // clean up silly Windows -- stuff?
17586 if (node.nodeName == "#comment") {
17587 node.parentNode.removeChild(node);
17588 // clean up silly Windows -- stuff?
17592 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17593 node.parentNode.removeChild(node);
17597 // remove - but keep children..
17598 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17599 while (node.childNodes.length) {
17600 var cn = node.childNodes[0];
17601 node.removeChild(cn);
17602 node.parentNode.insertBefore(cn, node);
17604 node.parentNode.removeChild(node);
17605 cleanWordChildren();
17609 if (node.className.length) {
17611 var cn = node.className.split(/\W+/);
17613 Roo.each(cn, function(cls) {
17614 if (cls.match(/Mso[a-zA-Z]+/)) {
17619 node.className = cna.length ? cna.join(' ') : '';
17621 node.removeAttribute("class");
17625 if (node.hasAttribute("lang")) {
17626 node.removeAttribute("lang");
17629 if (node.hasAttribute("style")) {
17631 var styles = node.getAttribute("style").split(";");
17633 Roo.each(styles, function(s) {
17634 if (!s.match(/:/)) {
17637 var kv = s.split(":");
17638 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17641 // what ever is left... we allow.
17644 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17645 if (!nstyle.length) {
17646 node.removeAttribute('style');
17650 cleanWordChildren();
17654 domToHTML : function(currentElement, depth, nopadtext) {
17656 depth = depth || 0;
17657 nopadtext = nopadtext || false;
17659 if (!currentElement) {
17660 return this.domToHTML(this.doc.body);
17663 //Roo.log(currentElement);
17665 var allText = false;
17666 var nodeName = currentElement.nodeName;
17667 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17669 if (nodeName == '#text') {
17670 return currentElement.nodeValue;
17675 if (nodeName != 'BODY') {
17678 // Prints the node tagName, such as <A>, <IMG>, etc
17681 for(i = 0; i < currentElement.attributes.length;i++) {
17683 var aname = currentElement.attributes.item(i).name;
17684 if (!currentElement.attributes.item(i).value.length) {
17687 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17690 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17699 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17702 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17707 // Traverse the tree
17709 var currentElementChild = currentElement.childNodes.item(i);
17710 var allText = true;
17711 var innerHTML = '';
17713 while (currentElementChild) {
17714 // Formatting code (indent the tree so it looks nice on the screen)
17715 var nopad = nopadtext;
17716 if (lastnode == 'SPAN') {
17720 if (currentElementChild.nodeName == '#text') {
17721 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17722 if (!nopad && toadd.length > 80) {
17723 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17725 innerHTML += toadd;
17728 currentElementChild = currentElement.childNodes.item(i);
17734 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17736 // Recursively traverse the tree structure of the child node
17737 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17738 lastnode = currentElementChild.nodeName;
17740 currentElementChild=currentElement.childNodes.item(i);
17746 // The remaining code is mostly for formatting the tree
17747 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17752 ret+= "</"+tagName+">";
17758 applyBlacklists : function()
17760 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
17761 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
17765 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17766 if (b.indexOf(tag) > -1) {
17769 this.white.push(tag);
17773 Roo.each(w, function(tag) {
17774 if (b.indexOf(tag) > -1) {
17777 if (this.white.indexOf(tag) > -1) {
17780 this.white.push(tag);
17785 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17786 if (w.indexOf(tag) > -1) {
17789 this.black.push(tag);
17793 Roo.each(b, function(tag) {
17794 if (w.indexOf(tag) > -1) {
17797 if (this.black.indexOf(tag) > -1) {
17800 this.black.push(tag);
17805 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
17806 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
17810 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17811 if (b.indexOf(tag) > -1) {
17814 this.cwhite.push(tag);
17818 Roo.each(w, function(tag) {
17819 if (b.indexOf(tag) > -1) {
17822 if (this.cwhite.indexOf(tag) > -1) {
17825 this.cwhite.push(tag);
17830 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17831 if (w.indexOf(tag) > -1) {
17834 this.cblack.push(tag);
17838 Roo.each(b, function(tag) {
17839 if (w.indexOf(tag) > -1) {
17842 if (this.cblack.indexOf(tag) > -1) {
17845 this.cblack.push(tag);
17850 // hide stuff that is not compatible
17864 * @event specialkey
17868 * @cfg {String} fieldClass @hide
17871 * @cfg {String} focusClass @hide
17874 * @cfg {String} autoCreate @hide
17877 * @cfg {String} inputType @hide
17880 * @cfg {String} invalidClass @hide
17883 * @cfg {String} invalidText @hide
17886 * @cfg {String} msgFx @hide
17889 * @cfg {String} validateOnBlur @hide
17893 Roo.HtmlEditorCore.white = [
17894 'area', 'br', 'img', 'input', 'hr', 'wbr',
17896 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17897 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17898 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17899 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17900 'table', 'ul', 'xmp',
17902 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17905 'dir', 'menu', 'ol', 'ul', 'dl',
17911 Roo.HtmlEditorCore.black = [
17912 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17914 'base', 'basefont', 'bgsound', 'blink', 'body',
17915 'frame', 'frameset', 'head', 'html', 'ilayer',
17916 'iframe', 'layer', 'link', 'meta', 'object',
17917 'script', 'style' ,'title', 'xml' // clean later..
17919 Roo.HtmlEditorCore.clean = [
17920 'script', 'style', 'title', 'xml'
17922 Roo.HtmlEditorCore.remove = [
17927 Roo.HtmlEditorCore.ablack = [
17931 Roo.HtmlEditorCore.aclean = [
17932 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17936 Roo.HtmlEditorCore.pwhite= [
17937 'http', 'https', 'mailto'
17940 // white listed style attributes.
17941 Roo.HtmlEditorCore.cwhite= [
17942 // 'text-align', /// default is to allow most things..
17948 // black listed style attributes.
17949 Roo.HtmlEditorCore.cblack= [
17950 // 'font-size' -- this can be set by the project
17954 Roo.HtmlEditorCore.swapCodes =[
17973 * @class Roo.bootstrap.HtmlEditor
17974 * @extends Roo.bootstrap.TextArea
17975 * Bootstrap HtmlEditor class
17978 * Create a new HtmlEditor
17979 * @param {Object} config The config object
17982 Roo.bootstrap.HtmlEditor = function(config){
17983 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17984 if (!this.toolbars) {
17985 this.toolbars = [];
17987 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17990 * @event initialize
17991 * Fires when the editor is fully initialized (including the iframe)
17992 * @param {HtmlEditor} this
17997 * Fires when the editor is first receives the focus. Any insertion must wait
17998 * until after this event.
17999 * @param {HtmlEditor} this
18003 * @event beforesync
18004 * Fires before the textarea is updated with content from the editor iframe. Return false
18005 * to cancel the sync.
18006 * @param {HtmlEditor} this
18007 * @param {String} html
18011 * @event beforepush
18012 * Fires before the iframe editor is updated with content from the textarea. Return false
18013 * to cancel the push.
18014 * @param {HtmlEditor} this
18015 * @param {String} html
18020 * Fires when the textarea is updated with content from the editor iframe.
18021 * @param {HtmlEditor} this
18022 * @param {String} html
18027 * Fires when the iframe editor is updated with content from the textarea.
18028 * @param {HtmlEditor} this
18029 * @param {String} html
18033 * @event editmodechange
18034 * Fires when the editor switches edit modes
18035 * @param {HtmlEditor} this
18036 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18038 editmodechange: true,
18040 * @event editorevent
18041 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18042 * @param {HtmlEditor} this
18046 * @event firstfocus
18047 * Fires when on first focus - needed by toolbars..
18048 * @param {HtmlEditor} this
18053 * Auto save the htmlEditor value as a file into Events
18054 * @param {HtmlEditor} this
18058 * @event savedpreview
18059 * preview the saved version of htmlEditor
18060 * @param {HtmlEditor} this
18067 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
18071 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18076 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18081 * @cfg {Number} height (in pixels)
18085 * @cfg {Number} width (in pixels)
18090 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18093 stylesheets: false,
18098 // private properties
18099 validationEvent : false,
18101 initialized : false,
18104 onFocus : Roo.emptyFn,
18106 hideMode:'offsets',
18109 tbContainer : false,
18111 toolbarContainer :function() {
18112 return this.wrap.select('.x-html-editor-tb',true).first();
18116 * Protected method that will not generally be called directly. It
18117 * is called when the editor creates its toolbar. Override this method if you need to
18118 * add custom toolbar buttons.
18119 * @param {HtmlEditor} editor
18121 createToolbar : function(){
18123 Roo.log("create toolbars");
18125 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18126 this.toolbars[0].render(this.toolbarContainer());
18130 // if (!editor.toolbars || !editor.toolbars.length) {
18131 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18134 // for (var i =0 ; i < editor.toolbars.length;i++) {
18135 // editor.toolbars[i] = Roo.factory(
18136 // typeof(editor.toolbars[i]) == 'string' ?
18137 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
18138 // Roo.bootstrap.HtmlEditor);
18139 // editor.toolbars[i].init(editor);
18145 onRender : function(ct, position)
18147 // Roo.log("Call onRender: " + this.xtype);
18149 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18151 this.wrap = this.inputEl().wrap({
18152 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18155 this.editorcore.onRender(ct, position);
18157 if (this.resizable) {
18158 this.resizeEl = new Roo.Resizable(this.wrap, {
18162 minHeight : this.height,
18163 height: this.height,
18164 handles : this.resizable,
18167 resize : function(r, w, h) {
18168 _t.onResize(w,h); // -something
18174 this.createToolbar(this);
18177 if(!this.width && this.resizable){
18178 this.setSize(this.wrap.getSize());
18180 if (this.resizeEl) {
18181 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18182 // should trigger onReize..
18188 onResize : function(w, h)
18190 Roo.log('resize: ' +w + ',' + h );
18191 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18195 if(this.inputEl() ){
18196 if(typeof w == 'number'){
18197 var aw = w - this.wrap.getFrameWidth('lr');
18198 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18201 if(typeof h == 'number'){
18202 var tbh = -11; // fixme it needs to tool bar size!
18203 for (var i =0; i < this.toolbars.length;i++) {
18204 // fixme - ask toolbars for heights?
18205 tbh += this.toolbars[i].el.getHeight();
18206 //if (this.toolbars[i].footer) {
18207 // tbh += this.toolbars[i].footer.el.getHeight();
18215 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18216 ah -= 5; // knock a few pixes off for look..
18217 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18221 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18222 this.editorcore.onResize(ew,eh);
18227 * Toggles the editor between standard and source edit mode.
18228 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18230 toggleSourceEdit : function(sourceEditMode)
18232 this.editorcore.toggleSourceEdit(sourceEditMode);
18234 if(this.editorcore.sourceEditMode){
18235 Roo.log('editor - showing textarea');
18238 // Roo.log(this.syncValue());
18240 this.inputEl().removeClass(['hide', 'x-hidden']);
18241 this.inputEl().dom.removeAttribute('tabIndex');
18242 this.inputEl().focus();
18244 Roo.log('editor - hiding textarea');
18246 // Roo.log(this.pushValue());
18249 this.inputEl().addClass(['hide', 'x-hidden']);
18250 this.inputEl().dom.setAttribute('tabIndex', -1);
18251 //this.deferFocus();
18254 if(this.resizable){
18255 this.setSize(this.wrap.getSize());
18258 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18261 // private (for BoxComponent)
18262 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18264 // private (for BoxComponent)
18265 getResizeEl : function(){
18269 // private (for BoxComponent)
18270 getPositionEl : function(){
18275 initEvents : function(){
18276 this.originalValue = this.getValue();
18280 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18283 // markInvalid : Roo.emptyFn,
18285 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18288 // clearInvalid : Roo.emptyFn,
18290 setValue : function(v){
18291 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18292 this.editorcore.pushValue();
18297 deferFocus : function(){
18298 this.focus.defer(10, this);
18302 focus : function(){
18303 this.editorcore.focus();
18309 onDestroy : function(){
18315 for (var i =0; i < this.toolbars.length;i++) {
18316 // fixme - ask toolbars for heights?
18317 this.toolbars[i].onDestroy();
18320 this.wrap.dom.innerHTML = '';
18321 this.wrap.remove();
18326 onFirstFocus : function(){
18327 //Roo.log("onFirstFocus");
18328 this.editorcore.onFirstFocus();
18329 for (var i =0; i < this.toolbars.length;i++) {
18330 this.toolbars[i].onFirstFocus();
18336 syncValue : function()
18338 this.editorcore.syncValue();
18341 pushValue : function()
18343 this.editorcore.pushValue();
18347 // hide stuff that is not compatible
18361 * @event specialkey
18365 * @cfg {String} fieldClass @hide
18368 * @cfg {String} focusClass @hide
18371 * @cfg {String} autoCreate @hide
18374 * @cfg {String} inputType @hide
18377 * @cfg {String} invalidClass @hide
18380 * @cfg {String} invalidText @hide
18383 * @cfg {String} msgFx @hide
18386 * @cfg {String} validateOnBlur @hide
18395 Roo.namespace('Roo.bootstrap.htmleditor');
18397 * @class Roo.bootstrap.HtmlEditorToolbar1
18402 new Roo.bootstrap.HtmlEditor({
18405 new Roo.bootstrap.HtmlEditorToolbar1({
18406 disable : { fonts: 1 , format: 1, ..., ... , ...],
18412 * @cfg {Object} disable List of elements to disable..
18413 * @cfg {Array} btns List of additional buttons.
18417 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18420 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18423 Roo.apply(this, config);
18425 // default disabled, based on 'good practice'..
18426 this.disable = this.disable || {};
18427 Roo.applyIf(this.disable, {
18430 specialElements : true
18432 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18434 this.editor = config.editor;
18435 this.editorcore = config.editor.editorcore;
18437 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18439 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18440 // dont call parent... till later.
18442 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18447 editorcore : false,
18452 "h1","h2","h3","h4","h5","h6",
18454 "abbr", "acronym", "address", "cite", "samp", "var",
18458 onRender : function(ct, position)
18460 // Roo.log("Call onRender: " + this.xtype);
18462 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18464 this.el.dom.style.marginBottom = '0';
18466 var editorcore = this.editorcore;
18467 var editor= this.editor;
18470 var btn = function(id,cmd , toggle, handler){
18472 var event = toggle ? 'toggle' : 'click';
18477 xns: Roo.bootstrap,
18480 enableToggle:toggle !== false,
18482 pressed : toggle ? false : null,
18485 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18486 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18495 xns: Roo.bootstrap,
18496 glyphicon : 'font',
18500 xns: Roo.bootstrap,
18504 Roo.each(this.formats, function(f) {
18505 style.menu.items.push({
18507 xns: Roo.bootstrap,
18508 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18513 editorcore.insertTag(this.tagname);
18520 children.push(style);
18523 btn('bold',false,true);
18524 btn('italic',false,true);
18525 btn('align-left', 'justifyleft',true);
18526 btn('align-center', 'justifycenter',true);
18527 btn('align-right' , 'justifyright',true);
18528 btn('link', false, false, function(btn) {
18529 //Roo.log("create link?");
18530 var url = prompt(this.createLinkText, this.defaultLinkValue);
18531 if(url && url != 'http:/'+'/'){
18532 this.editorcore.relayCmd('createlink', url);
18535 btn('list','insertunorderedlist',true);
18536 btn('pencil', false,true, function(btn){
18539 this.toggleSourceEdit(btn.pressed);
18545 xns: Roo.bootstrap,
18550 xns: Roo.bootstrap,
18555 cog.menu.items.push({
18557 xns: Roo.bootstrap,
18558 html : Clean styles,
18563 editorcore.insertTag(this.tagname);
18572 this.xtype = 'NavSimplebar';
18574 for(var i=0;i< children.length;i++) {
18576 this.buttons.add(this.addxtypeChild(children[i]));
18580 editor.on('editorevent', this.updateToolbar, this);
18582 onBtnClick : function(id)
18584 this.editorcore.relayCmd(id);
18585 this.editorcore.focus();
18589 * Protected method that will not generally be called directly. It triggers
18590 * a toolbar update by reading the markup state of the current selection in the editor.
18592 updateToolbar: function(){
18594 if(!this.editorcore.activated){
18595 this.editor.onFirstFocus(); // is this neeed?
18599 var btns = this.buttons;
18600 var doc = this.editorcore.doc;
18601 btns.get('bold').setActive(doc.queryCommandState('bold'));
18602 btns.get('italic').setActive(doc.queryCommandState('italic'));
18603 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18605 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18606 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18607 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18609 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18610 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18613 var ans = this.editorcore.getAllAncestors();
18614 if (this.formatCombo) {
18617 var store = this.formatCombo.store;
18618 this.formatCombo.setValue("");
18619 for (var i =0; i < ans.length;i++) {
18620 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18622 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18630 // hides menus... - so this cant be on a menu...
18631 Roo.bootstrap.MenuMgr.hideAll();
18633 Roo.bootstrap.MenuMgr.hideAll();
18634 //this.editorsyncValue();
18636 onFirstFocus: function() {
18637 this.buttons.each(function(item){
18641 toggleSourceEdit : function(sourceEditMode){
18644 if(sourceEditMode){
18645 Roo.log("disabling buttons");
18646 this.buttons.each( function(item){
18647 if(item.cmd != 'pencil'){
18653 Roo.log("enabling buttons");
18654 if(this.editorcore.initialized){
18655 this.buttons.each( function(item){
18661 Roo.log("calling toggole on editor");
18662 // tell the editor that it's been pressed..
18663 this.editor.toggleSourceEdit(sourceEditMode);
18673 * @class Roo.bootstrap.Table.AbstractSelectionModel
18674 * @extends Roo.util.Observable
18675 * Abstract base class for grid SelectionModels. It provides the interface that should be
18676 * implemented by descendant classes. This class should not be directly instantiated.
18679 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18680 this.locked = false;
18681 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18685 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18686 /** @ignore Called by the grid automatically. Do not call directly. */
18687 init : function(grid){
18693 * Locks the selections.
18696 this.locked = true;
18700 * Unlocks the selections.
18702 unlock : function(){
18703 this.locked = false;
18707 * Returns true if the selections are locked.
18708 * @return {Boolean}
18710 isLocked : function(){
18711 return this.locked;
18715 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18716 * @class Roo.bootstrap.Table.RowSelectionModel
18717 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18718 * It supports multiple selections and keyboard selection/navigation.
18720 * @param {Object} config
18723 Roo.bootstrap.Table.RowSelectionModel = function(config){
18724 Roo.apply(this, config);
18725 this.selections = new Roo.util.MixedCollection(false, function(o){
18730 this.lastActive = false;
18734 * @event selectionchange
18735 * Fires when the selection changes
18736 * @param {SelectionModel} this
18738 "selectionchange" : true,
18740 * @event afterselectionchange
18741 * Fires after the selection changes (eg. by key press or clicking)
18742 * @param {SelectionModel} this
18744 "afterselectionchange" : true,
18746 * @event beforerowselect
18747 * Fires when a row is selected being selected, return false to cancel.
18748 * @param {SelectionModel} this
18749 * @param {Number} rowIndex The selected index
18750 * @param {Boolean} keepExisting False if other selections will be cleared
18752 "beforerowselect" : true,
18755 * Fires when a row is selected.
18756 * @param {SelectionModel} this
18757 * @param {Number} rowIndex The selected index
18758 * @param {Roo.data.Record} r The record
18760 "rowselect" : true,
18762 * @event rowdeselect
18763 * Fires when a row is deselected.
18764 * @param {SelectionModel} this
18765 * @param {Number} rowIndex The selected index
18767 "rowdeselect" : true
18769 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18770 this.locked = false;
18773 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18775 * @cfg {Boolean} singleSelect
18776 * True to allow selection of only one row at a time (defaults to false)
18778 singleSelect : false,
18781 initEvents : function(){
18783 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18784 this.grid.on("mousedown", this.handleMouseDown, this);
18785 }else{ // allow click to work like normal
18786 this.grid.on("rowclick", this.handleDragableRowClick, this);
18789 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18790 "up" : function(e){
18792 this.selectPrevious(e.shiftKey);
18793 }else if(this.last !== false && this.lastActive !== false){
18794 var last = this.last;
18795 this.selectRange(this.last, this.lastActive-1);
18796 this.grid.getView().focusRow(this.lastActive);
18797 if(last !== false){
18801 this.selectFirstRow();
18803 this.fireEvent("afterselectionchange", this);
18805 "down" : function(e){
18807 this.selectNext(e.shiftKey);
18808 }else if(this.last !== false && this.lastActive !== false){
18809 var last = this.last;
18810 this.selectRange(this.last, this.lastActive+1);
18811 this.grid.getView().focusRow(this.lastActive);
18812 if(last !== false){
18816 this.selectFirstRow();
18818 this.fireEvent("afterselectionchange", this);
18823 var view = this.grid.view;
18824 view.on("refresh", this.onRefresh, this);
18825 view.on("rowupdated", this.onRowUpdated, this);
18826 view.on("rowremoved", this.onRemove, this);
18830 onRefresh : function(){
18831 var ds = this.grid.dataSource, i, v = this.grid.view;
18832 var s = this.selections;
18833 s.each(function(r){
18834 if((i = ds.indexOfId(r.id)) != -1){
18843 onRemove : function(v, index, r){
18844 this.selections.remove(r);
18848 onRowUpdated : function(v, index, r){
18849 if(this.isSelected(r)){
18850 v.onRowSelect(index);
18856 * @param {Array} records The records to select
18857 * @param {Boolean} keepExisting (optional) True to keep existing selections
18859 selectRecords : function(records, keepExisting){
18861 this.clearSelections();
18863 var ds = this.grid.dataSource;
18864 for(var i = 0, len = records.length; i < len; i++){
18865 this.selectRow(ds.indexOf(records[i]), true);
18870 * Gets the number of selected rows.
18873 getCount : function(){
18874 return this.selections.length;
18878 * Selects the first row in the grid.
18880 selectFirstRow : function(){
18885 * Select the last row.
18886 * @param {Boolean} keepExisting (optional) True to keep existing selections
18888 selectLastRow : function(keepExisting){
18889 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18893 * Selects the row immediately following the last selected row.
18894 * @param {Boolean} keepExisting (optional) True to keep existing selections
18896 selectNext : function(keepExisting){
18897 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18898 this.selectRow(this.last+1, keepExisting);
18899 this.grid.getView().focusRow(this.last);
18904 * Selects the row that precedes the last selected row.
18905 * @param {Boolean} keepExisting (optional) True to keep existing selections
18907 selectPrevious : function(keepExisting){
18909 this.selectRow(this.last-1, keepExisting);
18910 this.grid.getView().focusRow(this.last);
18915 * Returns the selected records
18916 * @return {Array} Array of selected records
18918 getSelections : function(){
18919 return [].concat(this.selections.items);
18923 * Returns the first selected record.
18926 getSelected : function(){
18927 return this.selections.itemAt(0);
18932 * Clears all selections.
18934 clearSelections : function(fast){
18935 if(this.locked) return;
18937 var ds = this.grid.dataSource;
18938 var s = this.selections;
18939 s.each(function(r){
18940 this.deselectRow(ds.indexOfId(r.id));
18944 this.selections.clear();
18951 * Selects all rows.
18953 selectAll : function(){
18954 if(this.locked) return;
18955 this.selections.clear();
18956 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18957 this.selectRow(i, true);
18962 * Returns True if there is a selection.
18963 * @return {Boolean}
18965 hasSelection : function(){
18966 return this.selections.length > 0;
18970 * Returns True if the specified row is selected.
18971 * @param {Number/Record} record The record or index of the record to check
18972 * @return {Boolean}
18974 isSelected : function(index){
18975 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18976 return (r && this.selections.key(r.id) ? true : false);
18980 * Returns True if the specified record id is selected.
18981 * @param {String} id The id of record to check
18982 * @return {Boolean}
18984 isIdSelected : function(id){
18985 return (this.selections.key(id) ? true : false);
18989 handleMouseDown : function(e, t){
18990 var view = this.grid.getView(), rowIndex;
18991 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18994 if(e.shiftKey && this.last !== false){
18995 var last = this.last;
18996 this.selectRange(last, rowIndex, e.ctrlKey);
18997 this.last = last; // reset the last
18998 view.focusRow(rowIndex);
19000 var isSelected = this.isSelected(rowIndex);
19001 if(e.button !== 0 && isSelected){
19002 view.focusRow(rowIndex);
19003 }else if(e.ctrlKey && isSelected){
19004 this.deselectRow(rowIndex);
19005 }else if(!isSelected){
19006 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
19007 view.focusRow(rowIndex);
19010 this.fireEvent("afterselectionchange", this);
19013 handleDragableRowClick : function(grid, rowIndex, e)
19015 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
19016 this.selectRow(rowIndex, false);
19017 grid.view.focusRow(rowIndex);
19018 this.fireEvent("afterselectionchange", this);
19023 * Selects multiple rows.
19024 * @param {Array} rows Array of the indexes of the row to select
19025 * @param {Boolean} keepExisting (optional) True to keep existing selections
19027 selectRows : function(rows, keepExisting){
19029 this.clearSelections();
19031 for(var i = 0, len = rows.length; i < len; i++){
19032 this.selectRow(rows[i], true);
19037 * Selects a range of rows. All rows in between startRow and endRow are also selected.
19038 * @param {Number} startRow The index of the first row in the range
19039 * @param {Number} endRow The index of the last row in the range
19040 * @param {Boolean} keepExisting (optional) True to retain existing selections
19042 selectRange : function(startRow, endRow, keepExisting){
19043 if(this.locked) return;
19045 this.clearSelections();
19047 if(startRow <= endRow){
19048 for(var i = startRow; i <= endRow; i++){
19049 this.selectRow(i, true);
19052 for(var i = startRow; i >= endRow; i--){
19053 this.selectRow(i, true);
19059 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19060 * @param {Number} startRow The index of the first row in the range
19061 * @param {Number} endRow The index of the last row in the range
19063 deselectRange : function(startRow, endRow, preventViewNotify){
19064 if(this.locked) return;
19065 for(var i = startRow; i <= endRow; i++){
19066 this.deselectRow(i, preventViewNotify);
19072 * @param {Number} row The index of the row to select
19073 * @param {Boolean} keepExisting (optional) True to keep existing selections
19075 selectRow : function(index, keepExisting, preventViewNotify){
19076 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19077 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19078 if(!keepExisting || this.singleSelect){
19079 this.clearSelections();
19081 var r = this.grid.dataSource.getAt(index);
19082 this.selections.add(r);
19083 this.last = this.lastActive = index;
19084 if(!preventViewNotify){
19085 this.grid.getView().onRowSelect(index);
19087 this.fireEvent("rowselect", this, index, r);
19088 this.fireEvent("selectionchange", this);
19094 * @param {Number} row The index of the row to deselect
19096 deselectRow : function(index, preventViewNotify){
19097 if(this.locked) return;
19098 if(this.last == index){
19101 if(this.lastActive == index){
19102 this.lastActive = false;
19104 var r = this.grid.dataSource.getAt(index);
19105 this.selections.remove(r);
19106 if(!preventViewNotify){
19107 this.grid.getView().onRowDeselect(index);
19109 this.fireEvent("rowdeselect", this, index);
19110 this.fireEvent("selectionchange", this);
19114 restoreLast : function(){
19116 this.last = this._last;
19121 acceptsNav : function(row, col, cm){
19122 return !cm.isHidden(col) && cm.isCellEditable(col, row);
19126 onEditorKey : function(field, e){
19127 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19132 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19134 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19136 }else if(k == e.ENTER && !e.ctrlKey){
19140 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19142 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19144 }else if(k == e.ESC){
19148 g.startEditing(newCell[0], newCell[1]);
19153 * Ext JS Library 1.1.1
19154 * Copyright(c) 2006-2007, Ext JS, LLC.
19156 * Originally Released Under LGPL - original licence link has changed is not relivant.
19159 * <script type="text/javascript">
19163 * @class Roo.bootstrap.PagingToolbar
19165 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19167 * Create a new PagingToolbar
19168 * @param {Object} config The config object
19170 Roo.bootstrap.PagingToolbar = function(config)
19172 // old args format still supported... - xtype is prefered..
19173 // created from xtype...
19174 var ds = config.dataSource;
19175 this.toolbarItems = [];
19176 if (config.items) {
19177 this.toolbarItems = config.items;
19178 // config.items = [];
19181 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19188 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19192 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19194 * @cfg {Roo.data.Store} dataSource
19195 * The underlying data store providing the paged data
19198 * @cfg {String/HTMLElement/Element} container
19199 * container The id or element that will contain the toolbar
19202 * @cfg {Boolean} displayInfo
19203 * True to display the displayMsg (defaults to false)
19206 * @cfg {Number} pageSize
19207 * The number of records to display per page (defaults to 20)
19211 * @cfg {String} displayMsg
19212 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19214 displayMsg : 'Displaying {0} - {1} of {2}',
19216 * @cfg {String} emptyMsg
19217 * The message to display when no records are found (defaults to "No data to display")
19219 emptyMsg : 'No data to display',
19221 * Customizable piece of the default paging text (defaults to "Page")
19224 beforePageText : "Page",
19226 * Customizable piece of the default paging text (defaults to "of %0")
19229 afterPageText : "of {0}",
19231 * Customizable piece of the default paging text (defaults to "First Page")
19234 firstText : "First Page",
19236 * Customizable piece of the default paging text (defaults to "Previous Page")
19239 prevText : "Previous Page",
19241 * Customizable piece of the default paging text (defaults to "Next Page")
19244 nextText : "Next Page",
19246 * Customizable piece of the default paging text (defaults to "Last Page")
19249 lastText : "Last Page",
19251 * Customizable piece of the default paging text (defaults to "Refresh")
19254 refreshText : "Refresh",
19258 onRender : function(ct, position)
19260 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19261 this.navgroup.parentId = this.id;
19262 this.navgroup.onRender(this.el, null);
19263 // add the buttons to the navgroup
19265 if(this.displayInfo){
19266 Roo.log(this.el.select('ul.navbar-nav',true).first());
19267 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19268 this.displayEl = this.el.select('.x-paging-info', true).first();
19269 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19270 // this.displayEl = navel.el.select('span',true).first();
19276 Roo.each(_this.buttons, function(e){
19277 Roo.factory(e).onRender(_this.el, null);
19281 Roo.each(_this.toolbarItems, function(e) {
19282 _this.navgroup.addItem(e);
19285 this.first = this.navgroup.addItem({
19286 tooltip: this.firstText,
19288 icon : 'fa fa-backward',
19290 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19293 this.prev = this.navgroup.addItem({
19294 tooltip: this.prevText,
19296 icon : 'fa fa-step-backward',
19298 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19300 //this.addSeparator();
19303 var field = this.navgroup.addItem( {
19305 cls : 'x-paging-position',
19307 html : this.beforePageText +
19308 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19309 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19312 this.field = field.el.select('input', true).first();
19313 this.field.on("keydown", this.onPagingKeydown, this);
19314 this.field.on("focus", function(){this.dom.select();});
19317 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19318 //this.field.setHeight(18);
19319 //this.addSeparator();
19320 this.next = this.navgroup.addItem({
19321 tooltip: this.nextText,
19323 html : ' <i class="fa fa-step-forward">',
19325 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19327 this.last = this.navgroup.addItem({
19328 tooltip: this.lastText,
19329 icon : 'fa fa-forward',
19332 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19334 //this.addSeparator();
19335 this.loading = this.navgroup.addItem({
19336 tooltip: this.refreshText,
19337 icon: 'fa fa-refresh',
19339 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19345 updateInfo : function(){
19346 if(this.displayEl){
19347 var count = this.ds.getCount();
19348 var msg = count == 0 ?
19352 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19354 this.displayEl.update(msg);
19359 onLoad : function(ds, r, o){
19360 this.cursor = o.params ? o.params.start : 0;
19361 var d = this.getPageData(),
19365 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19366 this.field.dom.value = ap;
19367 this.first.setDisabled(ap == 1);
19368 this.prev.setDisabled(ap == 1);
19369 this.next.setDisabled(ap == ps);
19370 this.last.setDisabled(ap == ps);
19371 this.loading.enable();
19376 getPageData : function(){
19377 var total = this.ds.getTotalCount();
19380 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19381 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19386 onLoadError : function(){
19387 this.loading.enable();
19391 onPagingKeydown : function(e){
19392 var k = e.getKey();
19393 var d = this.getPageData();
19395 var v = this.field.dom.value, pageNum;
19396 if(!v || isNaN(pageNum = parseInt(v, 10))){
19397 this.field.dom.value = d.activePage;
19400 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19401 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19404 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))
19406 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19407 this.field.dom.value = pageNum;
19408 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19411 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19413 var v = this.field.dom.value, pageNum;
19414 var increment = (e.shiftKey) ? 10 : 1;
19415 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19417 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19418 this.field.dom.value = d.activePage;
19421 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19423 this.field.dom.value = parseInt(v, 10) + increment;
19424 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19425 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19432 beforeLoad : function(){
19434 this.loading.disable();
19439 onClick : function(which){
19446 ds.load({params:{start: 0, limit: this.pageSize}});
19449 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19452 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19455 var total = ds.getTotalCount();
19456 var extra = total % this.pageSize;
19457 var lastStart = extra ? (total - extra) : total-this.pageSize;
19458 ds.load({params:{start: lastStart, limit: this.pageSize}});
19461 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19467 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19468 * @param {Roo.data.Store} store The data store to unbind
19470 unbind : function(ds){
19471 ds.un("beforeload", this.beforeLoad, this);
19472 ds.un("load", this.onLoad, this);
19473 ds.un("loadexception", this.onLoadError, this);
19474 ds.un("remove", this.updateInfo, this);
19475 ds.un("add", this.updateInfo, this);
19476 this.ds = undefined;
19480 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19481 * @param {Roo.data.Store} store The data store to bind
19483 bind : function(ds){
19484 ds.on("beforeload", this.beforeLoad, this);
19485 ds.on("load", this.onLoad, this);
19486 ds.on("loadexception", this.onLoadError, this);
19487 ds.on("remove", this.updateInfo, this);
19488 ds.on("add", this.updateInfo, this);
19499 * @class Roo.bootstrap.MessageBar
19500 * @extends Roo.bootstrap.Component
19501 * Bootstrap MessageBar class
19502 * @cfg {String} html contents of the MessageBar
19503 * @cfg {String} weight (info | success | warning | danger) default info
19504 * @cfg {String} beforeClass insert the bar before the given class
19505 * @cfg {Boolean} closable (true | false) default false
19506 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19509 * Create a new Element
19510 * @param {Object} config The config object
19513 Roo.bootstrap.MessageBar = function(config){
19514 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19517 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19523 beforeClass: 'bootstrap-sticky-wrap',
19525 getAutoCreate : function(){
19529 cls: 'alert alert-dismissable alert-' + this.weight,
19534 html: this.html || ''
19540 cfg.cls += ' alert-messages-fixed';
19554 onRender : function(ct, position)
19556 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19559 var cfg = Roo.apply({}, this.getAutoCreate());
19563 cfg.cls += ' ' + this.cls;
19566 cfg.style = this.style;
19568 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19570 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19573 this.el.select('>button.close').on('click', this.hide, this);
19579 if (!this.rendered) {
19585 this.fireEvent('show', this);
19591 if (!this.rendered) {
19597 this.fireEvent('hide', this);
19600 update : function()
19602 // var e = this.el.dom.firstChild;
19604 // if(this.closable){
19605 // e = e.nextSibling;
19608 // e.data = this.html || '';
19610 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19626 * @class Roo.bootstrap.Graph
19627 * @extends Roo.bootstrap.Component
19628 * Bootstrap Graph class
19632 @cfg {String} graphtype bar | vbar | pie
19633 @cfg {number} g_x coodinator | centre x (pie)
19634 @cfg {number} g_y coodinator | centre y (pie)
19635 @cfg {number} g_r radius (pie)
19636 @cfg {number} g_height height of the chart (respected by all elements in the set)
19637 @cfg {number} g_width width of the chart (respected by all elements in the set)
19638 @cfg {Object} title The title of the chart
19641 -opts (object) options for the chart
19643 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19644 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19646 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.
19647 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19649 o stretch (boolean)
19651 -opts (object) options for the pie
19654 o startAngle (number)
19655 o endAngle (number)
19659 * Create a new Input
19660 * @param {Object} config The config object
19663 Roo.bootstrap.Graph = function(config){
19664 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19670 * The img click event for the img.
19671 * @param {Roo.EventObject} e
19677 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19688 //g_colors: this.colors,
19695 getAutoCreate : function(){
19706 onRender : function(ct,position){
19707 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19708 this.raphael = Raphael(this.el.dom);
19710 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19711 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19712 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19713 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19715 r.text(160, 10, "Single Series Chart").attr(txtattr);
19716 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19717 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19718 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19720 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19721 r.barchart(330, 10, 300, 220, data1);
19722 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19723 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19726 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19727 // r.barchart(30, 30, 560, 250, xdata, {
19728 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19729 // axis : "0 0 1 1",
19730 // axisxlabels : xdata
19731 // //yvalues : cols,
19734 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19736 // this.load(null,xdata,{
19737 // axis : "0 0 1 1",
19738 // axisxlabels : xdata
19743 load : function(graphtype,xdata,opts){
19744 this.raphael.clear();
19746 graphtype = this.graphtype;
19751 var r = this.raphael,
19752 fin = function () {
19753 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19755 fout = function () {
19756 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19758 pfin = function() {
19759 this.sector.stop();
19760 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19763 this.label[0].stop();
19764 this.label[0].attr({ r: 7.5 });
19765 this.label[1].attr({ "font-weight": 800 });
19768 pfout = function() {
19769 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19772 this.label[0].animate({ r: 5 }, 500, "bounce");
19773 this.label[1].attr({ "font-weight": 400 });
19779 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19782 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19785 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19786 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19788 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19795 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19800 setTitle: function(o)
19805 initEvents: function() {
19808 this.el.on('click', this.onClick, this);
19812 onClick : function(e)
19814 Roo.log('img onclick');
19815 this.fireEvent('click', this, e);
19827 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19830 * @class Roo.bootstrap.dash.NumberBox
19831 * @extends Roo.bootstrap.Component
19832 * Bootstrap NumberBox class
19833 * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19834 * @cfg {String} headline Box headline
19835 * @cfg {String} content Box content
19836 * @cfg {String} icon Box icon
19837 * @cfg {String} footer Footer text
19838 * @cfg {String} fhref Footer href
19841 * Create a new NumberBox
19842 * @param {Object} config The config object
19846 Roo.bootstrap.dash.NumberBox = function(config){
19847 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19851 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19861 getAutoCreate : function(){
19865 cls : 'small-box bg-' + this.bgcolor,
19873 cls : 'roo-headline',
19874 html : this.headline
19878 cls : 'roo-content',
19879 html : this.content
19893 cls : 'ion ' + this.icon
19902 cls : 'small-box-footer',
19903 href : this.fhref || '#',
19907 cfg.cn.push(footer);
19914 onRender : function(ct,position){
19915 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19922 setHeadline: function (value)
19924 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19927 setFooter: function (value, href)
19929 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19932 this.el.select('a.small-box-footer',true).first().attr('href', href);
19937 setContent: function (value)
19939 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19942 initEvents: function()
19956 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19959 * @class Roo.bootstrap.dash.TabBox
19960 * @extends Roo.bootstrap.Component
19961 * Bootstrap TabBox class
19962 * @cfg {String} title Title of the TabBox
19963 * @cfg {String} icon Icon of the TabBox
19964 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19965 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19968 * Create a new TabBox
19969 * @param {Object} config The config object
19973 Roo.bootstrap.dash.TabBox = function(config){
19974 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19979 * When a pane is added
19980 * @param {Roo.bootstrap.dash.TabPane} pane
19984 * @event activatepane
19985 * When a pane is activated
19986 * @param {Roo.bootstrap.dash.TabPane} pane
19988 "activatepane" : true
19996 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
20001 tabScrollable : false,
20003 getChildContainer : function()
20005 return this.el.select('.tab-content', true).first();
20008 getAutoCreate : function(){
20012 cls: 'pull-left header',
20020 cls: 'fa ' + this.icon
20026 cls: 'nav nav-tabs pull-right',
20032 if(this.tabScrollable){
20039 cls: 'nav nav-tabs pull-right',
20050 cls: 'nav-tabs-custom',
20055 cls: 'tab-content no-padding',
20063 initEvents : function()
20065 //Roo.log('add add pane handler');
20066 this.on('addpane', this.onAddPane, this);
20069 * Updates the box title
20070 * @param {String} html to set the title to.
20072 setTitle : function(value)
20074 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20076 onAddPane : function(pane)
20078 this.panes.push(pane);
20079 //Roo.log('addpane');
20081 // tabs are rendere left to right..
20082 if(!this.showtabs){
20086 var ctr = this.el.select('.nav-tabs', true).first();
20089 var existing = ctr.select('.nav-tab',true);
20090 var qty = existing.getCount();;
20093 var tab = ctr.createChild({
20095 cls : 'nav-tab' + (qty ? '' : ' active'),
20103 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20106 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20108 pane.el.addClass('active');
20113 onTabClick : function(ev,un,ob,pane)
20115 //Roo.log('tab - prev default');
20116 ev.preventDefault();
20119 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20120 pane.tab.addClass('active');
20121 //Roo.log(pane.title);
20122 this.getChildContainer().select('.tab-pane',true).removeClass('active');
20123 // technically we should have a deactivate event.. but maybe add later.
20124 // and it should not de-activate the selected tab...
20125 this.fireEvent('activatepane', pane);
20126 pane.el.addClass('active');
20127 pane.fireEvent('activate');
20132 getActivePane : function()
20135 Roo.each(this.panes, function(p) {
20136 if(p.el.hasClass('active')){
20157 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20159 * @class Roo.bootstrap.TabPane
20160 * @extends Roo.bootstrap.Component
20161 * Bootstrap TabPane class
20162 * @cfg {Boolean} active (false | true) Default false
20163 * @cfg {String} title title of panel
20167 * Create a new TabPane
20168 * @param {Object} config The config object
20171 Roo.bootstrap.dash.TabPane = function(config){
20172 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20178 * When a pane is activated
20179 * @param {Roo.bootstrap.dash.TabPane} pane
20186 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
20191 // the tabBox that this is attached to.
20194 getAutoCreate : function()
20202 cfg.cls += ' active';
20207 initEvents : function()
20209 //Roo.log('trigger add pane handler');
20210 this.parent().fireEvent('addpane', this)
20214 * Updates the tab title
20215 * @param {String} html to set the title to.
20217 setTitle: function(str)
20223 this.tab.select('a', true).first().dom.innerHTML = str;
20240 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20243 * @class Roo.bootstrap.menu.Menu
20244 * @extends Roo.bootstrap.Component
20245 * Bootstrap Menu class - container for Menu
20246 * @cfg {String} html Text of the menu
20247 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20248 * @cfg {String} icon Font awesome icon
20249 * @cfg {String} pos Menu align to (top | bottom) default bottom
20253 * Create a new Menu
20254 * @param {Object} config The config object
20258 Roo.bootstrap.menu.Menu = function(config){
20259 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20263 * @event beforeshow
20264 * Fires before this menu is displayed
20265 * @param {Roo.bootstrap.menu.Menu} this
20269 * @event beforehide
20270 * Fires before this menu is hidden
20271 * @param {Roo.bootstrap.menu.Menu} this
20276 * Fires after this menu is displayed
20277 * @param {Roo.bootstrap.menu.Menu} this
20282 * Fires after this menu is hidden
20283 * @param {Roo.bootstrap.menu.Menu} this
20288 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20289 * @param {Roo.bootstrap.menu.Menu} this
20290 * @param {Roo.EventObject} e
20297 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
20301 weight : 'default',
20306 getChildContainer : function() {
20307 if(this.isSubMenu){
20311 return this.el.select('ul.dropdown-menu', true).first();
20314 getAutoCreate : function()
20319 cls : 'roo-menu-text',
20327 cls : 'fa ' + this.icon
20338 cls : 'dropdown-button btn btn-' + this.weight,
20343 cls : 'dropdown-toggle btn btn-' + this.weight,
20353 cls : 'dropdown-menu'
20359 if(this.pos == 'top'){
20360 cfg.cls += ' dropup';
20363 if(this.isSubMenu){
20366 cls : 'dropdown-menu'
20373 onRender : function(ct, position)
20375 this.isSubMenu = ct.hasClass('dropdown-submenu');
20377 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20380 initEvents : function()
20382 if(this.isSubMenu){
20386 this.hidden = true;
20388 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20389 this.triggerEl.on('click', this.onTriggerPress, this);
20391 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20392 this.buttonEl.on('click', this.onClick, this);
20398 if(this.isSubMenu){
20402 return this.el.select('ul.dropdown-menu', true).first();
20405 onClick : function(e)
20407 this.fireEvent("click", this, e);
20410 onTriggerPress : function(e)
20412 if (this.isVisible()) {
20419 isVisible : function(){
20420 return !this.hidden;
20425 this.fireEvent("beforeshow", this);
20427 this.hidden = false;
20428 this.el.addClass('open');
20430 Roo.get(document).on("mouseup", this.onMouseUp, this);
20432 this.fireEvent("show", this);
20439 this.fireEvent("beforehide", this);
20441 this.hidden = true;
20442 this.el.removeClass('open');
20444 Roo.get(document).un("mouseup", this.onMouseUp);
20446 this.fireEvent("hide", this);
20449 onMouseUp : function()
20463 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20466 * @class Roo.bootstrap.menu.Item
20467 * @extends Roo.bootstrap.Component
20468 * Bootstrap MenuItem class
20469 * @cfg {Boolean} submenu (true | false) default false
20470 * @cfg {String} html text of the item
20471 * @cfg {String} href the link
20472 * @cfg {Boolean} disable (true | false) default false
20473 * @cfg {Boolean} preventDefault (true | false) default true
20474 * @cfg {String} icon Font awesome icon
20475 * @cfg {String} pos Submenu align to (left | right) default right
20479 * Create a new Item
20480 * @param {Object} config The config object
20484 Roo.bootstrap.menu.Item = function(config){
20485 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20489 * Fires when the mouse is hovering over this menu
20490 * @param {Roo.bootstrap.menu.Item} this
20491 * @param {Roo.EventObject} e
20496 * Fires when the mouse exits this menu
20497 * @param {Roo.bootstrap.menu.Item} this
20498 * @param {Roo.EventObject} e
20504 * The raw click event for the entire grid.
20505 * @param {Roo.EventObject} e
20511 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20516 preventDefault: true,
20521 getAutoCreate : function()
20526 cls : 'roo-menu-item-text',
20534 cls : 'fa ' + this.icon
20543 href : this.href || '#',
20550 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20554 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20556 if(this.pos == 'left'){
20557 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20564 initEvents : function()
20566 this.el.on('mouseover', this.onMouseOver, this);
20567 this.el.on('mouseout', this.onMouseOut, this);
20569 this.el.select('a', true).first().on('click', this.onClick, this);
20573 onClick : function(e)
20575 if(this.preventDefault){
20576 e.preventDefault();
20579 this.fireEvent("click", this, e);
20582 onMouseOver : function(e)
20584 if(this.submenu && this.pos == 'left'){
20585 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20588 this.fireEvent("mouseover", this, e);
20591 onMouseOut : function(e)
20593 this.fireEvent("mouseout", this, e);
20605 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20608 * @class Roo.bootstrap.menu.Separator
20609 * @extends Roo.bootstrap.Component
20610 * Bootstrap Separator class
20613 * Create a new Separator
20614 * @param {Object} config The config object
20618 Roo.bootstrap.menu.Separator = function(config){
20619 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20622 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20624 getAutoCreate : function(){
20645 * @class Roo.bootstrap.Tooltip
20646 * Bootstrap Tooltip class
20647 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20648 * to determine which dom element triggers the tooltip.
20650 * It needs to add support for additional attributes like tooltip-position
20653 * Create a new Toolti
20654 * @param {Object} config The config object
20657 Roo.bootstrap.Tooltip = function(config){
20658 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20661 Roo.apply(Roo.bootstrap.Tooltip, {
20663 * @function init initialize tooltip monitoring.
20667 currentTip : false,
20668 currentRegion : false,
20674 Roo.get(document).on('mouseover', this.enter ,this);
20675 Roo.get(document).on('mouseout', this.leave, this);
20678 this.currentTip = new Roo.bootstrap.Tooltip();
20681 enter : function(ev)
20683 var dom = ev.getTarget();
20684 //Roo.log(['enter',dom]);
20685 var el = Roo.fly(dom);
20686 if (this.currentEl) {
20688 //Roo.log(this.currentEl);
20689 //Roo.log(this.currentEl.contains(dom));
20690 if (this.currentEl == el) {
20693 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
20701 if (this.currentTip.el) {
20702 this.currentTip.el.hide(); // force hiding...
20705 if (!el.attr('tooltip')) { // parents who have tip?
20708 this.currentEl = el;
20709 this.currentTip.bind(el);
20710 this.currentRegion = Roo.lib.Region.getRegion(dom);
20711 this.currentTip.enter();
20714 leave : function(ev)
20716 var dom = ev.getTarget();
20717 //Roo.log(['leave',dom]);
20718 if (!this.currentEl) {
20723 if (dom != this.currentEl.dom) {
20726 var xy = ev.getXY();
20727 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
20730 // only activate leave if mouse cursor is outside... bounding box..
20735 if (this.currentTip) {
20736 this.currentTip.leave();
20738 //Roo.log('clear currentEl');
20739 this.currentEl = false;
20744 'left' : ['r-l', [-2,0], 'right'],
20745 'right' : ['l-r', [2,0], 'left'],
20746 'bottom' : ['t-b', [0,2], 'top'],
20747 'top' : [ 'b-t', [0,-2], 'bottom']
20753 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
20758 delay : null, // can be { show : 300 , hide: 500}
20762 hoverState : null, //???
20764 placement : 'bottom',
20766 getAutoCreate : function(){
20773 cls : 'tooltip-arrow'
20776 cls : 'tooltip-inner'
20783 bind : function(el)
20789 enter : function () {
20791 if (this.timeout != null) {
20792 clearTimeout(this.timeout);
20795 this.hoverState = 'in'
20796 //Roo.log("enter - show");
20797 if (!this.delay || !this.delay.show) {
20802 this.timeout = setTimeout(function () {
20803 if (_t.hoverState == 'in') {
20806 }, this.delay.show);
20810 clearTimeout(this.timeout);
20812 this.hoverState = 'out'
20813 if (!this.delay || !this.delay.hide) {
20819 this.timeout = setTimeout(function () {
20820 //Roo.log("leave - timeout");
20822 if (_t.hoverState == 'out') {
20824 Roo.bootstrap.Tooltip.currentEl = false;
20832 this.render(document.body);
20835 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
20836 this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
20838 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
20840 var placement = typeof this.placement == 'function' ?
20841 this.placement.call(this, this.el, on_el) :
20844 var autoToken = /\s?auto?\s?/i;
20845 var autoPlace = autoToken.test(placement);
20847 placement = placement.replace(autoToken, '') || 'top';
20851 //this.el.setXY([0,0]);
20853 //this.el.dom.style.display='block';
20854 this.el.addClass(placement);
20856 //this.el.appendTo(on_el);
20858 var p = this.getPosition();
20859 var box = this.el.getBox();
20864 var align = Roo.bootstrap.Tooltip.alignment[placement]
20865 this.el.alignTo(this.bindEl, align[0],align[1]);
20866 //var arrow = this.el.select('.arrow',true).first();
20867 //arrow.set(align[2],
20869 this.el.addClass('in fade');
20870 this.hoverState = null;
20872 if (this.el.hasClass('fade')) {
20883 //this.el.setXY([0,0]);
20884 this.el.removeClass('in');