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.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.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
250 Roo.log(self_cntr_el);
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.log('skipping render');
264 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.
4393 * Returns the id of the column at the specified index.
4394 * @param {Number} index The column index
4395 * @return {String} the id
4397 getColumnId : function(index){
4398 return this.config[index].id;
4402 * Returns the column for a specified id.
4403 * @param {String} id The column id
4404 * @return {Object} the column
4406 getColumnById : function(id){
4407 return this.lookup[id];
4412 * Returns the column for a specified dataIndex.
4413 * @param {String} dataIndex The column dataIndex
4414 * @return {Object|Boolean} the column or false if not found
4416 getColumnByDataIndex: function(dataIndex){
4417 var index = this.findColumnIndex(dataIndex);
4418 return index > -1 ? this.config[index] : false;
4422 * Returns the index for a specified column id.
4423 * @param {String} id The column id
4424 * @return {Number} the index, or -1 if not found
4426 getIndexById : function(id){
4427 for(var i = 0, len = this.config.length; i < len; i++){
4428 if(this.config[i].id == id){
4436 * Returns the index for a specified column dataIndex.
4437 * @param {String} dataIndex The column dataIndex
4438 * @return {Number} the index, or -1 if not found
4441 findColumnIndex : function(dataIndex){
4442 for(var i = 0, len = this.config.length; i < len; i++){
4443 if(this.config[i].dataIndex == dataIndex){
4451 moveColumn : function(oldIndex, newIndex){
4452 var c = this.config[oldIndex];
4453 this.config.splice(oldIndex, 1);
4454 this.config.splice(newIndex, 0, c);
4455 this.dataMap = null;
4456 this.fireEvent("columnmoved", this, oldIndex, newIndex);
4459 isLocked : function(colIndex){
4460 return this.config[colIndex].locked === true;
4463 setLocked : function(colIndex, value, suppressEvent){
4464 if(this.isLocked(colIndex) == value){
4467 this.config[colIndex].locked = value;
4469 this.fireEvent("columnlockchange", this, colIndex, value);
4473 getTotalLockedWidth : function(){
4475 for(var i = 0; i < this.config.length; i++){
4476 if(this.isLocked(i) && !this.isHidden(i)){
4477 this.totalWidth += this.getColumnWidth(i);
4483 getLockedCount : function(){
4484 for(var i = 0, len = this.config.length; i < len; i++){
4485 if(!this.isLocked(i)){
4492 * Returns the number of columns.
4495 getColumnCount : function(visibleOnly){
4496 if(visibleOnly === true){
4498 for(var i = 0, len = this.config.length; i < len; i++){
4499 if(!this.isHidden(i)){
4505 return this.config.length;
4509 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
4510 * @param {Function} fn
4511 * @param {Object} scope (optional)
4512 * @return {Array} result
4514 getColumnsBy : function(fn, scope){
4516 for(var i = 0, len = this.config.length; i < len; i++){
4517 var c = this.config[i];
4518 if(fn.call(scope||this, c, i) === true){
4526 * Returns true if the specified column is sortable.
4527 * @param {Number} col The column index
4530 isSortable : function(col){
4531 if(typeof this.config[col].sortable == "undefined"){
4532 return this.defaultSortable;
4534 return this.config[col].sortable;
4538 * Returns the rendering (formatting) function defined for the column.
4539 * @param {Number} col The column index.
4540 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
4542 getRenderer : function(col){
4543 if(!this.config[col].renderer){
4544 return Roo.grid.ColumnModel.defaultRenderer;
4546 return this.config[col].renderer;
4550 * Sets the rendering (formatting) function for a column.
4551 * @param {Number} col The column index
4552 * @param {Function} fn The function to use to process the cell's raw data
4553 * to return HTML markup for the grid view. The render function is called with
4554 * the following parameters:<ul>
4555 * <li>Data value.</li>
4556 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
4557 * <li>css A CSS style string to apply to the table cell.</li>
4558 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
4559 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
4560 * <li>Row index</li>
4561 * <li>Column index</li>
4562 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
4564 setRenderer : function(col, fn){
4565 this.config[col].renderer = fn;
4569 * Returns the width for the specified column.
4570 * @param {Number} col The column index
4573 getColumnWidth : function(col){
4574 return this.config[col].width * 1 || this.defaultWidth;
4578 * Sets the width for a column.
4579 * @param {Number} col The column index
4580 * @param {Number} width The new width
4582 setColumnWidth : function(col, width, suppressEvent){
4583 this.config[col].width = width;
4584 this.totalWidth = null;
4586 this.fireEvent("widthchange", this, col, width);
4591 * Returns the total width of all columns.
4592 * @param {Boolean} includeHidden True to include hidden column widths
4595 getTotalWidth : function(includeHidden){
4596 if(!this.totalWidth){
4597 this.totalWidth = 0;
4598 for(var i = 0, len = this.config.length; i < len; i++){
4599 if(includeHidden || !this.isHidden(i)){
4600 this.totalWidth += this.getColumnWidth(i);
4604 return this.totalWidth;
4608 * Returns the header for the specified column.
4609 * @param {Number} col The column index
4612 getColumnHeader : function(col){
4613 return this.config[col].header;
4617 * Sets the header for a column.
4618 * @param {Number} col The column index
4619 * @param {String} header The new header
4621 setColumnHeader : function(col, header){
4622 this.config[col].header = header;
4623 this.fireEvent("headerchange", this, col, header);
4627 * Returns the tooltip for the specified column.
4628 * @param {Number} col The column index
4631 getColumnTooltip : function(col){
4632 return this.config[col].tooltip;
4635 * Sets the tooltip for a column.
4636 * @param {Number} col The column index
4637 * @param {String} tooltip The new tooltip
4639 setColumnTooltip : function(col, tooltip){
4640 this.config[col].tooltip = tooltip;
4644 * Returns the dataIndex for the specified column.
4645 * @param {Number} col The column index
4648 getDataIndex : function(col){
4649 return this.config[col].dataIndex;
4653 * Sets the dataIndex for a column.
4654 * @param {Number} col The column index
4655 * @param {Number} dataIndex The new dataIndex
4657 setDataIndex : function(col, dataIndex){
4658 this.config[col].dataIndex = dataIndex;
4664 * Returns true if the cell is editable.
4665 * @param {Number} colIndex The column index
4666 * @param {Number} rowIndex The row index
4669 isCellEditable : function(colIndex, rowIndex){
4670 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
4674 * Returns the editor defined for the cell/column.
4675 * return false or null to disable editing.
4676 * @param {Number} colIndex The column index
4677 * @param {Number} rowIndex The row index
4680 getCellEditor : function(colIndex, rowIndex){
4681 return this.config[colIndex].editor;
4685 * Sets if a column is editable.
4686 * @param {Number} col The column index
4687 * @param {Boolean} editable True if the column is editable
4689 setEditable : function(col, editable){
4690 this.config[col].editable = editable;
4695 * Returns true if the column is hidden.
4696 * @param {Number} colIndex The column index
4699 isHidden : function(colIndex){
4700 return this.config[colIndex].hidden;
4705 * Returns true if the column width cannot be changed
4707 isFixed : function(colIndex){
4708 return this.config[colIndex].fixed;
4712 * Returns true if the column can be resized
4715 isResizable : function(colIndex){
4716 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
4719 * Sets if a column is hidden.
4720 * @param {Number} colIndex The column index
4721 * @param {Boolean} hidden True if the column is hidden
4723 setHidden : function(colIndex, hidden){
4724 this.config[colIndex].hidden = hidden;
4725 this.totalWidth = null;
4726 this.fireEvent("hiddenchange", this, colIndex, hidden);
4730 * Sets the editor for a column.
4731 * @param {Number} col The column index
4732 * @param {Object} editor The editor object
4734 setEditor : function(col, editor){
4735 this.config[col].editor = editor;
4739 Roo.grid.ColumnModel.defaultRenderer = function(value){
4740 if(typeof value == "string" && value.length < 1){
4746 // Alias for backwards compatibility
4747 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
4750 * Ext JS Library 1.1.1
4751 * Copyright(c) 2006-2007, Ext JS, LLC.
4753 * Originally Released Under LGPL - original licence link has changed is not relivant.
4756 * <script type="text/javascript">
4760 * @class Roo.LoadMask
4761 * A simple utility class for generically masking elements while loading data. If the element being masked has
4762 * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
4763 * process and the mask element will be cached for reuse. For all other elements, this mask will replace the
4764 * element's UpdateManager load indicator and will be destroyed after the initial load.
4766 * Create a new LoadMask
4767 * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
4768 * @param {Object} config The config object
4770 Roo.LoadMask = function(el, config){
4771 this.el = Roo.get(el);
4772 Roo.apply(this, config);
4774 this.store.on('beforeload', this.onBeforeLoad, this);
4775 this.store.on('load', this.onLoad, this);
4776 this.store.on('loadexception', this.onLoadException, this);
4777 this.removeMask = false;
4779 var um = this.el.getUpdateManager();
4780 um.showLoadIndicator = false; // disable the default indicator
4781 um.on('beforeupdate', this.onBeforeLoad, this);
4782 um.on('update', this.onLoad, this);
4783 um.on('failure', this.onLoad, this);
4784 this.removeMask = true;
4788 Roo.LoadMask.prototype = {
4790 * @cfg {Boolean} removeMask
4791 * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
4792 * False to persist the mask element reference for multiple uses (e.g., for paged data widgets). Defaults to false.
4796 * The text to display in a centered loading message box (defaults to 'Loading...')
4800 * @cfg {String} msgCls
4801 * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
4803 msgCls : 'x-mask-loading',
4806 * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
4812 * Disables the mask to prevent it from being displayed
4814 disable : function(){
4815 this.disabled = true;
4819 * Enables the mask so that it can be displayed
4821 enable : function(){
4822 this.disabled = false;
4825 onLoadException : function()
4829 if (typeof(arguments[3]) != 'undefined') {
4830 Roo.MessageBox.alert("Error loading",arguments[3]);
4834 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
4835 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
4844 this.el.unmask(this.removeMask);
4849 this.el.unmask(this.removeMask);
4853 onBeforeLoad : function(){
4855 this.el.mask(this.msg, this.msgCls);
4860 destroy : function(){
4862 this.store.un('beforeload', this.onBeforeLoad, this);
4863 this.store.un('load', this.onLoad, this);
4864 this.store.un('loadexception', this.onLoadException, this);
4866 var um = this.el.getUpdateManager();
4867 um.un('beforeupdate', this.onBeforeLoad, this);
4868 um.un('update', this.onLoad, this);
4869 um.un('failure', this.onLoad, this);
4880 * @class Roo.bootstrap.Table
4881 * @extends Roo.bootstrap.Component
4882 * Bootstrap Table class
4883 * @cfg {String} cls table class
4884 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4885 * @cfg {String} bgcolor Specifies the background color for a table
4886 * @cfg {Number} border Specifies whether the table cells should have borders or not
4887 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4888 * @cfg {Number} cellspacing Specifies the space between cells
4889 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4890 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4891 * @cfg {String} sortable Specifies that the table should be sortable
4892 * @cfg {String} summary Specifies a summary of the content of a table
4893 * @cfg {Number} width Specifies the width of a table
4894 * @cfg {String} layout table layout (auto | fixed | initial | inherit)
4896 * @cfg {boolean} striped Should the rows be alternative striped
4897 * @cfg {boolean} bordered Add borders to the table
4898 * @cfg {boolean} hover Add hover highlighting
4899 * @cfg {boolean} condensed Format condensed
4900 * @cfg {boolean} responsive Format condensed
4901 * @cfg {Boolean} loadMask (true|false) default false
4902 * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
4903 * @cfg {Boolean} thead (true|false) generate thead, default true
4904 * @cfg {Boolean} RowSelection (true|false) default false
4905 * @cfg {Boolean} CellSelection (true|false) default false
4907 * @cfg {Roo.bootstrap.PagingToolbar} footer a paging toolbar
4911 * Create a new Table
4912 * @param {Object} config The config object
4915 Roo.bootstrap.Table = function(config){
4916 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4919 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4920 this.sm = this.selModel;
4921 this.sm.xmodule = this.xmodule || false;
4923 if (this.cm && typeof(this.cm.config) == 'undefined') {
4924 this.colModel = new Roo.grid.ColumnModel(this.cm);
4925 this.cm = this.colModel;
4926 this.cm.xmodule = this.xmodule || false;
4929 this.store= Roo.factory(this.store, Roo.data);
4930 this.ds = this.store;
4931 this.ds.xmodule = this.xmodule || false;
4934 if (this.footer && this.store) {
4935 this.footer.dataSource = this.ds;
4936 this.footer = Roo.factory(this.footer);
4943 * Fires when a cell is clicked
4944 * @param {Roo.bootstrap.Table} this
4945 * @param {Roo.Element} el
4946 * @param {Number} rowIndex
4947 * @param {Number} columnIndex
4948 * @param {Roo.EventObject} e
4952 * @event celldblclick
4953 * Fires when a cell is double clicked
4954 * @param {Roo.bootstrap.Table} this
4955 * @param {Roo.Element} el
4956 * @param {Number} rowIndex
4957 * @param {Number} columnIndex
4958 * @param {Roo.EventObject} e
4960 "celldblclick" : true,
4963 * Fires when a row is clicked
4964 * @param {Roo.bootstrap.Table} this
4965 * @param {Roo.Element} el
4966 * @param {Number} rowIndex
4967 * @param {Roo.EventObject} e
4971 * @event rowdblclick
4972 * Fires when a row is double clicked
4973 * @param {Roo.bootstrap.Table} this
4974 * @param {Roo.Element} el
4975 * @param {Number} rowIndex
4976 * @param {Roo.EventObject} e
4978 "rowdblclick" : true,
4981 * Fires when a mouseover occur
4982 * @param {Roo.bootstrap.Table} this
4983 * @param {Roo.Element} el
4984 * @param {Number} rowIndex
4985 * @param {Number} columnIndex
4986 * @param {Roo.EventObject} e
4991 * Fires when a mouseout occur
4992 * @param {Roo.bootstrap.Table} this
4993 * @param {Roo.Element} el
4994 * @param {Number} rowIndex
4995 * @param {Number} columnIndex
4996 * @param {Roo.EventObject} e
5001 * Fires when a row is rendered, so you can change add a style to it.
5002 * @param {Roo.bootstrap.Table} this
5003 * @param {Object} rowcfg contains record rowIndex colIndex and rowClass - set rowClass to add a style.
5010 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
5034 RowSelection : false,
5035 CellSelection : false,
5038 // Roo.Element - the tbody
5041 getAutoCreate : function(){
5042 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5051 cfg.cls += ' table-striped';
5055 cfg.cls += ' table-hover';
5057 if (this.bordered) {
5058 cfg.cls += ' table-bordered';
5060 if (this.condensed) {
5061 cfg.cls += ' table-condensed';
5063 if (this.responsive) {
5064 cfg.cls += ' table-responsive';
5068 cfg.cls+= ' ' +this.cls;
5071 // this lot should be simplifed...
5074 cfg.align=this.align;
5077 cfg.bgcolor=this.bgcolor;
5080 cfg.border=this.border;
5082 if (this.cellpadding) {
5083 cfg.cellpadding=this.cellpadding;
5085 if (this.cellspacing) {
5086 cfg.cellspacing=this.cellspacing;
5089 cfg.frame=this.frame;
5092 cfg.rules=this.rules;
5094 if (this.sortable) {
5095 cfg.sortable=this.sortable;
5098 cfg.summary=this.summary;
5101 cfg.width=this.width;
5104 cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5107 if(this.store || this.cm){
5109 cfg.cn.push(this.renderHeader());
5112 cfg.cn.push(this.renderBody());
5115 cfg.cn.push(this.renderFooter());
5118 cfg.cls+= ' TableGrid';
5121 return { cn : [ cfg ] };
5124 initEvents : function()
5126 if(!this.store || !this.cm){
5130 //Roo.log('initEvents with ds!!!!');
5132 this.mainBody = this.el.select('tbody', true).first();
5137 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5138 e.on('click', _this.sort, _this);
5141 this.el.on("click", this.onClick, this);
5142 this.el.on("dblclick", this.onDblClick, this);
5144 this.parent().el.setStyle('position', 'relative');
5146 this.footer.parentId = this.id;
5147 this.footer.onRender(this.el.select('tfoot tr td').first(), null);
5150 this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5152 this.store.on('load', this.onLoad, this);
5153 this.store.on('beforeload', this.onBeforeLoad, this);
5154 this.store.on('update', this.onUpdate, this);
5158 onMouseover : function(e, el)
5160 var cell = Roo.get(el);
5166 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5167 cell = cell.findParent('td', false, true);
5170 var row = cell.findParent('tr', false, true);
5171 var cellIndex = cell.dom.cellIndex;
5172 var rowIndex = row.dom.rowIndex - 1; // start from 0
5174 this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
5178 onMouseout : function(e, el)
5180 var cell = Roo.get(el);
5186 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5187 cell = cell.findParent('td', false, true);
5190 var row = cell.findParent('tr', false, true);
5191 var cellIndex = cell.dom.cellIndex;
5192 var rowIndex = row.dom.rowIndex - 1; // start from 0
5194 this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
5198 onClick : function(e, el)
5200 var cell = Roo.get(el);
5202 if(!cell || (!this.CellSelection && !this.RowSelection)){
5207 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5208 cell = cell.findParent('td', false, true);
5211 var row = cell.findParent('tr', false, true);
5212 var cellIndex = cell.dom.cellIndex;
5213 var rowIndex = row.dom.rowIndex - 1;
5215 if(this.CellSelection){
5216 this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
5219 if(this.RowSelection){
5220 this.fireEvent('rowclick', this, row, rowIndex, e);
5226 onDblClick : function(e,el)
5228 var cell = Roo.get(el);
5230 if(!cell || (!this.CellSelection && !this.RowSelection)){
5234 if(e.getTarget().nodeName.toLowerCase() != 'td'){
5235 cell = cell.findParent('td', false, true);
5238 var row = cell.findParent('tr', false, true);
5239 var cellIndex = cell.dom.cellIndex;
5240 var rowIndex = row.dom.rowIndex - 1;
5242 if(this.CellSelection){
5243 this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
5246 if(this.RowSelection){
5247 this.fireEvent('rowdblclick', this, row, rowIndex, e);
5251 sort : function(e,el)
5253 var col = Roo.get(el)
5255 if(!col.hasClass('sortable')){
5259 var sort = col.attr('sort');
5262 if(col.hasClass('glyphicon-arrow-up')){
5266 this.store.sortInfo = {field : sort, direction : dir};
5269 Roo.log("calling footer first");
5270 this.footer.onClick('first');
5273 this.store.load({ params : { start : 0 } });
5277 renderHeader : function()
5286 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5288 var config = cm.config[i];
5293 html: cm.getColumnHeader(i)
5296 if(typeof(config.hidden) != 'undefined' && config.hidden){
5297 c.style += ' display:none;';
5300 if(typeof(config.dataIndex) != 'undefined'){
5301 c.sort = config.dataIndex;
5304 if(typeof(config.sortable) != 'undefined' && config.sortable){
5308 if(typeof(config.align) != 'undefined' && config.align.length){
5309 c.style += ' text-align:' + config.align + ';';
5312 if(typeof(config.width) != 'undefined'){
5313 c.style += ' width:' + config.width + 'px;';
5322 renderBody : function()
5332 colspan : this.cm.getColumnCount()
5342 renderFooter : function()
5352 colspan : this.cm.getColumnCount()
5366 Roo.log('ds onload');
5371 var ds = this.store;
5373 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5374 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
5376 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
5377 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
5380 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
5381 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
5385 var tbody = this.mainBody;
5387 if(ds.getCount() > 0){
5388 ds.data.each(function(d,rowIndex){
5389 var row = this.renderRow(cm, ds, rowIndex);
5391 tbody.createChild(row);
5395 if(row.cellObjects.length){
5396 Roo.each(row.cellObjects, function(r){
5397 _this.renderCellObject(r);
5404 Roo.each(this.el.select('tbody td', true).elements, function(e){
5405 e.on('mouseover', _this.onMouseover, _this);
5408 Roo.each(this.el.select('tbody td', true).elements, function(e){
5409 e.on('mouseout', _this.onMouseout, _this);
5412 //if(this.loadMask){
5413 // this.maskEl.hide();
5418 onUpdate : function(ds,record)
5420 this.refreshRow(record);
5422 onRemove : function(ds, record, index, isUpdate){
5423 if(isUpdate !== true){
5424 this.fireEvent("beforerowremoved", this, index, record);
5426 var bt = this.mainBody.dom;
5428 bt.removeChild(bt.rows[index]);
5431 if(isUpdate !== true){
5432 //this.stripeRows(index);
5433 //this.syncRowHeights(index, index);
5435 this.fireEvent("rowremoved", this, index, record);
5440 refreshRow : function(record){
5441 var ds = this.store, index;
5442 if(typeof record == 'number'){
5444 record = ds.getAt(index);
5446 index = ds.indexOf(record);
5448 this.insertRow(ds, index, true);
5449 this.onRemove(ds, record, index+1, true);
5450 //this.syncRowHeights(index, index);
5452 this.fireEvent("rowupdated", this, index, record);
5455 insertRow : function(dm, rowIndex, isUpdate){
5458 this.fireEvent("beforerowsinserted", this, rowIndex);
5460 //var s = this.getScrollState();
5461 var row = this.renderRow(this.cm, this.store, rowIndex);
5462 // insert before rowIndex..
5463 var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
5467 if(row.cellObjects.length){
5468 Roo.each(row.cellObjects, function(r){
5469 _this.renderCellObject(r);
5474 this.fireEvent("rowsinserted", this, rowIndex);
5475 //this.syncRowHeights(firstRow, lastRow);
5476 //this.stripeRows(firstRow);
5483 getRowDom : function(rowIndex)
5485 // not sure if I need to check this.. but let's do it anyway..
5486 return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
5487 this.mainBody.dom.rows[rowIndex] : false
5489 // returns the object tree for a tr..
5492 renderRow : function(cm, ds, rowIndex) {
5494 var d = ds.getAt(rowIndex);
5501 var cellObjects = [];
5503 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
5504 var config = cm.config[i];
5506 var renderer = cm.getRenderer(i);
5510 if(typeof(renderer) !== 'undefined'){
5511 value = renderer(d.data[cm.getDataIndex(i)], false, d);
5513 // if object are returned, then they are expected to be Roo.bootstrap.Component instances
5514 // and are rendered into the cells after the row is rendered - using the id for the element.
5516 if(typeof(value) === 'object'){
5526 rowIndex : rowIndex,
5531 this.fireEvent('rowclass', this, rowcfg);
5535 cls : rowcfg.rowClass,
5537 html: (typeof(value) === 'object') ? '' : value
5544 if(typeof(config.hidden) != 'undefined' && config.hidden){
5545 td.style += ' display:none;';
5548 if(typeof(config.align) != 'undefined' && config.align.length){
5549 td.style += ' text-align:' + config.align + ';';
5552 if(typeof(config.width) != 'undefined'){
5553 td.style += ' width:' + config.width + 'px;';
5560 row.cellObjects = cellObjects;
5568 onBeforeLoad : function()
5570 //Roo.log('ds onBeforeLoad');
5574 //if(this.loadMask){
5575 // this.maskEl.show();
5581 this.el.select('tbody', true).first().dom.innerHTML = '';
5584 getSelectionModel : function(){
5586 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
5588 return this.selModel;
5591 * Render the Roo.bootstrap object from renderder
5593 renderCellObject : function(r)
5597 var t = r.cfg.render(r.container);
5600 Roo.each(r.cfg.cn, function(c){
5602 container: t.getChildContainer(),
5605 _this.renderCellObject(child);
5622 * @class Roo.bootstrap.TableCell
5623 * @extends Roo.bootstrap.Component
5624 * Bootstrap TableCell class
5625 * @cfg {String} html cell contain text
5626 * @cfg {String} cls cell class
5627 * @cfg {String} tag cell tag (td|th) default td
5628 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
5629 * @cfg {String} align Aligns the content in a cell
5630 * @cfg {String} axis Categorizes cells
5631 * @cfg {String} bgcolor Specifies the background color of a cell
5632 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5633 * @cfg {Number} colspan Specifies the number of columns a cell should span
5634 * @cfg {String} headers Specifies one or more header cells a cell is related to
5635 * @cfg {Number} height Sets the height of a cell
5636 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
5637 * @cfg {Number} rowspan Sets the number of rows a cell should span
5638 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
5639 * @cfg {String} valign Vertical aligns the content in a cell
5640 * @cfg {Number} width Specifies the width of a cell
5643 * Create a new TableCell
5644 * @param {Object} config The config object
5647 Roo.bootstrap.TableCell = function(config){
5648 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
5651 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
5671 getAutoCreate : function(){
5672 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
5692 cfg.align=this.align
5698 cfg.bgcolor=this.bgcolor
5701 cfg.charoff=this.charoff
5704 cfg.colspan=this.colspan
5707 cfg.headers=this.headers
5710 cfg.height=this.height
5713 cfg.nowrap=this.nowrap
5716 cfg.rowspan=this.rowspan
5719 cfg.scope=this.scope
5722 cfg.valign=this.valign
5725 cfg.width=this.width
5744 * @class Roo.bootstrap.TableRow
5745 * @extends Roo.bootstrap.Component
5746 * Bootstrap TableRow class
5747 * @cfg {String} cls row class
5748 * @cfg {String} align Aligns the content in a table row
5749 * @cfg {String} bgcolor Specifies a background color for a table row
5750 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
5751 * @cfg {String} valign Vertical aligns the content in a table row
5754 * Create a new TableRow
5755 * @param {Object} config The config object
5758 Roo.bootstrap.TableRow = function(config){
5759 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
5762 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
5770 getAutoCreate : function(){
5771 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
5781 cfg.align = this.align;
5784 cfg.bgcolor = this.bgcolor;
5787 cfg.charoff = this.charoff;
5790 cfg.valign = this.valign;
5808 * @class Roo.bootstrap.TableBody
5809 * @extends Roo.bootstrap.Component
5810 * Bootstrap TableBody class
5811 * @cfg {String} cls element class
5812 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
5813 * @cfg {String} align Aligns the content inside the element
5814 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
5815 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
5818 * Create a new TableBody
5819 * @param {Object} config The config object
5822 Roo.bootstrap.TableBody = function(config){
5823 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
5826 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
5834 getAutoCreate : function(){
5835 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
5849 cfg.align = this.align;
5852 cfg.charoff = this.charoff;
5855 cfg.valign = this.valign;
5862 // initEvents : function()
5869 // this.store = Roo.factory(this.store, Roo.data);
5870 // this.store.on('load', this.onLoad, this);
5872 // this.store.load();
5876 // onLoad: function ()
5878 // this.fireEvent('load', this);
5888 * Ext JS Library 1.1.1
5889 * Copyright(c) 2006-2007, Ext JS, LLC.
5891 * Originally Released Under LGPL - original licence link has changed is not relivant.
5894 * <script type="text/javascript">
5897 // as we use this in bootstrap.
5898 Roo.namespace('Roo.form');
5900 * @class Roo.form.Action
5901 * Internal Class used to handle form actions
5903 * @param {Roo.form.BasicForm} el The form element or its id
5904 * @param {Object} config Configuration options
5909 // define the action interface
5910 Roo.form.Action = function(form, options){
5912 this.options = options || {};
5915 * Client Validation Failed
5918 Roo.form.Action.CLIENT_INVALID = 'client';
5920 * Server Validation Failed
5923 Roo.form.Action.SERVER_INVALID = 'server';
5925 * Connect to Server Failed
5928 Roo.form.Action.CONNECT_FAILURE = 'connect';
5930 * Reading Data from Server Failed
5933 Roo.form.Action.LOAD_FAILURE = 'load';
5935 Roo.form.Action.prototype = {
5937 failureType : undefined,
5938 response : undefined,
5942 run : function(options){
5947 success : function(response){
5952 handleResponse : function(response){
5956 // default connection failure
5957 failure : function(response){
5959 this.response = response;
5960 this.failureType = Roo.form.Action.CONNECT_FAILURE;
5961 this.form.afterAction(this, false);
5964 processResponse : function(response){
5965 this.response = response;
5966 if(!response.responseText){
5969 this.result = this.handleResponse(response);
5973 // utility functions used internally
5974 getUrl : function(appendParams){
5975 var url = this.options.url || this.form.url || this.form.el.dom.action;
5977 var p = this.getParams();
5979 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
5985 getMethod : function(){
5986 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
5989 getParams : function(){
5990 var bp = this.form.baseParams;
5991 var p = this.options.params;
5993 if(typeof p == "object"){
5994 p = Roo.urlEncode(Roo.applyIf(p, bp));
5995 }else if(typeof p == 'string' && bp){
5996 p += '&' + Roo.urlEncode(bp);
5999 p = Roo.urlEncode(bp);
6004 createCallback : function(){
6006 success: this.success,
6007 failure: this.failure,
6009 timeout: (this.form.timeout*1000),
6010 upload: this.form.fileUpload ? this.success : undefined
6015 Roo.form.Action.Submit = function(form, options){
6016 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
6019 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
6022 haveProgress : false,
6023 uploadComplete : false,
6025 // uploadProgress indicator.
6026 uploadProgress : function()
6028 if (!this.form.progressUrl) {
6032 if (!this.haveProgress) {
6033 Roo.MessageBox.progress("Uploading", "Uploading");
6035 if (this.uploadComplete) {
6036 Roo.MessageBox.hide();
6040 this.haveProgress = true;
6042 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
6044 var c = new Roo.data.Connection();
6046 url : this.form.progressUrl,
6051 success : function(req){
6052 //console.log(data);
6056 rdata = Roo.decode(req.responseText)
6058 Roo.log("Invalid data from server..");
6062 if (!rdata || !rdata.success) {
6064 Roo.MessageBox.alert(Roo.encode(rdata));
6067 var data = rdata.data;
6069 if (this.uploadComplete) {
6070 Roo.MessageBox.hide();
6075 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
6076 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
6079 this.uploadProgress.defer(2000,this);
6082 failure: function(data) {
6083 Roo.log('progress url failed ');
6094 // run get Values on the form, so it syncs any secondary forms.
6095 this.form.getValues();
6097 var o = this.options;
6098 var method = this.getMethod();
6099 var isPost = method == 'POST';
6100 if(o.clientValidation === false || this.form.isValid()){
6102 if (this.form.progressUrl) {
6103 this.form.findField('UPLOAD_IDENTIFIER').setValue(
6104 (new Date() * 1) + '' + Math.random());
6109 Roo.Ajax.request(Roo.apply(this.createCallback(), {
6110 form:this.form.el.dom,
6111 url:this.getUrl(!isPost),
6113 params:isPost ? this.getParams() : null,
6114 isUpload: this.form.fileUpload
6117 this.uploadProgress();
6119 }else if (o.clientValidation !== false){ // client validation failed
6120 this.failureType = Roo.form.Action.CLIENT_INVALID;
6121 this.form.afterAction(this, false);
6125 success : function(response)
6127 this.uploadComplete= true;
6128 if (this.haveProgress) {
6129 Roo.MessageBox.hide();
6133 var result = this.processResponse(response);
6134 if(result === true || result.success){
6135 this.form.afterAction(this, true);
6139 this.form.markInvalid(result.errors);
6140 this.failureType = Roo.form.Action.SERVER_INVALID;
6142 this.form.afterAction(this, false);
6144 failure : function(response)
6146 this.uploadComplete= true;
6147 if (this.haveProgress) {
6148 Roo.MessageBox.hide();
6151 this.response = response;
6152 this.failureType = Roo.form.Action.CONNECT_FAILURE;
6153 this.form.afterAction(this, false);
6156 handleResponse : function(response){
6157 if(this.form.errorReader){
6158 var rs = this.form.errorReader.read(response);
6161 for(var i = 0, len = rs.records.length; i < len; i++) {
6162 var r = rs.records[i];
6166 if(errors.length < 1){
6170 success : rs.success,
6176 ret = Roo.decode(response.responseText);
6180 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
6190 Roo.form.Action.Load = function(form, options){
6191 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
6192 this.reader = this.form.reader;
6195 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
6200 Roo.Ajax.request(Roo.apply(
6201 this.createCallback(), {
6202 method:this.getMethod(),
6203 url:this.getUrl(false),
6204 params:this.getParams()
6208 success : function(response){
6210 var result = this.processResponse(response);
6211 if(result === true || !result.success || !result.data){
6212 this.failureType = Roo.form.Action.LOAD_FAILURE;
6213 this.form.afterAction(this, false);
6216 this.form.clearInvalid();
6217 this.form.setValues(result.data);
6218 this.form.afterAction(this, true);
6221 handleResponse : function(response){
6222 if(this.form.reader){
6223 var rs = this.form.reader.read(response);
6224 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
6226 success : rs.success,
6230 return Roo.decode(response.responseText);
6234 Roo.form.Action.ACTION_TYPES = {
6235 'load' : Roo.form.Action.Load,
6236 'submit' : Roo.form.Action.Submit
6245 * @class Roo.bootstrap.Form
6246 * @extends Roo.bootstrap.Component
6247 * Bootstrap Form class
6248 * @cfg {String} method GET | POST (default POST)
6249 * @cfg {String} labelAlign top | left (default top)
6250 * @cfg {String} align left | right - for navbars
6251 * @cfg {Boolean} loadMask load mask when submit (default true)
6256 * @param {Object} config The config object
6260 Roo.bootstrap.Form = function(config){
6261 Roo.bootstrap.Form.superclass.constructor.call(this, config);
6264 * @event clientvalidation
6265 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
6266 * @param {Form} this
6267 * @param {Boolean} valid true if the form has passed client-side validation
6269 clientvalidation: true,
6271 * @event beforeaction
6272 * Fires before any action is performed. Return false to cancel the action.
6273 * @param {Form} this
6274 * @param {Action} action The action to be performed
6278 * @event actionfailed
6279 * Fires when an action fails.
6280 * @param {Form} this
6281 * @param {Action} action The action that failed
6283 actionfailed : true,
6285 * @event actioncomplete
6286 * Fires when an action is completed.
6287 * @param {Form} this
6288 * @param {Action} action The action that completed
6290 actioncomplete : true
6295 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
6298 * @cfg {String} method
6299 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
6304 * The URL to use for form actions if one isn't supplied in the action options.
6307 * @cfg {Boolean} fileUpload
6308 * Set to true if this form is a file upload.
6312 * @cfg {Object} baseParams
6313 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
6317 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
6321 * @cfg {Sting} align (left|right) for navbar forms
6326 activeAction : null,
6329 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
6330 * element by passing it or its id or mask the form itself by passing in true.
6333 waitMsgTarget : false,
6337 getAutoCreate : function(){
6341 method : this.method || 'POST',
6342 id : this.id || Roo.id(),
6345 if (this.parent().xtype.match(/^Nav/)) {
6346 cfg.cls = 'navbar-form navbar-' + this.align;
6350 if (this.labelAlign == 'left' ) {
6351 cfg.cls += ' form-horizontal';
6357 initEvents : function()
6359 this.el.on('submit', this.onSubmit, this);
6360 // this was added as random key presses on the form where triggering form submit.
6361 this.el.on('keypress', function(e) {
6362 if (e.getCharCode() != 13) {
6365 // we might need to allow it for textareas.. and some other items.
6366 // check e.getTarget().
6368 if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
6372 Roo.log("keypress blocked");
6380 onSubmit : function(e){
6385 * Returns true if client-side validation on the form is successful.
6388 isValid : function(){
6389 var items = this.getItems();
6391 items.each(function(f){
6400 * Returns true if any fields in this form have changed since their original load.
6403 isDirty : function(){
6405 var items = this.getItems();
6406 items.each(function(f){
6416 * Performs a predefined action (submit or load) or custom actions you define on this form.
6417 * @param {String} actionName The name of the action type
6418 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
6419 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
6420 * accept other config options):
6422 Property Type Description
6423 ---------------- --------------- ----------------------------------------------------------------------------------
6424 url String The url for the action (defaults to the form's url)
6425 method String The form method to use (defaults to the form's method, or POST if not defined)
6426 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
6427 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
6428 validate the form on the client (defaults to false)
6430 * @return {BasicForm} this
6432 doAction : function(action, options){
6433 if(typeof action == 'string'){
6434 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
6436 if(this.fireEvent('beforeaction', this, action) !== false){
6437 this.beforeAction(action);
6438 action.run.defer(100, action);
6444 beforeAction : function(action){
6445 var o = action.options;
6448 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6450 // not really supported yet.. ??
6452 //if(this.waitMsgTarget === true){
6453 // this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
6454 //}else if(this.waitMsgTarget){
6455 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
6456 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
6458 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
6464 afterAction : function(action, success){
6465 this.activeAction = null;
6466 var o = action.options;
6468 //if(this.waitMsgTarget === true){
6470 //}else if(this.waitMsgTarget){
6471 // this.waitMsgTarget.unmask();
6473 // Roo.MessageBox.updateProgress(1);
6474 // Roo.MessageBox.hide();
6481 Roo.callback(o.success, o.scope, [this, action]);
6482 this.fireEvent('actioncomplete', this, action);
6486 // failure condition..
6487 // we have a scenario where updates need confirming.
6488 // eg. if a locking scenario exists..
6489 // we look for { errors : { needs_confirm : true }} in the response.
6491 (typeof(action.result) != 'undefined') &&
6492 (typeof(action.result.errors) != 'undefined') &&
6493 (typeof(action.result.errors.needs_confirm) != 'undefined')
6496 Roo.log("not supported yet");
6499 Roo.MessageBox.confirm(
6500 "Change requires confirmation",
6501 action.result.errorMsg,
6506 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
6516 Roo.callback(o.failure, o.scope, [this, action]);
6517 // show an error message if no failed handler is set..
6518 if (!this.hasListener('actionfailed')) {
6519 Roo.log("need to add dialog support");
6521 Roo.MessageBox.alert("Error",
6522 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
6523 action.result.errorMsg :
6524 "Saving Failed, please check your entries or try again"
6529 this.fireEvent('actionfailed', this, action);
6534 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
6535 * @param {String} id The value to search for
6538 findField : function(id){
6539 var items = this.getItems();
6540 var field = items.get(id);
6542 items.each(function(f){
6543 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
6550 return field || null;
6553 * Mark fields in this form invalid in bulk.
6554 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
6555 * @return {BasicForm} this
6557 markInvalid : function(errors){
6558 if(errors instanceof Array){
6559 for(var i = 0, len = errors.length; i < len; i++){
6560 var fieldError = errors[i];
6561 var f = this.findField(fieldError.id);
6563 f.markInvalid(fieldError.msg);
6569 if(typeof errors[id] != 'function' && (field = this.findField(id))){
6570 field.markInvalid(errors[id]);
6574 //Roo.each(this.childForms || [], function (f) {
6575 // f.markInvalid(errors);
6582 * Set values for fields in this form in bulk.
6583 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
6584 * @return {BasicForm} this
6586 setValues : function(values){
6587 if(values instanceof Array){ // array of objects
6588 for(var i = 0, len = values.length; i < len; i++){
6590 var f = this.findField(v.id);
6592 f.setValue(v.value);
6593 if(this.trackResetOnLoad){
6594 f.originalValue = f.getValue();
6598 }else{ // object hash
6601 if(typeof values[id] != 'function' && (field = this.findField(id))){
6603 if (field.setFromData &&
6605 field.displayField &&
6606 // combos' with local stores can
6607 // be queried via setValue()
6608 // to set their value..
6609 (field.store && !field.store.isLocal)
6613 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
6614 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
6615 field.setFromData(sd);
6618 field.setValue(values[id]);
6622 if(this.trackResetOnLoad){
6623 field.originalValue = field.getValue();
6629 //Roo.each(this.childForms || [], function (f) {
6630 // f.setValues(values);
6637 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
6638 * they are returned as an array.
6639 * @param {Boolean} asString
6642 getValues : function(asString){
6643 //if (this.childForms) {
6644 // copy values from the child forms
6645 // Roo.each(this.childForms, function (f) {
6646 // this.setValues(f.getValues());
6652 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
6653 if(asString === true){
6656 return Roo.urlDecode(fs);
6660 * Returns the fields in this form as an object with key/value pairs.
6661 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
6664 getFieldValues : function(with_hidden)
6666 var items = this.getItems();
6668 items.each(function(f){
6672 var v = f.getValue();
6673 if (f.inputType =='radio') {
6674 if (typeof(ret[f.getName()]) == 'undefined') {
6675 ret[f.getName()] = ''; // empty..
6678 if (!f.el.dom.checked) {
6686 // not sure if this supported any more..
6687 if ((typeof(v) == 'object') && f.getRawValue) {
6688 v = f.getRawValue() ; // dates..
6690 // combo boxes where name != hiddenName...
6691 if (f.name != f.getName()) {
6692 ret[f.name] = f.getRawValue();
6694 ret[f.getName()] = v;
6701 * Clears all invalid messages in this form.
6702 * @return {BasicForm} this
6704 clearInvalid : function(){
6705 var items = this.getItems();
6707 items.each(function(f){
6718 * @return {BasicForm} this
6721 var items = this.getItems();
6722 items.each(function(f){
6726 Roo.each(this.childForms || [], function (f) {
6733 getItems : function()
6735 var r=new Roo.util.MixedCollection(false, function(o){
6736 return o.id || (o.id = Roo.id());
6738 var iter = function(el) {
6745 Roo.each(el.items,function(e) {
6764 * Ext JS Library 1.1.1
6765 * Copyright(c) 2006-2007, Ext JS, LLC.
6767 * Originally Released Under LGPL - original licence link has changed is not relivant.
6770 * <script type="text/javascript">
6773 * @class Roo.form.VTypes
6774 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
6777 Roo.form.VTypes = function(){
6778 // closure these in so they are only created once.
6779 var alpha = /^[a-zA-Z_]+$/;
6780 var alphanum = /^[a-zA-Z0-9_]+$/;
6781 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
6782 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
6784 // All these messages and functions are configurable
6787 * The function used to validate email addresses
6788 * @param {String} value The email address
6790 'email' : function(v){
6791 return email.test(v);
6794 * The error text to display when the email validation function returns false
6797 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
6799 * The keystroke filter mask to be applied on email input
6802 'emailMask' : /[a-z0-9_\.\-@]/i,
6805 * The function used to validate URLs
6806 * @param {String} value The URL
6808 'url' : function(v){
6812 * The error text to display when the url validation function returns false
6815 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
6818 * The function used to validate alpha values
6819 * @param {String} value The value
6821 'alpha' : function(v){
6822 return alpha.test(v);
6825 * The error text to display when the alpha validation function returns false
6828 'alphaText' : 'This field should only contain letters and _',
6830 * The keystroke filter mask to be applied on alpha input
6833 'alphaMask' : /[a-z_]/i,
6836 * The function used to validate alphanumeric values
6837 * @param {String} value The value
6839 'alphanum' : function(v){
6840 return alphanum.test(v);
6843 * The error text to display when the alphanumeric validation function returns false
6846 'alphanumText' : 'This field should only contain letters, numbers and _',
6848 * The keystroke filter mask to be applied on alphanumeric input
6851 'alphanumMask' : /[a-z0-9_]/i
6861 * @class Roo.bootstrap.Input
6862 * @extends Roo.bootstrap.Component
6863 * Bootstrap Input class
6864 * @cfg {Boolean} disabled is it disabled
6865 * @cfg {String} fieldLabel - the label associated
6866 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
6867 * @cfg {String} name name of the input
6868 * @cfg {string} fieldLabel - the label associated
6869 * @cfg {string} inputType - input / file submit ...
6870 * @cfg {string} placeholder - placeholder to put in text.
6871 * @cfg {string} before - input group add on before
6872 * @cfg {string} after - input group add on after
6873 * @cfg {string} size - (lg|sm) or leave empty..
6874 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
6875 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
6876 * @cfg {Number} md colspan out of 12 for computer-sized screens
6877 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
6878 * @cfg {string} value default value of the input
6879 * @cfg {Number} labelWidth set the width of label (0-12)
6880 * @cfg {String} labelAlign (top|left)
6881 * @cfg {Boolean} readOnly Specifies that the field should be read-only
6882 * @cfg {String} align (left|center|right) Default left
6886 * Create a new Input
6887 * @param {Object} config The config object
6890 Roo.bootstrap.Input = function(config){
6891 Roo.bootstrap.Input.superclass.constructor.call(this, config);
6896 * Fires when this field receives input focus.
6897 * @param {Roo.form.Field} this
6902 * Fires when this field loses input focus.
6903 * @param {Roo.form.Field} this
6908 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
6909 * {@link Roo.EventObject#getKey} to determine which key was pressed.
6910 * @param {Roo.form.Field} this
6911 * @param {Roo.EventObject} e The event object
6916 * Fires just before the field blurs if the field value has changed.
6917 * @param {Roo.form.Field} this
6918 * @param {Mixed} newValue The new value
6919 * @param {Mixed} oldValue The original value
6924 * Fires after the field has been marked as invalid.
6925 * @param {Roo.form.Field} this
6926 * @param {String} msg The validation message
6931 * Fires after the field has been validated with no errors.
6932 * @param {Roo.form.Field} this
6937 * Fires after the key up
6938 * @param {Roo.form.Field} this
6939 * @param {Roo.EventObject} e The event Object
6945 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
6947 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
6948 automatic validation (defaults to "keyup").
6950 validationEvent : "keyup",
6952 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
6954 validateOnBlur : true,
6956 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
6958 validationDelay : 250,
6960 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
6962 focusClass : "x-form-focus", // not needed???
6966 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
6968 invalidClass : "has-error",
6971 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
6973 selectOnFocus : false,
6976 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
6980 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
6985 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
6987 disableKeyFilter : false,
6990 * @cfg {Boolean} disabled True to disable the field (defaults to false).
6994 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
6998 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
7000 blankText : "This field is required",
7003 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
7007 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
7009 maxLength : Number.MAX_VALUE,
7011 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
7013 minLengthText : "The minimum length for this field is {0}",
7015 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
7017 maxLengthText : "The maximum length for this field is {0}",
7021 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
7022 * If available, this function will be called only after the basic validators all return true, and will be passed the
7023 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
7027 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
7028 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
7029 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
7033 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
7056 formatedValue : false,
7058 parentLabelAlign : function()
7061 while (parent.parent()) {
7062 parent = parent.parent();
7063 if (typeof(parent.labelAlign) !='undefined') {
7064 return parent.labelAlign;
7071 getAutoCreate : function(){
7073 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7079 if(this.inputType != 'hidden'){
7080 cfg.cls = 'form-group' //input-group
7086 type : this.inputType,
7088 cls : 'form-control',
7089 placeholder : this.placeholder || ''
7094 input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
7097 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7098 input.maxLength = this.maxLength;
7101 if (this.disabled) {
7102 input.disabled=true;
7105 if (this.readOnly) {
7106 input.readonly=true;
7110 input.name = this.name;
7113 input.cls += ' input-' + this.size;
7116 ['xs','sm','md','lg'].map(function(size){
7117 if (settings[size]) {
7118 cfg.cls += ' col-' + size + '-' + settings[size];
7122 var inputblock = input;
7124 if (this.before || this.after) {
7127 cls : 'input-group',
7130 if (this.before && typeof(this.before) == 'string') {
7132 inputblock.cn.push({
7134 cls : 'roo-input-before input-group-addon',
7138 if (this.before && typeof(this.before) == 'object') {
7139 this.before = Roo.factory(this.before);
7140 Roo.log(this.before);
7141 inputblock.cn.push({
7143 cls : 'roo-input-before input-group-' +
7144 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7148 inputblock.cn.push(input);
7150 if (this.after && typeof(this.after) == 'string') {
7151 inputblock.cn.push({
7153 cls : 'roo-input-after input-group-addon',
7157 if (this.after && typeof(this.after) == 'object') {
7158 this.after = Roo.factory(this.after);
7159 Roo.log(this.after);
7160 inputblock.cn.push({
7162 cls : 'roo-input-after input-group-' +
7163 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
7168 if (align ==='left' && this.fieldLabel.length) {
7169 Roo.log("left and has label");
7175 cls : 'control-label col-sm-' + this.labelWidth,
7176 html : this.fieldLabel
7180 cls : "col-sm-" + (12 - this.labelWidth),
7187 } else if ( this.fieldLabel.length) {
7193 //cls : 'input-group-addon',
7194 html : this.fieldLabel
7204 Roo.log(" no label && no align");
7213 Roo.log('input-parentType: ' + this.parentType);
7215 if (this.parentType === 'Navbar' && this.parent().bar) {
7216 cfg.cls += ' navbar-form';
7224 * return the real input element.
7226 inputEl: function ()
7228 return this.el.select('input.form-control',true).first();
7231 tooltipEl : function()
7233 return this.inputEl();
7236 setDisabled : function(v)
7238 var i = this.inputEl().dom;
7240 i.removeAttribute('disabled');
7244 i.setAttribute('disabled','true');
7246 initEvents : function()
7249 this.inputEl().on("keydown" , this.fireKey, this);
7250 this.inputEl().on("focus", this.onFocus, this);
7251 this.inputEl().on("blur", this.onBlur, this);
7253 this.inputEl().relayEvent('keyup', this);
7255 // reference to original value for reset
7256 this.originalValue = this.getValue();
7257 //Roo.form.TextField.superclass.initEvents.call(this);
7258 if(this.validationEvent == 'keyup'){
7259 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
7260 this.inputEl().on('keyup', this.filterValidation, this);
7262 else if(this.validationEvent !== false){
7263 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
7266 if(this.selectOnFocus){
7267 this.on("focus", this.preFocus, this);
7270 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
7271 this.inputEl().on("keypress", this.filterKeys, this);
7274 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
7275 this.el.on("click", this.autoSize, this);
7278 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
7279 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
7282 if (typeof(this.before) == 'object') {
7283 this.before.render(this.el.select('.roo-input-before',true).first());
7285 if (typeof(this.after) == 'object') {
7286 this.after.render(this.el.select('.roo-input-after',true).first());
7291 filterValidation : function(e){
7292 if(!e.isNavKeyPress()){
7293 this.validationTask.delay(this.validationDelay);
7297 * Validates the field value
7298 * @return {Boolean} True if the value is valid, else false
7300 validate : function(){
7301 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
7302 if(this.disabled || this.validateValue(this.getRawValue())){
7303 this.clearInvalid();
7311 * Validates a value according to the field's validation rules and marks the field as invalid
7312 * if the validation fails
7313 * @param {Mixed} value The value to validate
7314 * @return {Boolean} True if the value is valid, else false
7316 validateValue : function(value){
7317 if(value.length < 1) { // if it's blank
7318 if(this.allowBlank){
7319 this.clearInvalid();
7322 this.markInvalid(this.blankText);
7326 if(value.length < this.minLength){
7327 this.markInvalid(String.format(this.minLengthText, this.minLength));
7330 if(value.length > this.maxLength){
7331 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
7335 var vt = Roo.form.VTypes;
7336 if(!vt[this.vtype](value, this)){
7337 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
7341 if(typeof this.validator == "function"){
7342 var msg = this.validator(value);
7344 this.markInvalid(msg);
7348 if(this.regex && !this.regex.test(value)){
7349 this.markInvalid(this.regexText);
7358 fireKey : function(e){
7359 //Roo.log('field ' + e.getKey());
7360 if(e.isNavKeyPress()){
7361 this.fireEvent("specialkey", this, e);
7364 focus : function (selectText){
7366 this.inputEl().focus();
7367 if(selectText === true){
7368 this.inputEl().dom.select();
7374 onFocus : function(){
7375 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7376 // this.el.addClass(this.focusClass);
7379 this.hasFocus = true;
7380 this.startValue = this.getValue();
7381 this.fireEvent("focus", this);
7385 beforeBlur : Roo.emptyFn,
7389 onBlur : function(){
7391 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
7392 //this.el.removeClass(this.focusClass);
7394 this.hasFocus = false;
7395 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
7398 var v = this.getValue();
7399 if(String(v) !== String(this.startValue)){
7400 this.fireEvent('change', this, v, this.startValue);
7402 this.fireEvent("blur", this);
7406 * Resets the current field value to the originally loaded value and clears any validation messages
7409 this.setValue(this.originalValue);
7410 this.clearInvalid();
7413 * Returns the name of the field
7414 * @return {Mixed} name The name field
7416 getName: function(){
7420 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
7421 * @return {Mixed} value The field value
7423 getValue : function(){
7425 var v = this.inputEl().getValue();
7430 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
7431 * @return {Mixed} value The field value
7433 getRawValue : function(){
7434 var v = this.inputEl().getValue();
7440 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
7441 * @param {Mixed} value The value to set
7443 setRawValue : function(v){
7444 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7447 selectText : function(start, end){
7448 var v = this.getRawValue();
7450 start = start === undefined ? 0 : start;
7451 end = end === undefined ? v.length : end;
7452 var d = this.inputEl().dom;
7453 if(d.setSelectionRange){
7454 d.setSelectionRange(start, end);
7455 }else if(d.createTextRange){
7456 var range = d.createTextRange();
7457 range.moveStart("character", start);
7458 range.moveEnd("character", v.length-end);
7465 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
7466 * @param {Mixed} value The value to set
7468 setValue : function(v){
7471 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
7477 processValue : function(value){
7478 if(this.stripCharsRe){
7479 var newValue = value.replace(this.stripCharsRe, '');
7480 if(newValue !== value){
7481 this.setRawValue(newValue);
7488 preFocus : function(){
7490 if(this.selectOnFocus){
7491 this.inputEl().dom.select();
7494 filterKeys : function(e){
7496 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
7499 var c = e.getCharCode(), cc = String.fromCharCode(c);
7500 if(Roo.isIE && (e.isSpecialKey() || !cc)){
7503 if(!this.maskRe.test(cc)){
7508 * Clear any invalid styles/messages for this field
7510 clearInvalid : function(){
7512 if(!this.el || this.preventMark){ // not rendered
7515 this.el.removeClass(this.invalidClass);
7517 switch(this.msgTarget){
7519 this.el.dom.qtip = '';
7522 this.el.dom.title = '';
7526 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
7531 this.errorIcon.dom.qtip = '';
7532 this.errorIcon.hide();
7533 this.un('resize', this.alignErrorIcon, this);
7537 var t = Roo.getDom(this.msgTarget);
7539 t.style.display = 'none';
7543 this.fireEvent('valid', this);
7546 * Mark this field as invalid
7547 * @param {String} msg The validation message
7549 markInvalid : function(msg){
7550 if(!this.el || this.preventMark){ // not rendered
7553 this.el.addClass(this.invalidClass);
7555 msg = msg || this.invalidText;
7556 switch(this.msgTarget){
7558 this.el.dom.qtip = msg;
7559 this.el.dom.qclass = 'x-form-invalid-tip';
7560 if(Roo.QuickTips){ // fix for floating editors interacting with DND
7561 Roo.QuickTips.enable();
7565 this.el.dom.title = msg;
7569 var elp = this.el.findParent('.x-form-element', 5, true);
7570 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
7571 this.errorEl.setWidth(elp.getWidth(true)-20);
7573 this.errorEl.update(msg);
7574 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
7577 if(!this.errorIcon){
7578 var elp = this.el.findParent('.x-form-element', 5, true);
7579 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
7581 this.alignErrorIcon();
7582 this.errorIcon.dom.qtip = msg;
7583 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
7584 this.errorIcon.show();
7585 this.on('resize', this.alignErrorIcon, this);
7588 var t = Roo.getDom(this.msgTarget);
7590 t.style.display = this.msgDisplay;
7594 this.fireEvent('invalid', this, msg);
7597 SafariOnKeyDown : function(event)
7599 // this is a workaround for a password hang bug on chrome/ webkit.
7601 var isSelectAll = false;
7603 if(this.inputEl().dom.selectionEnd > 0){
7604 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
7606 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
7607 event.preventDefault();
7612 if(isSelectAll){ // backspace and delete key
7614 event.preventDefault();
7615 // this is very hacky as keydown always get's upper case.
7617 var cc = String.fromCharCode(event.getCharCode());
7618 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
7622 adjustWidth : function(tag, w){
7623 tag = tag.toLowerCase();
7624 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
7625 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
7629 if(tag == 'textarea'){
7632 }else if(Roo.isOpera){
7636 if(tag == 'textarea'){
7655 * @class Roo.bootstrap.TextArea
7656 * @extends Roo.bootstrap.Input
7657 * Bootstrap TextArea class
7658 * @cfg {Number} cols Specifies the visible width of a text area
7659 * @cfg {Number} rows Specifies the visible number of lines in a text area
7660 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
7661 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
7662 * @cfg {string} html text
7665 * Create a new TextArea
7666 * @param {Object} config The config object
7669 Roo.bootstrap.TextArea = function(config){
7670 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
7674 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
7684 getAutoCreate : function(){
7686 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
7697 value : this.value || '',
7698 html: this.html || '',
7699 cls : 'form-control',
7700 placeholder : this.placeholder || ''
7704 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
7705 input.maxLength = this.maxLength;
7709 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
7713 input.cols = this.cols;
7716 if (this.readOnly) {
7717 input.readonly = true;
7721 input.name = this.name;
7725 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
7729 ['xs','sm','md','lg'].map(function(size){
7730 if (settings[size]) {
7731 cfg.cls += ' col-' + size + '-' + settings[size];
7735 var inputblock = input;
7737 if (this.before || this.after) {
7740 cls : 'input-group',
7744 inputblock.cn.push({
7746 cls : 'input-group-addon',
7750 inputblock.cn.push(input);
7752 inputblock.cn.push({
7754 cls : 'input-group-addon',
7761 if (align ==='left' && this.fieldLabel.length) {
7762 Roo.log("left and has label");
7768 cls : 'control-label col-sm-' + this.labelWidth,
7769 html : this.fieldLabel
7773 cls : "col-sm-" + (12 - this.labelWidth),
7780 } else if ( this.fieldLabel.length) {
7786 //cls : 'input-group-addon',
7787 html : this.fieldLabel
7797 Roo.log(" no label && no align");
7807 if (this.disabled) {
7808 input.disabled=true;
7815 * return the real textarea element.
7817 inputEl: function ()
7819 return this.el.select('textarea.form-control',true).first();
7827 * trigger field - base class for combo..
7832 * @class Roo.bootstrap.TriggerField
7833 * @extends Roo.bootstrap.Input
7834 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
7835 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
7836 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
7837 * for which you can provide a custom implementation. For example:
7839 var trigger = new Roo.bootstrap.TriggerField();
7840 trigger.onTriggerClick = myTriggerFn;
7841 trigger.applyTo('my-field');
7844 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
7845 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
7846 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
7847 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
7849 * Create a new TriggerField.
7850 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
7851 * to the base TextField)
7853 Roo.bootstrap.TriggerField = function(config){
7854 this.mimicing = false;
7855 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
7858 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
7860 * @cfg {String} triggerClass A CSS class to apply to the trigger
7863 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
7867 /** @cfg {Boolean} grow @hide */
7868 /** @cfg {Number} growMin @hide */
7869 /** @cfg {Number} growMax @hide */
7875 autoSize: Roo.emptyFn,
7882 actionMode : 'wrap',
7886 getAutoCreate : function(){
7888 var align = this.labelAlign || this.parentLabelAlign();
7893 cls: 'form-group' //input-group
7900 type : this.inputType,
7901 cls : 'form-control',
7902 autocomplete: 'off',
7903 placeholder : this.placeholder || ''
7907 input.name = this.name;
7910 input.cls += ' input-' + this.size;
7913 if (this.disabled) {
7914 input.disabled=true;
7917 var inputblock = input;
7919 if (this.before || this.after) {
7922 cls : 'input-group',
7926 inputblock.cn.push({
7928 cls : 'input-group-addon',
7932 inputblock.cn.push(input);
7934 inputblock.cn.push({
7936 cls : 'input-group-addon',
7949 cls: 'form-hidden-field'
7957 Roo.log('multiple');
7965 cls: 'form-hidden-field'
7969 cls: 'select2-choices',
7973 cls: 'select2-search-field',
7986 cls: 'select2-container input-group',
7991 // cls: 'typeahead typeahead-long dropdown-menu',
7992 // style: 'display:none'
7997 if(!this.multiple && this.showToggleBtn){
8000 cls : 'input-group-addon btn dropdown-toggle',
8008 cls: 'combobox-clear',
8022 combobox.cls += ' select2-container-multi';
8025 if (align ==='left' && this.fieldLabel.length) {
8027 Roo.log("left and has label");
8033 cls : 'control-label col-sm-' + this.labelWidth,
8034 html : this.fieldLabel
8038 cls : "col-sm-" + (12 - this.labelWidth),
8045 } else if ( this.fieldLabel.length) {
8051 //cls : 'input-group-addon',
8052 html : this.fieldLabel
8062 Roo.log(" no label && no align");
8069 ['xs','sm','md','lg'].map(function(size){
8070 if (settings[size]) {
8071 cfg.cls += ' col-' + size + '-' + settings[size];
8082 onResize : function(w, h){
8083 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
8084 // if(typeof w == 'number'){
8085 // var x = w - this.trigger.getWidth();
8086 // this.inputEl().setWidth(this.adjustWidth('input', x));
8087 // this.trigger.setStyle('left', x+'px');
8092 adjustSize : Roo.BoxComponent.prototype.adjustSize,
8095 getResizeEl : function(){
8096 return this.inputEl();
8100 getPositionEl : function(){
8101 return this.inputEl();
8105 alignErrorIcon : function(){
8106 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
8110 initEvents : function(){
8114 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
8115 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
8116 if(!this.multiple && this.showToggleBtn){
8117 this.trigger = this.el.select('span.dropdown-toggle',true).first();
8118 if(this.hideTrigger){
8119 this.trigger.setDisplayed(false);
8121 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
8125 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
8128 //this.trigger.addClassOnOver('x-form-trigger-over');
8129 //this.trigger.addClassOnClick('x-form-trigger-click');
8132 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
8136 createList : function()
8138 this.list = Roo.get(document.body).createChild({
8140 cls: 'typeahead typeahead-long dropdown-menu',
8141 style: 'display:none'
8144 this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
8149 initTrigger : function(){
8154 onDestroy : function(){
8156 this.trigger.removeAllListeners();
8157 // this.trigger.remove();
8160 // this.wrap.remove();
8162 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
8166 onFocus : function(){
8167 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
8170 this.wrap.addClass('x-trigger-wrap-focus');
8171 this.mimicing = true;
8172 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
8173 if(this.monitorTab){
8174 this.el.on("keydown", this.checkTab, this);
8181 checkTab : function(e){
8182 if(e.getKey() == e.TAB){
8188 onBlur : function(){
8193 mimicBlur : function(e, t){
8195 if(!this.wrap.contains(t) && this.validateBlur()){
8202 triggerBlur : function(){
8203 this.mimicing = false;
8204 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
8205 if(this.monitorTab){
8206 this.el.un("keydown", this.checkTab, this);
8208 //this.wrap.removeClass('x-trigger-wrap-focus');
8209 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
8213 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
8214 validateBlur : function(e, t){
8219 onDisable : function(){
8220 this.inputEl().dom.disabled = true;
8221 //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
8223 // this.wrap.addClass('x-item-disabled');
8228 onEnable : function(){
8229 this.inputEl().dom.disabled = false;
8230 //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
8232 // this.el.removeClass('x-item-disabled');
8237 onShow : function(){
8238 var ae = this.getActionEl();
8241 ae.dom.style.display = '';
8242 ae.dom.style.visibility = 'visible';
8248 onHide : function(){
8249 var ae = this.getActionEl();
8250 ae.dom.style.display = 'none';
8254 * The function that should handle the trigger's click event. This method does nothing by default until overridden
8255 * by an implementing function.
8257 * @param {EventObject} e
8259 onTriggerClick : Roo.emptyFn
8263 * Ext JS Library 1.1.1
8264 * Copyright(c) 2006-2007, Ext JS, LLC.
8266 * Originally Released Under LGPL - original licence link has changed is not relivant.
8269 * <script type="text/javascript">
8274 * @class Roo.data.SortTypes
8276 * Defines the default sorting (casting?) comparison functions used when sorting data.
8278 Roo.data.SortTypes = {
8280 * Default sort that does nothing
8281 * @param {Mixed} s The value being converted
8282 * @return {Mixed} The comparison value
8289 * The regular expression used to strip tags
8293 stripTagsRE : /<\/?[^>]+>/gi,
8296 * Strips all HTML tags to sort on text only
8297 * @param {Mixed} s The value being converted
8298 * @return {String} The comparison value
8300 asText : function(s){
8301 return String(s).replace(this.stripTagsRE, "");
8305 * Strips all HTML tags to sort on text only - Case insensitive
8306 * @param {Mixed} s The value being converted
8307 * @return {String} The comparison value
8309 asUCText : function(s){
8310 return String(s).toUpperCase().replace(this.stripTagsRE, "");
8314 * Case insensitive string
8315 * @param {Mixed} s The value being converted
8316 * @return {String} The comparison value
8318 asUCString : function(s) {
8319 return String(s).toUpperCase();
8324 * @param {Mixed} s The value being converted
8325 * @return {Number} The comparison value
8327 asDate : function(s) {
8331 if(s instanceof Date){
8334 return Date.parse(String(s));
8339 * @param {Mixed} s The value being converted
8340 * @return {Float} The comparison value
8342 asFloat : function(s) {
8343 var val = parseFloat(String(s).replace(/,/g, ""));
8344 if(isNaN(val)) val = 0;
8350 * @param {Mixed} s The value being converted
8351 * @return {Number} The comparison value
8353 asInt : function(s) {
8354 var val = parseInt(String(s).replace(/,/g, ""));
8355 if(isNaN(val)) val = 0;
8360 * Ext JS Library 1.1.1
8361 * Copyright(c) 2006-2007, Ext JS, LLC.
8363 * Originally Released Under LGPL - original licence link has changed is not relivant.
8366 * <script type="text/javascript">
8370 * @class Roo.data.Record
8371 * Instances of this class encapsulate both record <em>definition</em> information, and record
8372 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
8373 * to access Records cached in an {@link Roo.data.Store} object.<br>
8375 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
8376 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
8379 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
8381 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
8382 * {@link #create}. The parameters are the same.
8383 * @param {Array} data An associative Array of data values keyed by the field name.
8384 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
8385 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
8386 * not specified an integer id is generated.
8388 Roo.data.Record = function(data, id){
8389 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
8394 * Generate a constructor for a specific record layout.
8395 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
8396 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
8397 * Each field definition object may contain the following properties: <ul>
8398 * <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,
8399 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
8400 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
8401 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
8402 * is being used, then this is a string containing the javascript expression to reference the data relative to
8403 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
8404 * to the data item relative to the record element. If the mapping expression is the same as the field name,
8405 * this may be omitted.</p></li>
8406 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
8407 * <ul><li>auto (Default, implies no conversion)</li>
8412 * <li>date</li></ul></p></li>
8413 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
8414 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
8415 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
8416 * by the Reader into an object that will be stored in the Record. It is passed the
8417 * following parameters:<ul>
8418 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
8420 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
8422 * <br>usage:<br><pre><code>
8423 var TopicRecord = Roo.data.Record.create(
8424 {name: 'title', mapping: 'topic_title'},
8425 {name: 'author', mapping: 'username'},
8426 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
8427 {name: 'lastPost', mapping: 'post_time', type: 'date'},
8428 {name: 'lastPoster', mapping: 'user2'},
8429 {name: 'excerpt', mapping: 'post_text'}
8432 var myNewRecord = new TopicRecord({
8433 title: 'Do my job please',
8436 lastPost: new Date(),
8437 lastPoster: 'Animal',
8438 excerpt: 'No way dude!'
8440 myStore.add(myNewRecord);
8445 Roo.data.Record.create = function(o){
8447 f.superclass.constructor.apply(this, arguments);
8449 Roo.extend(f, Roo.data.Record);
8450 var p = f.prototype;
8451 p.fields = new Roo.util.MixedCollection(false, function(field){
8454 for(var i = 0, len = o.length; i < len; i++){
8455 p.fields.add(new Roo.data.Field(o[i]));
8457 f.getField = function(name){
8458 return p.fields.get(name);
8463 Roo.data.Record.AUTO_ID = 1000;
8464 Roo.data.Record.EDIT = 'edit';
8465 Roo.data.Record.REJECT = 'reject';
8466 Roo.data.Record.COMMIT = 'commit';
8468 Roo.data.Record.prototype = {
8470 * Readonly flag - true if this record has been modified.
8479 join : function(store){
8484 * Set the named field to the specified value.
8485 * @param {String} name The name of the field to set.
8486 * @param {Object} value The value to set the field to.
8488 set : function(name, value){
8489 if(this.data[name] == value){
8496 if(typeof this.modified[name] == 'undefined'){
8497 this.modified[name] = this.data[name];
8499 this.data[name] = value;
8500 if(!this.editing && this.store){
8501 this.store.afterEdit(this);
8506 * Get the value of the named field.
8507 * @param {String} name The name of the field to get the value of.
8508 * @return {Object} The value of the field.
8510 get : function(name){
8511 return this.data[name];
8515 beginEdit : function(){
8516 this.editing = true;
8521 cancelEdit : function(){
8522 this.editing = false;
8523 delete this.modified;
8527 endEdit : function(){
8528 this.editing = false;
8529 if(this.dirty && this.store){
8530 this.store.afterEdit(this);
8535 * Usually called by the {@link Roo.data.Store} which owns the Record.
8536 * Rejects all changes made to the Record since either creation, or the last commit operation.
8537 * Modified fields are reverted to their original values.
8539 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8540 * of reject operations.
8542 reject : function(){
8543 var m = this.modified;
8545 if(typeof m[n] != "function"){
8546 this.data[n] = m[n];
8550 delete this.modified;
8551 this.editing = false;
8553 this.store.afterReject(this);
8558 * Usually called by the {@link Roo.data.Store} which owns the Record.
8559 * Commits all changes made to the Record since either creation, or the last commit operation.
8561 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
8562 * of commit operations.
8564 commit : function(){
8566 delete this.modified;
8567 this.editing = false;
8569 this.store.afterCommit(this);
8574 hasError : function(){
8575 return this.error != null;
8579 clearError : function(){
8584 * Creates a copy of this record.
8585 * @param {String} id (optional) A new record id if you don't want to use this record's id
8588 copy : function(newId) {
8589 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
8593 * Ext JS Library 1.1.1
8594 * Copyright(c) 2006-2007, Ext JS, LLC.
8596 * Originally Released Under LGPL - original licence link has changed is not relivant.
8599 * <script type="text/javascript">
8605 * @class Roo.data.Store
8606 * @extends Roo.util.Observable
8607 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
8608 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
8610 * 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
8611 * has no knowledge of the format of the data returned by the Proxy.<br>
8613 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
8614 * instances from the data object. These records are cached and made available through accessor functions.
8616 * Creates a new Store.
8617 * @param {Object} config A config object containing the objects needed for the Store to access data,
8618 * and read the data into Records.
8620 Roo.data.Store = function(config){
8621 this.data = new Roo.util.MixedCollection(false);
8622 this.data.getKey = function(o){
8625 this.baseParams = {};
8632 "multisort" : "_multisort"
8635 if(config && config.data){
8636 this.inlineData = config.data;
8640 Roo.apply(this, config);
8642 if(this.reader){ // reader passed
8643 this.reader = Roo.factory(this.reader, Roo.data);
8644 this.reader.xmodule = this.xmodule || false;
8645 if(!this.recordType){
8646 this.recordType = this.reader.recordType;
8648 if(this.reader.onMetaChange){
8649 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
8653 if(this.recordType){
8654 this.fields = this.recordType.prototype.fields;
8660 * @event datachanged
8661 * Fires when the data cache has changed, and a widget which is using this Store
8662 * as a Record cache should refresh its view.
8663 * @param {Store} this
8668 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
8669 * @param {Store} this
8670 * @param {Object} meta The JSON metadata
8675 * Fires when Records have been added to the Store
8676 * @param {Store} this
8677 * @param {Roo.data.Record[]} records The array of Records added
8678 * @param {Number} index The index at which the record(s) were added
8683 * Fires when a Record has been removed from the Store
8684 * @param {Store} this
8685 * @param {Roo.data.Record} record The Record that was removed
8686 * @param {Number} index The index at which the record was removed
8691 * Fires when a Record has been updated
8692 * @param {Store} this
8693 * @param {Roo.data.Record} record The Record that was updated
8694 * @param {String} operation The update operation being performed. Value may be one of:
8696 Roo.data.Record.EDIT
8697 Roo.data.Record.REJECT
8698 Roo.data.Record.COMMIT
8704 * Fires when the data cache has been cleared.
8705 * @param {Store} this
8710 * Fires before a request is made for a new data object. If the beforeload handler returns false
8711 * the load action will be canceled.
8712 * @param {Store} this
8713 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8717 * @event beforeloadadd
8718 * Fires after a new set of Records has been loaded.
8719 * @param {Store} this
8720 * @param {Roo.data.Record[]} records The Records that were loaded
8721 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8723 beforeloadadd : true,
8726 * Fires after a new set of Records has been loaded, before they are added to the store.
8727 * @param {Store} this
8728 * @param {Roo.data.Record[]} records The Records that were loaded
8729 * @param {Object} options The loading options that were specified (see {@link #load} for details)
8730 * @params {Object} return from reader
8734 * @event loadexception
8735 * Fires if an exception occurs in the Proxy during loading.
8736 * Called with the signature of the Proxy's "loadexception" event.
8737 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
8740 * @param {Object} return from JsonData.reader() - success, totalRecords, records
8741 * @param {Object} load options
8742 * @param {Object} jsonData from your request (normally this contains the Exception)
8744 loadexception : true
8748 this.proxy = Roo.factory(this.proxy, Roo.data);
8749 this.proxy.xmodule = this.xmodule || false;
8750 this.relayEvents(this.proxy, ["loadexception"]);
8752 this.sortToggle = {};
8753 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
8755 Roo.data.Store.superclass.constructor.call(this);
8757 if(this.inlineData){
8758 this.loadData(this.inlineData);
8759 delete this.inlineData;
8763 Roo.extend(Roo.data.Store, Roo.util.Observable, {
8765 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
8766 * without a remote query - used by combo/forms at present.
8770 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
8773 * @cfg {Array} data Inline data to be loaded when the store is initialized.
8776 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
8777 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
8780 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
8781 * on any HTTP request
8784 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
8787 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
8791 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
8792 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
8797 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
8798 * loaded or when a record is removed. (defaults to false).
8800 pruneModifiedRecords : false,
8806 * Add Records to the Store and fires the add event.
8807 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8809 add : function(records){
8810 records = [].concat(records);
8811 for(var i = 0, len = records.length; i < len; i++){
8812 records[i].join(this);
8814 var index = this.data.length;
8815 this.data.addAll(records);
8816 this.fireEvent("add", this, records, index);
8820 * Remove a Record from the Store and fires the remove event.
8821 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
8823 remove : function(record){
8824 var index = this.data.indexOf(record);
8825 this.data.removeAt(index);
8826 if(this.pruneModifiedRecords){
8827 this.modified.remove(record);
8829 this.fireEvent("remove", this, record, index);
8833 * Remove all Records from the Store and fires the clear event.
8835 removeAll : function(){
8837 if(this.pruneModifiedRecords){
8840 this.fireEvent("clear", this);
8844 * Inserts Records to the Store at the given index and fires the add event.
8845 * @param {Number} index The start index at which to insert the passed Records.
8846 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
8848 insert : function(index, records){
8849 records = [].concat(records);
8850 for(var i = 0, len = records.length; i < len; i++){
8851 this.data.insert(index, records[i]);
8852 records[i].join(this);
8854 this.fireEvent("add", this, records, index);
8858 * Get the index within the cache of the passed Record.
8859 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
8860 * @return {Number} The index of the passed Record. Returns -1 if not found.
8862 indexOf : function(record){
8863 return this.data.indexOf(record);
8867 * Get the index within the cache of the Record with the passed id.
8868 * @param {String} id The id of the Record to find.
8869 * @return {Number} The index of the Record. Returns -1 if not found.
8871 indexOfId : function(id){
8872 return this.data.indexOfKey(id);
8876 * Get the Record with the specified id.
8877 * @param {String} id The id of the Record to find.
8878 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
8880 getById : function(id){
8881 return this.data.key(id);
8885 * Get the Record at the specified index.
8886 * @param {Number} index The index of the Record to find.
8887 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
8889 getAt : function(index){
8890 return this.data.itemAt(index);
8894 * Returns a range of Records between specified indices.
8895 * @param {Number} startIndex (optional) The starting index (defaults to 0)
8896 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
8897 * @return {Roo.data.Record[]} An array of Records
8899 getRange : function(start, end){
8900 return this.data.getRange(start, end);
8904 storeOptions : function(o){
8905 o = Roo.apply({}, o);
8908 this.lastOptions = o;
8912 * Loads the Record cache from the configured Proxy using the configured Reader.
8914 * If using remote paging, then the first load call must specify the <em>start</em>
8915 * and <em>limit</em> properties in the options.params property to establish the initial
8916 * position within the dataset, and the number of Records to cache on each read from the Proxy.
8918 * <strong>It is important to note that for remote data sources, loading is asynchronous,
8919 * and this call will return before the new data has been loaded. Perform any post-processing
8920 * in a callback function, or in a "load" event handler.</strong>
8922 * @param {Object} options An object containing properties which control loading options:<ul>
8923 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
8924 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
8925 * passed the following arguments:<ul>
8926 * <li>r : Roo.data.Record[]</li>
8927 * <li>options: Options object from the load call</li>
8928 * <li>success: Boolean success indicator</li></ul></li>
8929 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
8930 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
8933 load : function(options){
8934 options = options || {};
8935 if(this.fireEvent("beforeload", this, options) !== false){
8936 this.storeOptions(options);
8937 var p = Roo.apply(options.params || {}, this.baseParams);
8938 // if meta was not loaded from remote source.. try requesting it.
8939 if (!this.reader.metaFromRemote) {
8942 if(this.sortInfo && this.remoteSort){
8943 var pn = this.paramNames;
8944 p[pn["sort"]] = this.sortInfo.field;
8945 p[pn["dir"]] = this.sortInfo.direction;
8947 if (this.multiSort) {
8948 var pn = this.paramNames;
8949 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
8952 this.proxy.load(p, this.reader, this.loadRecords, this, options);
8957 * Reloads the Record cache from the configured Proxy using the configured Reader and
8958 * the options from the last load operation performed.
8959 * @param {Object} options (optional) An object containing properties which may override the options
8960 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
8961 * the most recently used options are reused).
8963 reload : function(options){
8964 this.load(Roo.applyIf(options||{}, this.lastOptions));
8968 // Called as a callback by the Reader during a load operation.
8969 loadRecords : function(o, options, success){
8970 if(!o || success === false){
8971 if(success !== false){
8972 this.fireEvent("load", this, [], options, o);
8974 if(options.callback){
8975 options.callback.call(options.scope || this, [], options, false);
8979 // if data returned failure - throw an exception.
8980 if (o.success === false) {
8981 // show a message if no listener is registered.
8982 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
8983 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
8985 // loadmask wil be hooked into this..
8986 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
8989 var r = o.records, t = o.totalRecords || r.length;
8991 this.fireEvent("beforeloadadd", this, r, options, o);
8993 if(!options || options.add !== true){
8994 if(this.pruneModifiedRecords){
8997 for(var i = 0, len = r.length; i < len; i++){
9001 this.data = this.snapshot;
9002 delete this.snapshot;
9005 this.data.addAll(r);
9006 this.totalLength = t;
9008 this.fireEvent("datachanged", this);
9010 this.totalLength = Math.max(t, this.data.length+r.length);
9013 this.fireEvent("load", this, r, options, o);
9014 if(options.callback){
9015 options.callback.call(options.scope || this, r, options, true);
9021 * Loads data from a passed data block. A Reader which understands the format of the data
9022 * must have been configured in the constructor.
9023 * @param {Object} data The data block from which to read the Records. The format of the data expected
9024 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
9025 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
9027 loadData : function(o, append){
9028 var r = this.reader.readRecords(o);
9029 this.loadRecords(r, {add: append}, true);
9033 * Gets the number of cached records.
9035 * <em>If using paging, this may not be the total size of the dataset. If the data object
9036 * used by the Reader contains the dataset size, then the getTotalCount() function returns
9037 * the data set size</em>
9039 getCount : function(){
9040 return this.data.length || 0;
9044 * Gets the total number of records in the dataset as returned by the server.
9046 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
9047 * the dataset size</em>
9049 getTotalCount : function(){
9050 return this.totalLength || 0;
9054 * Returns the sort state of the Store as an object with two properties:
9056 field {String} The name of the field by which the Records are sorted
9057 direction {String} The sort order, "ASC" or "DESC"
9060 getSortState : function(){
9061 return this.sortInfo;
9065 applySort : function(){
9066 if(this.sortInfo && !this.remoteSort){
9067 var s = this.sortInfo, f = s.field;
9068 var st = this.fields.get(f).sortType;
9069 var fn = function(r1, r2){
9070 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
9071 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
9073 this.data.sort(s.direction, fn);
9074 if(this.snapshot && this.snapshot != this.data){
9075 this.snapshot.sort(s.direction, fn);
9081 * Sets the default sort column and order to be used by the next load operation.
9082 * @param {String} fieldName The name of the field to sort by.
9083 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9085 setDefaultSort : function(field, dir){
9086 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
9091 * If remote sorting is used, the sort is performed on the server, and the cache is
9092 * reloaded. If local sorting is used, the cache is sorted internally.
9093 * @param {String} fieldName The name of the field to sort by.
9094 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
9096 sort : function(fieldName, dir){
9097 var f = this.fields.get(fieldName);
9099 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
9101 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
9102 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
9107 this.sortToggle[f.name] = dir;
9108 this.sortInfo = {field: f.name, direction: dir};
9109 if(!this.remoteSort){
9111 this.fireEvent("datachanged", this);
9113 this.load(this.lastOptions);
9118 * Calls the specified function for each of the Records in the cache.
9119 * @param {Function} fn The function to call. The Record is passed as the first parameter.
9120 * Returning <em>false</em> aborts and exits the iteration.
9121 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
9123 each : function(fn, scope){
9124 this.data.each(fn, scope);
9128 * Gets all records modified since the last commit. Modified records are persisted across load operations
9129 * (e.g., during paging).
9130 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
9132 getModifiedRecords : function(){
9133 return this.modified;
9137 createFilterFn : function(property, value, anyMatch){
9138 if(!value.exec){ // not a regex
9139 value = String(value);
9140 if(value.length == 0){
9143 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
9146 return value.test(r.data[property]);
9151 * Sums the value of <i>property</i> for each record between start and end and returns the result.
9152 * @param {String} property A field on your records
9153 * @param {Number} start The record index to start at (defaults to 0)
9154 * @param {Number} end The last record index to include (defaults to length - 1)
9155 * @return {Number} The sum
9157 sum : function(property, start, end){
9158 var rs = this.data.items, v = 0;
9160 end = (end || end === 0) ? end : rs.length-1;
9162 for(var i = start; i <= end; i++){
9163 v += (rs[i].data[property] || 0);
9169 * Filter the records by a specified property.
9170 * @param {String} field A field on your records
9171 * @param {String/RegExp} value Either a string that the field
9172 * should start with or a RegExp to test against the field
9173 * @param {Boolean} anyMatch True to match any part not just the beginning
9175 filter : function(property, value, anyMatch){
9176 var fn = this.createFilterFn(property, value, anyMatch);
9177 return fn ? this.filterBy(fn) : this.clearFilter();
9181 * Filter by a function. The specified function will be called with each
9182 * record in this data source. If the function returns true the record is included,
9183 * otherwise it is filtered.
9184 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9185 * @param {Object} scope (optional) The scope of the function (defaults to this)
9187 filterBy : function(fn, scope){
9188 this.snapshot = this.snapshot || this.data;
9189 this.data = this.queryBy(fn, scope||this);
9190 this.fireEvent("datachanged", this);
9194 * Query the records by a specified property.
9195 * @param {String} field A field on your records
9196 * @param {String/RegExp} value Either a string that the field
9197 * should start with or a RegExp to test against the field
9198 * @param {Boolean} anyMatch True to match any part not just the beginning
9199 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9201 query : function(property, value, anyMatch){
9202 var fn = this.createFilterFn(property, value, anyMatch);
9203 return fn ? this.queryBy(fn) : this.data.clone();
9207 * Query by a function. The specified function will be called with each
9208 * record in this data source. If the function returns true the record is included
9210 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
9211 * @param {Object} scope (optional) The scope of the function (defaults to this)
9212 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
9214 queryBy : function(fn, scope){
9215 var data = this.snapshot || this.data;
9216 return data.filterBy(fn, scope||this);
9220 * Collects unique values for a particular dataIndex from this store.
9221 * @param {String} dataIndex The property to collect
9222 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
9223 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
9224 * @return {Array} An array of the unique values
9226 collect : function(dataIndex, allowNull, bypassFilter){
9227 var d = (bypassFilter === true && this.snapshot) ?
9228 this.snapshot.items : this.data.items;
9229 var v, sv, r = [], l = {};
9230 for(var i = 0, len = d.length; i < len; i++){
9231 v = d[i].data[dataIndex];
9233 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
9242 * Revert to a view of the Record cache with no filtering applied.
9243 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
9245 clearFilter : function(suppressEvent){
9246 if(this.snapshot && this.snapshot != this.data){
9247 this.data = this.snapshot;
9248 delete this.snapshot;
9249 if(suppressEvent !== true){
9250 this.fireEvent("datachanged", this);
9256 afterEdit : function(record){
9257 if(this.modified.indexOf(record) == -1){
9258 this.modified.push(record);
9260 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
9264 afterReject : function(record){
9265 this.modified.remove(record);
9266 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
9270 afterCommit : function(record){
9271 this.modified.remove(record);
9272 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
9276 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
9277 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
9279 commitChanges : function(){
9280 var m = this.modified.slice(0);
9282 for(var i = 0, len = m.length; i < len; i++){
9288 * Cancel outstanding changes on all changed records.
9290 rejectChanges : function(){
9291 var m = this.modified.slice(0);
9293 for(var i = 0, len = m.length; i < len; i++){
9298 onMetaChange : function(meta, rtype, o){
9299 this.recordType = rtype;
9300 this.fields = rtype.prototype.fields;
9301 delete this.snapshot;
9302 this.sortInfo = meta.sortInfo || this.sortInfo;
9304 this.fireEvent('metachange', this, this.reader.meta);
9307 moveIndex : function(data, type)
9309 var index = this.indexOf(data);
9311 var newIndex = index + type;
9315 this.insert(newIndex, data);
9320 * Ext JS Library 1.1.1
9321 * Copyright(c) 2006-2007, Ext JS, LLC.
9323 * Originally Released Under LGPL - original licence link has changed is not relivant.
9326 * <script type="text/javascript">
9330 * @class Roo.data.SimpleStore
9331 * @extends Roo.data.Store
9332 * Small helper class to make creating Stores from Array data easier.
9333 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
9334 * @cfg {Array} fields An array of field definition objects, or field name strings.
9335 * @cfg {Array} data The multi-dimensional array of data
9337 * @param {Object} config
9339 Roo.data.SimpleStore = function(config){
9340 Roo.data.SimpleStore.superclass.constructor.call(this, {
9342 reader: new Roo.data.ArrayReader({
9345 Roo.data.Record.create(config.fields)
9347 proxy : new Roo.data.MemoryProxy(config.data)
9351 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
9353 * Ext JS Library 1.1.1
9354 * Copyright(c) 2006-2007, Ext JS, LLC.
9356 * Originally Released Under LGPL - original licence link has changed is not relivant.
9359 * <script type="text/javascript">
9364 * @extends Roo.data.Store
9365 * @class Roo.data.JsonStore
9366 * Small helper class to make creating Stores for JSON data easier. <br/>
9368 var store = new Roo.data.JsonStore({
9369 url: 'get-images.php',
9371 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
9374 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
9375 * JsonReader and HttpProxy (unless inline data is provided).</b>
9376 * @cfg {Array} fields An array of field definition objects, or field name strings.
9378 * @param {Object} config
9380 Roo.data.JsonStore = function(c){
9381 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
9382 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
9383 reader: new Roo.data.JsonReader(c, c.fields)
9386 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
9388 * Ext JS Library 1.1.1
9389 * Copyright(c) 2006-2007, Ext JS, LLC.
9391 * Originally Released Under LGPL - original licence link has changed is not relivant.
9394 * <script type="text/javascript">
9398 Roo.data.Field = function(config){
9399 if(typeof config == "string"){
9400 config = {name: config};
9402 Roo.apply(this, config);
9408 var st = Roo.data.SortTypes;
9409 // named sortTypes are supported, here we look them up
9410 if(typeof this.sortType == "string"){
9411 this.sortType = st[this.sortType];
9414 // set default sortType for strings and dates
9418 this.sortType = st.asUCString;
9421 this.sortType = st.asDate;
9424 this.sortType = st.none;
9429 var stripRe = /[\$,%]/g;
9431 // prebuilt conversion function for this field, instead of
9432 // switching every time we're reading a value
9434 var cv, dateFormat = this.dateFormat;
9439 cv = function(v){ return v; };
9442 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
9446 return v !== undefined && v !== null && v !== '' ?
9447 parseInt(String(v).replace(stripRe, ""), 10) : '';
9452 return v !== undefined && v !== null && v !== '' ?
9453 parseFloat(String(v).replace(stripRe, ""), 10) : '';
9458 cv = function(v){ return v === true || v === "true" || v == 1; };
9465 if(v instanceof Date){
9469 if(dateFormat == "timestamp"){
9470 return new Date(v*1000);
9472 return Date.parseDate(v, dateFormat);
9474 var parsed = Date.parse(v);
9475 return parsed ? new Date(parsed) : null;
9484 Roo.data.Field.prototype = {
9492 * Ext JS Library 1.1.1
9493 * Copyright(c) 2006-2007, Ext JS, LLC.
9495 * Originally Released Under LGPL - original licence link has changed is not relivant.
9498 * <script type="text/javascript">
9501 // Base class for reading structured data from a data source. This class is intended to be
9502 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
9505 * @class Roo.data.DataReader
9506 * Base class for reading structured data from a data source. This class is intended to be
9507 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
9510 Roo.data.DataReader = function(meta, recordType){
9514 this.recordType = recordType instanceof Array ?
9515 Roo.data.Record.create(recordType) : recordType;
9518 Roo.data.DataReader.prototype = {
9520 * Create an empty record
9521 * @param {Object} data (optional) - overlay some values
9522 * @return {Roo.data.Record} record created.
9524 newRow : function(d) {
9526 this.recordType.prototype.fields.each(function(c) {
9528 case 'int' : da[c.name] = 0; break;
9529 case 'date' : da[c.name] = new Date(); break;
9530 case 'float' : da[c.name] = 0.0; break;
9531 case 'boolean' : da[c.name] = false; break;
9532 default : da[c.name] = ""; break;
9536 return new this.recordType(Roo.apply(da, d));
9541 * Ext JS Library 1.1.1
9542 * Copyright(c) 2006-2007, Ext JS, LLC.
9544 * Originally Released Under LGPL - original licence link has changed is not relivant.
9547 * <script type="text/javascript">
9551 * @class Roo.data.DataProxy
9552 * @extends Roo.data.Observable
9553 * This class is an abstract base class for implementations which provide retrieval of
9554 * unformatted data objects.<br>
9556 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
9557 * (of the appropriate type which knows how to parse the data object) to provide a block of
9558 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
9560 * Custom implementations must implement the load method as described in
9561 * {@link Roo.data.HttpProxy#load}.
9563 Roo.data.DataProxy = function(){
9567 * Fires before a network request is made to retrieve a data object.
9568 * @param {Object} This DataProxy object.
9569 * @param {Object} params The params parameter to the load function.
9574 * Fires before the load method's callback is called.
9575 * @param {Object} This DataProxy object.
9576 * @param {Object} o The data object.
9577 * @param {Object} arg The callback argument object passed to the load function.
9581 * @event loadexception
9582 * Fires if an Exception occurs during data retrieval.
9583 * @param {Object} This DataProxy object.
9584 * @param {Object} o The data object.
9585 * @param {Object} arg The callback argument object passed to the load function.
9586 * @param {Object} e The Exception.
9588 loadexception : true
9590 Roo.data.DataProxy.superclass.constructor.call(this);
9593 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
9596 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
9600 * Ext JS Library 1.1.1
9601 * Copyright(c) 2006-2007, Ext JS, LLC.
9603 * Originally Released Under LGPL - original licence link has changed is not relivant.
9606 * <script type="text/javascript">
9609 * @class Roo.data.MemoryProxy
9610 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
9611 * to the Reader when its load method is called.
9613 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
9615 Roo.data.MemoryProxy = function(data){
9619 Roo.data.MemoryProxy.superclass.constructor.call(this);
9623 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
9625 * Load data from the requested source (in this case an in-memory
9626 * data object passed to the constructor), read the data object into
9627 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9628 * process that block using the passed callback.
9629 * @param {Object} params This parameter is not used by the MemoryProxy class.
9630 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9631 * object into a block of Roo.data.Records.
9632 * @param {Function} callback The function into which to pass the block of Roo.data.records.
9633 * The function must be passed <ul>
9634 * <li>The Record block object</li>
9635 * <li>The "arg" argument from the load function</li>
9636 * <li>A boolean success indicator</li>
9638 * @param {Object} scope The scope in which to call the callback
9639 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9641 load : function(params, reader, callback, scope, arg){
9642 params = params || {};
9645 result = reader.readRecords(this.data);
9647 this.fireEvent("loadexception", this, arg, null, e);
9648 callback.call(scope, null, arg, false);
9651 callback.call(scope, result, arg, true);
9655 update : function(params, records){
9660 * Ext JS Library 1.1.1
9661 * Copyright(c) 2006-2007, Ext JS, LLC.
9663 * Originally Released Under LGPL - original licence link has changed is not relivant.
9666 * <script type="text/javascript">
9669 * @class Roo.data.HttpProxy
9670 * @extends Roo.data.DataProxy
9671 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
9672 * configured to reference a certain URL.<br><br>
9674 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
9675 * from which the running page was served.<br><br>
9677 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
9679 * Be aware that to enable the browser to parse an XML document, the server must set
9680 * the Content-Type header in the HTTP response to "text/xml".
9682 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
9683 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
9684 * will be used to make the request.
9686 Roo.data.HttpProxy = function(conn){
9687 Roo.data.HttpProxy.superclass.constructor.call(this);
9688 // is conn a conn config or a real conn?
9690 this.useAjax = !conn || !conn.events;
9694 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
9695 // thse are take from connection...
9698 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
9701 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
9702 * extra parameters to each request made by this object. (defaults to undefined)
9705 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
9706 * to each request made by this object. (defaults to undefined)
9709 * @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)
9712 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
9715 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
9721 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
9725 * Return the {@link Roo.data.Connection} object being used by this Proxy.
9726 * @return {Connection} The Connection object. This object may be used to subscribe to events on
9727 * a finer-grained basis than the DataProxy events.
9729 getConnection : function(){
9730 return this.useAjax ? Roo.Ajax : this.conn;
9734 * Load data from the configured {@link Roo.data.Connection}, read the data object into
9735 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
9736 * process that block using the passed callback.
9737 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9738 * for the request to the remote server.
9739 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9740 * object into a block of Roo.data.Records.
9741 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9742 * The function must be passed <ul>
9743 * <li>The Record block object</li>
9744 * <li>The "arg" argument from the load function</li>
9745 * <li>A boolean success indicator</li>
9747 * @param {Object} scope The scope in which to call the callback
9748 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9750 load : function(params, reader, callback, scope, arg){
9751 if(this.fireEvent("beforeload", this, params) !== false){
9753 params : params || {},
9755 callback : callback,
9760 callback : this.loadResponse,
9764 Roo.applyIf(o, this.conn);
9765 if(this.activeRequest){
9766 Roo.Ajax.abort(this.activeRequest);
9768 this.activeRequest = Roo.Ajax.request(o);
9770 this.conn.request(o);
9773 callback.call(scope||this, null, arg, false);
9778 loadResponse : function(o, success, response){
9779 delete this.activeRequest;
9781 this.fireEvent("loadexception", this, o, response);
9782 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9787 result = o.reader.read(response);
9789 this.fireEvent("loadexception", this, o, response, e);
9790 o.request.callback.call(o.request.scope, null, o.request.arg, false);
9794 this.fireEvent("load", this, o, o.request.arg);
9795 o.request.callback.call(o.request.scope, result, o.request.arg, true);
9799 update : function(dataSet){
9804 updateResponse : function(dataSet){
9809 * Ext JS Library 1.1.1
9810 * Copyright(c) 2006-2007, Ext JS, LLC.
9812 * Originally Released Under LGPL - original licence link has changed is not relivant.
9815 * <script type="text/javascript">
9819 * @class Roo.data.ScriptTagProxy
9820 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
9821 * other than the originating domain of the running page.<br><br>
9823 * <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
9824 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
9826 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
9827 * source code that is used as the source inside a <script> tag.<br><br>
9829 * In order for the browser to process the returned data, the server must wrap the data object
9830 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
9831 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
9832 * depending on whether the callback name was passed:
9835 boolean scriptTag = false;
9836 String cb = request.getParameter("callback");
9839 response.setContentType("text/javascript");
9841 response.setContentType("application/x-json");
9843 Writer out = response.getWriter();
9845 out.write(cb + "(");
9847 out.print(dataBlock.toJsonString());
9854 * @param {Object} config A configuration object.
9856 Roo.data.ScriptTagProxy = function(config){
9857 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
9858 Roo.apply(this, config);
9859 this.head = document.getElementsByTagName("head")[0];
9862 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
9864 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
9866 * @cfg {String} url The URL from which to request the data object.
9869 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
9873 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
9874 * the server the name of the callback function set up by the load call to process the returned data object.
9875 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
9876 * javascript output which calls this named function passing the data object as its only parameter.
9878 callbackParam : "callback",
9880 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
9881 * name to the request.
9886 * Load data from the configured URL, read the data object into
9887 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
9888 * process that block using the passed callback.
9889 * @param {Object} params An object containing properties which are to be used as HTTP parameters
9890 * for the request to the remote server.
9891 * @param {Roo.data.DataReader} reader The Reader object which converts the data
9892 * object into a block of Roo.data.Records.
9893 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
9894 * The function must be passed <ul>
9895 * <li>The Record block object</li>
9896 * <li>The "arg" argument from the load function</li>
9897 * <li>A boolean success indicator</li>
9899 * @param {Object} scope The scope in which to call the callback
9900 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
9902 load : function(params, reader, callback, scope, arg){
9903 if(this.fireEvent("beforeload", this, params) !== false){
9905 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
9908 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
9910 url += "&_dc=" + (new Date().getTime());
9912 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
9915 cb : "stcCallback"+transId,
9916 scriptId : "stcScript"+transId,
9920 callback : callback,
9926 window[trans.cb] = function(o){
9927 conn.handleResponse(o, trans);
9930 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
9932 if(this.autoAbort !== false){
9936 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
9938 var script = document.createElement("script");
9939 script.setAttribute("src", url);
9940 script.setAttribute("type", "text/javascript");
9941 script.setAttribute("id", trans.scriptId);
9942 this.head.appendChild(script);
9946 callback.call(scope||this, null, arg, false);
9951 isLoading : function(){
9952 return this.trans ? true : false;
9956 * Abort the current server request.
9959 if(this.isLoading()){
9960 this.destroyTrans(this.trans);
9965 destroyTrans : function(trans, isLoaded){
9966 this.head.removeChild(document.getElementById(trans.scriptId));
9967 clearTimeout(trans.timeoutId);
9969 window[trans.cb] = undefined;
9971 delete window[trans.cb];
9974 // if hasn't been loaded, wait for load to remove it to prevent script error
9975 window[trans.cb] = function(){
9976 window[trans.cb] = undefined;
9978 delete window[trans.cb];
9985 handleResponse : function(o, trans){
9987 this.destroyTrans(trans, true);
9990 result = trans.reader.readRecords(o);
9992 this.fireEvent("loadexception", this, o, trans.arg, e);
9993 trans.callback.call(trans.scope||window, null, trans.arg, false);
9996 this.fireEvent("load", this, o, trans.arg);
9997 trans.callback.call(trans.scope||window, result, trans.arg, true);
10001 handleFailure : function(trans){
10002 this.trans = false;
10003 this.destroyTrans(trans, false);
10004 this.fireEvent("loadexception", this, null, trans.arg);
10005 trans.callback.call(trans.scope||window, null, trans.arg, false);
10009 * Ext JS Library 1.1.1
10010 * Copyright(c) 2006-2007, Ext JS, LLC.
10012 * Originally Released Under LGPL - original licence link has changed is not relivant.
10015 * <script type="text/javascript">
10019 * @class Roo.data.JsonReader
10020 * @extends Roo.data.DataReader
10021 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
10022 * based on mappings in a provided Roo.data.Record constructor.
10024 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
10025 * in the reply previously.
10030 var RecordDef = Roo.data.Record.create([
10031 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
10032 {name: 'occupation'} // This field will use "occupation" as the mapping.
10034 var myReader = new Roo.data.JsonReader({
10035 totalProperty: "results", // The property which contains the total dataset size (optional)
10036 root: "rows", // The property which contains an Array of row objects
10037 id: "id" // The property within each row object that provides an ID for the record (optional)
10041 * This would consume a JSON file like this:
10043 { 'results': 2, 'rows': [
10044 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
10045 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
10048 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
10049 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
10050 * paged from the remote server.
10051 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
10052 * @cfg {String} root name of the property which contains the Array of row objects.
10053 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
10055 * Create a new JsonReader
10056 * @param {Object} meta Metadata configuration options
10057 * @param {Object} recordType Either an Array of field definition objects,
10058 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
10060 Roo.data.JsonReader = function(meta, recordType){
10063 // set some defaults:
10064 Roo.applyIf(meta, {
10065 totalProperty: 'total',
10066 successProperty : 'success',
10071 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
10073 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
10076 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
10077 * Used by Store query builder to append _requestMeta to params.
10080 metaFromRemote : false,
10082 * This method is only used by a DataProxy which has retrieved data from a remote server.
10083 * @param {Object} response The XHR object which contains the JSON data in its responseText.
10084 * @return {Object} data A data block which is used by an Roo.data.Store object as
10085 * a cache of Roo.data.Records.
10087 read : function(response){
10088 var json = response.responseText;
10090 var o = /* eval:var:o */ eval("("+json+")");
10092 throw {message: "JsonReader.read: Json object not found"};
10098 this.metaFromRemote = true;
10099 this.meta = o.metaData;
10100 this.recordType = Roo.data.Record.create(o.metaData.fields);
10101 this.onMetaChange(this.meta, this.recordType, o);
10103 return this.readRecords(o);
10106 // private function a store will implement
10107 onMetaChange : function(meta, recordType, o){
10114 simpleAccess: function(obj, subsc) {
10121 getJsonAccessor: function(){
10123 return function(expr) {
10125 return(re.test(expr))
10126 ? new Function("obj", "return obj." + expr)
10131 return Roo.emptyFn;
10136 * Create a data block containing Roo.data.Records from an XML document.
10137 * @param {Object} o An object which contains an Array of row objects in the property specified
10138 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
10139 * which contains the total size of the dataset.
10140 * @return {Object} data A data block which is used by an Roo.data.Store object as
10141 * a cache of Roo.data.Records.
10143 readRecords : function(o){
10145 * After any data loads, the raw JSON data is available for further custom processing.
10149 var s = this.meta, Record = this.recordType,
10150 f = Record.prototype.fields, fi = f.items, fl = f.length;
10152 // Generate extraction functions for the totalProperty, the root, the id, and for each field
10154 if(s.totalProperty) {
10155 this.getTotal = this.getJsonAccessor(s.totalProperty);
10157 if(s.successProperty) {
10158 this.getSuccess = this.getJsonAccessor(s.successProperty);
10160 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
10162 var g = this.getJsonAccessor(s.id);
10163 this.getId = function(rec) {
10165 return (r === undefined || r === "") ? null : r;
10168 this.getId = function(){return null;};
10171 for(var jj = 0; jj < fl; jj++){
10173 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
10174 this.ef[jj] = this.getJsonAccessor(map);
10178 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
10179 if(s.totalProperty){
10180 var vt = parseInt(this.getTotal(o), 10);
10185 if(s.successProperty){
10186 var vs = this.getSuccess(o);
10187 if(vs === false || vs === 'false'){
10192 for(var i = 0; i < c; i++){
10195 var id = this.getId(n);
10196 for(var j = 0; j < fl; j++){
10198 var v = this.ef[j](n);
10200 Roo.log('missing convert for ' + f.name);
10204 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
10206 var record = new Record(values, id);
10208 records[i] = record;
10214 totalRecords : totalRecords
10219 * Ext JS Library 1.1.1
10220 * Copyright(c) 2006-2007, Ext JS, LLC.
10222 * Originally Released Under LGPL - original licence link has changed is not relivant.
10225 * <script type="text/javascript">
10229 * @class Roo.data.ArrayReader
10230 * @extends Roo.data.DataReader
10231 * Data reader class to create an Array of Roo.data.Record objects from an Array.
10232 * Each element of that Array represents a row of data fields. The
10233 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
10234 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
10238 var RecordDef = Roo.data.Record.create([
10239 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
10240 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
10242 var myReader = new Roo.data.ArrayReader({
10243 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
10247 * This would consume an Array like this:
10249 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
10251 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
10253 * Create a new JsonReader
10254 * @param {Object} meta Metadata configuration options.
10255 * @param {Object} recordType Either an Array of field definition objects
10256 * as specified to {@link Roo.data.Record#create},
10257 * or an {@link Roo.data.Record} object
10258 * created using {@link Roo.data.Record#create}.
10260 Roo.data.ArrayReader = function(meta, recordType){
10261 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
10264 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
10266 * Create a data block containing Roo.data.Records from an XML document.
10267 * @param {Object} o An Array of row objects which represents the dataset.
10268 * @return {Object} data A data block which is used by an Roo.data.Store object as
10269 * a cache of Roo.data.Records.
10271 readRecords : function(o){
10272 var sid = this.meta ? this.meta.id : null;
10273 var recordType = this.recordType, fields = recordType.prototype.fields;
10276 for(var i = 0; i < root.length; i++){
10279 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
10280 for(var j = 0, jlen = fields.length; j < jlen; j++){
10281 var f = fields.items[j];
10282 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
10283 var v = n[k] !== undefined ? n[k] : f.defaultValue;
10285 values[f.name] = v;
10287 var record = new recordType(values, id);
10289 records[records.length] = record;
10293 totalRecords : records.length
10302 * @class Roo.bootstrap.ComboBox
10303 * @extends Roo.bootstrap.TriggerField
10304 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
10305 * @cfg {Boolean} append (true|false) default false
10306 * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
10307 * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
10308 * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
10309 * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
10310 * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
10312 * Create a new ComboBox.
10313 * @param {Object} config Configuration options
10315 Roo.bootstrap.ComboBox = function(config){
10316 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
10320 * Fires when the dropdown list is expanded
10321 * @param {Roo.bootstrap.ComboBox} combo This combo box
10326 * Fires when the dropdown list is collapsed
10327 * @param {Roo.bootstrap.ComboBox} combo This combo box
10331 * @event beforeselect
10332 * Fires before a list item is selected. Return false to cancel the selection.
10333 * @param {Roo.bootstrap.ComboBox} combo This combo box
10334 * @param {Roo.data.Record} record The data record returned from the underlying store
10335 * @param {Number} index The index of the selected item in the dropdown list
10337 'beforeselect' : true,
10340 * Fires when a list item is selected
10341 * @param {Roo.bootstrap.ComboBox} combo This combo box
10342 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
10343 * @param {Number} index The index of the selected item in the dropdown list
10347 * @event beforequery
10348 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
10349 * The event object passed has these properties:
10350 * @param {Roo.bootstrap.ComboBox} combo This combo box
10351 * @param {String} query The query
10352 * @param {Boolean} forceAll true to force "all" query
10353 * @param {Boolean} cancel true to cancel the query
10354 * @param {Object} e The query event object
10356 'beforequery': true,
10359 * Fires when the 'add' icon is pressed (add a listener to enable add button)
10360 * @param {Roo.bootstrap.ComboBox} combo This combo box
10365 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
10366 * @param {Roo.bootstrap.ComboBox} combo This combo box
10367 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
10372 * Fires when the remove value from the combobox array
10373 * @param {Roo.bootstrap.ComboBox} combo This combo box
10380 this.tickItems = [];
10382 this.selectedIndex = -1;
10383 if(this.mode == 'local'){
10384 if(config.queryDelay === undefined){
10385 this.queryDelay = 10;
10387 if(config.minChars === undefined){
10393 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
10396 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
10397 * rendering into an Roo.Editor, defaults to false)
10400 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
10401 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
10404 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
10407 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
10408 * the dropdown list (defaults to undefined, with no header element)
10412 * @cfg {String/Roo.Template} tpl The template to use to render the output
10416 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
10418 listWidth: undefined,
10420 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
10421 * mode = 'remote' or 'text' if mode = 'local')
10423 displayField: undefined,
10425 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
10426 * mode = 'remote' or 'value' if mode = 'local').
10427 * Note: use of a valueField requires the user make a selection
10428 * in order for a value to be mapped.
10430 valueField: undefined,
10434 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
10435 * field's data value (defaults to the underlying DOM element's name)
10437 hiddenName: undefined,
10439 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
10443 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
10445 selectedClass: 'active',
10448 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
10452 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
10453 * anchor positions (defaults to 'tl-bl')
10455 listAlign: 'tl-bl?',
10457 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
10461 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
10462 * query specified by the allQuery config option (defaults to 'query')
10464 triggerAction: 'query',
10466 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
10467 * (defaults to 4, does not apply if editable = false)
10471 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
10472 * delay (typeAheadDelay) if it matches a known value (defaults to false)
10476 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
10477 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
10481 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
10482 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
10486 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
10487 * when editable = true (defaults to false)
10489 selectOnFocus:false,
10491 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
10493 queryParam: 'query',
10495 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
10496 * when mode = 'remote' (defaults to 'Loading...')
10498 loadingText: 'Loading...',
10500 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
10504 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
10508 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
10509 * traditional select (defaults to true)
10513 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
10517 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
10521 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
10522 * listWidth has a higher value)
10526 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
10527 * allow the user to set arbitrary text into the field (defaults to false)
10529 forceSelection:false,
10531 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
10532 * if typeAhead = true (defaults to 250)
10534 typeAheadDelay : 250,
10536 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
10537 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
10539 valueNotFoundText : undefined,
10541 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
10543 blockFocus : false,
10546 * @cfg {Boolean} disableClear Disable showing of clear button.
10548 disableClear : false,
10550 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
10552 alwaysQuery : false,
10555 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
10569 btnPosition : 'right',
10570 triggerList : true,
10571 showToggleBtn : true,
10572 // element that contains real text value.. (when hidden is used..)
10574 getAutoCreate : function()
10581 if(!this.tickable){
10582 cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
10587 * ComboBox with tickable selections
10590 var align = this.labelAlign || this.parentLabelAlign();
10593 cls : 'form-group roo-combobox-tickable' //input-group
10599 cls : 'tickable-buttons',
10604 cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
10611 cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
10618 cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
10625 Roo.each(buttons.cn, function(c){
10627 c.cls += ' btn-' + _this.size;
10630 if (_this.disabled) {
10641 cls: 'form-hidden-field'
10645 cls: 'select2-choices',
10649 cls: 'select2-search-field',
10661 cls: 'select2-container input-group select2-container-multi',
10666 // cls: 'typeahead typeahead-long dropdown-menu',
10667 // style: 'display:none; max-height:' + this.maxHeight + 'px;'
10672 if (align ==='left' && this.fieldLabel.length) {
10674 Roo.log("left and has label");
10680 cls : 'control-label col-sm-' + this.labelWidth,
10681 html : this.fieldLabel
10685 cls : "col-sm-" + (12 - this.labelWidth),
10692 } else if ( this.fieldLabel.length) {
10698 //cls : 'input-group-addon',
10699 html : this.fieldLabel
10709 Roo.log(" no label && no align");
10716 ['xs','sm','md','lg'].map(function(size){
10717 if (settings[size]) {
10718 cfg.cls += ' col-' + size + '-' + settings[size];
10727 initEvents: function()
10731 throw "can not find store for combo";
10733 this.store = Roo.factory(this.store, Roo.data);
10736 this.initTickableEvents();
10740 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
10742 if(this.hiddenName){
10744 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10746 this.hiddenField.dom.value =
10747 this.hiddenValue !== undefined ? this.hiddenValue :
10748 this.value !== undefined ? this.value : '';
10750 // prevent input submission
10751 this.el.dom.removeAttribute('name');
10752 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10757 // this.el.dom.setAttribute('autocomplete', 'off');
10760 var cls = 'x-combo-list';
10762 //this.list = new Roo.Layer({
10763 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
10769 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
10770 _this.list.setWidth(lw);
10773 this.list.on('mouseover', this.onViewOver, this);
10774 this.list.on('mousemove', this.onViewMove, this);
10776 this.list.on('scroll', this.onViewScroll, this);
10779 this.list.swallowEvent('mousewheel');
10780 this.assetHeight = 0;
10783 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
10784 this.assetHeight += this.header.getHeight();
10787 this.innerList = this.list.createChild({cls:cls+'-inner'});
10788 this.innerList.on('mouseover', this.onViewOver, this);
10789 this.innerList.on('mousemove', this.onViewMove, this);
10790 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
10792 if(this.allowBlank && !this.pageSize && !this.disableClear){
10793 this.footer = this.list.createChild({cls:cls+'-ft'});
10794 this.pageTb = new Roo.Toolbar(this.footer);
10798 this.footer = this.list.createChild({cls:cls+'-ft'});
10799 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
10800 {pageSize: this.pageSize});
10804 if (this.pageTb && this.allowBlank && !this.disableClear) {
10806 this.pageTb.add(new Roo.Toolbar.Fill(), {
10807 cls: 'x-btn-icon x-btn-clear',
10809 handler: function()
10812 _this.clearValue();
10813 _this.onSelect(false, -1);
10818 this.assetHeight += this.footer.getHeight();
10823 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
10826 this.view = new Roo.View(this.list, this.tpl, {
10827 singleSelect:true, store: this.store, selectedClass: this.selectedClass
10829 //this.view.wrapEl.setDisplayed(false);
10830 this.view.on('click', this.onViewClick, this);
10834 this.store.on('beforeload', this.onBeforeLoad, this);
10835 this.store.on('load', this.onLoad, this);
10836 this.store.on('loadexception', this.onLoadException, this);
10838 if(this.resizable){
10839 this.resizer = new Roo.Resizable(this.list, {
10840 pinned:true, handles:'se'
10842 this.resizer.on('resize', function(r, w, h){
10843 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
10844 this.listWidth = w;
10845 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
10846 this.restrictHeight();
10848 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
10851 if(!this.editable){
10852 this.editable = true;
10853 this.setEditable(false);
10858 if (typeof(this.events.add.listeners) != 'undefined') {
10860 this.addicon = this.wrap.createChild(
10861 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
10863 this.addicon.on('click', function(e) {
10864 this.fireEvent('add', this);
10867 if (typeof(this.events.edit.listeners) != 'undefined') {
10869 this.editicon = this.wrap.createChild(
10870 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
10871 if (this.addicon) {
10872 this.editicon.setStyle('margin-left', '40px');
10874 this.editicon.on('click', function(e) {
10876 // we fire even if inothing is selected..
10877 this.fireEvent('edit', this, this.lastData );
10883 this.keyNav = new Roo.KeyNav(this.inputEl(), {
10884 "up" : function(e){
10885 this.inKeyMode = true;
10889 "down" : function(e){
10890 if(!this.isExpanded()){
10891 this.onTriggerClick();
10893 this.inKeyMode = true;
10898 "enter" : function(e){
10899 // this.onViewClick();
10903 if(this.fireEvent("specialkey", this, e)){
10904 this.onViewClick(false);
10910 "esc" : function(e){
10914 "tab" : function(e){
10917 if(this.fireEvent("specialkey", this, e)){
10918 this.onViewClick(false);
10926 doRelay : function(foo, bar, hname){
10927 if(hname == 'down' || this.scope.isExpanded()){
10928 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
10937 this.queryDelay = Math.max(this.queryDelay || 10,
10938 this.mode == 'local' ? 10 : 250);
10941 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
10943 if(this.typeAhead){
10944 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
10946 if(this.editable !== false){
10947 this.inputEl().on("keyup", this.onKeyUp, this);
10949 if(this.forceSelection){
10950 this.inputEl().on('blur', this.doForce, this);
10954 this.choices = this.el.select('ul.select2-choices', true).first();
10955 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10959 initTickableEvents: function()
10963 if(this.hiddenName){
10965 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
10967 this.hiddenField.dom.value =
10968 this.hiddenValue !== undefined ? this.hiddenValue :
10969 this.value !== undefined ? this.value : '';
10971 // prevent input submission
10972 this.el.dom.removeAttribute('name');
10973 this.hiddenField.dom.setAttribute('name', this.hiddenName);
10978 // this.list = this.el.select('ul.dropdown-menu',true).first();
10980 this.choices = this.el.select('ul.select2-choices', true).first();
10981 this.searchField = this.el.select('ul li.select2-search-field', true).first();
10982 if(this.triggerList){
10983 this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
10986 this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
10987 this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
10989 this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
10990 this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
10992 this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
10993 this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
10995 this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
10996 this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
10997 this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
11000 this.cancelBtn.hide();
11005 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
11006 _this.list.setWidth(lw);
11009 this.list.on('mouseover', this.onViewOver, this);
11010 this.list.on('mousemove', this.onViewMove, this);
11012 this.list.on('scroll', this.onViewScroll, this);
11015 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>';
11018 this.view = new Roo.View(this.list, this.tpl, {
11019 singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
11022 //this.view.wrapEl.setDisplayed(false);
11023 this.view.on('click', this.onViewClick, this);
11027 this.store.on('beforeload', this.onBeforeLoad, this);
11028 this.store.on('load', this.onLoad, this);
11029 this.store.on('loadexception', this.onLoadException, this);
11031 // this.keyNav = new Roo.KeyNav(this.inputEl(), {
11032 // "up" : function(e){
11033 // this.inKeyMode = true;
11034 // this.selectPrev();
11037 // "down" : function(e){
11038 // if(!this.isExpanded()){
11039 // this.onTriggerClick();
11041 // this.inKeyMode = true;
11042 // this.selectNext();
11046 // "enter" : function(e){
11047 //// this.onViewClick();
11049 // this.collapse();
11051 // if(this.fireEvent("specialkey", this, e)){
11052 // this.onViewClick(false);
11058 // "esc" : function(e){
11059 // this.collapse();
11062 // "tab" : function(e){
11063 // this.collapse();
11065 // if(this.fireEvent("specialkey", this, e)){
11066 // this.onViewClick(false);
11074 // doRelay : function(foo, bar, hname){
11075 // if(hname == 'down' || this.scope.isExpanded()){
11076 // return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
11081 // forceKeyDown: true
11085 this.queryDelay = Math.max(this.queryDelay || 10,
11086 this.mode == 'local' ? 10 : 250);
11089 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
11091 if(this.typeAhead){
11092 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
11096 onDestroy : function(){
11098 this.view.setStore(null);
11099 this.view.el.removeAllListeners();
11100 this.view.el.remove();
11101 this.view.purgeListeners();
11104 this.list.dom.innerHTML = '';
11108 this.store.un('beforeload', this.onBeforeLoad, this);
11109 this.store.un('load', this.onLoad, this);
11110 this.store.un('loadexception', this.onLoadException, this);
11112 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
11116 fireKey : function(e){
11117 if(e.isNavKeyPress() && !this.list.isVisible()){
11118 this.fireEvent("specialkey", this, e);
11123 onResize: function(w, h){
11124 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
11126 // if(typeof w != 'number'){
11127 // // we do not handle it!?!?
11130 // var tw = this.trigger.getWidth();
11131 // // tw += this.addicon ? this.addicon.getWidth() : 0;
11132 // // tw += this.editicon ? this.editicon.getWidth() : 0;
11134 // this.inputEl().setWidth( this.adjustWidth('input', x));
11136 // //this.trigger.setStyle('left', x+'px');
11138 // if(this.list && this.listWidth === undefined){
11139 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
11140 // this.list.setWidth(lw);
11141 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
11149 * Allow or prevent the user from directly editing the field text. If false is passed,
11150 * the user will only be able to select from the items defined in the dropdown list. This method
11151 * is the runtime equivalent of setting the 'editable' config option at config time.
11152 * @param {Boolean} value True to allow the user to directly edit the field text
11154 setEditable : function(value){
11155 if(value == this.editable){
11158 this.editable = value;
11160 this.inputEl().dom.setAttribute('readOnly', true);
11161 this.inputEl().on('mousedown', this.onTriggerClick, this);
11162 this.inputEl().addClass('x-combo-noedit');
11164 this.inputEl().dom.setAttribute('readOnly', false);
11165 this.inputEl().un('mousedown', this.onTriggerClick, this);
11166 this.inputEl().removeClass('x-combo-noedit');
11172 onBeforeLoad : function(combo,opts){
11173 if(!this.hasFocus){
11177 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
11179 this.restrictHeight();
11180 this.selectedIndex = -1;
11184 onLoad : function(){
11186 this.hasQuery = false;
11188 if(!this.hasFocus){
11192 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11193 this.loading.hide();
11196 if(this.store.getCount() > 0){
11198 // this.restrictHeight();
11199 if(this.lastQuery == this.allQuery){
11200 if(this.editable && !this.tickable){
11201 this.inputEl().dom.select();
11205 !this.selectByValue(this.value, true) &&
11206 this.autoFocus && (typeof(this.store.lastOptions.add) == 'undefined' ||
11207 this.store.lastOptions.add != true)
11209 this.select(0, true);
11212 if(this.autoFocus){
11215 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
11216 this.taTask.delay(this.typeAheadDelay);
11220 this.onEmptyResults();
11226 onLoadException : function()
11228 this.hasQuery = false;
11230 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
11231 this.loading.hide();
11235 Roo.log(this.store.reader.jsonData);
11236 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
11238 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
11244 onTypeAhead : function(){
11245 if(this.store.getCount() > 0){
11246 var r = this.store.getAt(0);
11247 var newValue = r.data[this.displayField];
11248 var len = newValue.length;
11249 var selStart = this.getRawValue().length;
11251 if(selStart != len){
11252 this.setRawValue(newValue);
11253 this.selectText(selStart, newValue.length);
11259 onSelect : function(record, index){
11261 if(this.fireEvent('beforeselect', this, record, index) !== false){
11263 this.setFromData(index > -1 ? record.data : false);
11266 this.fireEvent('select', this, record, index);
11271 * Returns the currently selected field value or empty string if no value is set.
11272 * @return {String} value The selected value
11274 getValue : function(){
11277 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
11280 if(this.valueField){
11281 return typeof this.value != 'undefined' ? this.value : '';
11283 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
11288 * Clears any text/value currently set in the field
11290 clearValue : function(){
11291 if(this.hiddenField){
11292 this.hiddenField.dom.value = '';
11295 this.setRawValue('');
11296 this.lastSelectionText = '';
11301 * Sets the specified value into the field. If the value finds a match, the corresponding record text
11302 * will be displayed in the field. If the value does not match the data value of an existing item,
11303 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
11304 * Otherwise the field will be blank (although the value will still be set).
11305 * @param {String} value The value to match
11307 setValue : function(v){
11314 if(this.valueField){
11315 var r = this.findRecord(this.valueField, v);
11317 text = r.data[this.displayField];
11318 }else if(this.valueNotFoundText !== undefined){
11319 text = this.valueNotFoundText;
11322 this.lastSelectionText = text;
11323 if(this.hiddenField){
11324 this.hiddenField.dom.value = v;
11326 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
11330 * @property {Object} the last set data for the element
11335 * Sets the value of the field based on a object which is related to the record format for the store.
11336 * @param {Object} value the value to set as. or false on reset?
11338 setFromData : function(o){
11345 var dv = ''; // display value
11346 var vv = ''; // value value..
11348 if (this.displayField) {
11349 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11351 // this is an error condition!!!
11352 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11355 if(this.valueField){
11356 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
11359 if(this.hiddenField){
11360 this.hiddenField.dom.value = vv;
11362 this.lastSelectionText = dv;
11363 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11367 // no hidden field.. - we store the value in 'value', but still display
11368 // display field!!!!
11369 this.lastSelectionText = dv;
11370 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
11376 reset : function(){
11377 // overridden so that last data is reset..
11378 this.setValue(this.originalValue);
11379 this.clearInvalid();
11380 this.lastData = false;
11382 this.view.clearSelections();
11386 findRecord : function(prop, value){
11388 if(this.store.getCount() > 0){
11389 this.store.each(function(r){
11390 if(r.data[prop] == value){
11400 getName: function()
11402 // returns hidden if it's set..
11403 if (!this.rendered) {return ''};
11404 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
11408 onViewMove : function(e, t){
11409 this.inKeyMode = false;
11413 onViewOver : function(e, t){
11414 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
11417 var item = this.view.findItemFromChild(t);
11420 var index = this.view.indexOf(item);
11421 this.select(index, false);
11426 onViewClick : function(view, doFocus, el, e)
11428 var index = this.view.getSelectedIndexes()[0];
11430 var r = this.store.getAt(index);
11434 if(e.getTarget().nodeName.toLowerCase() != 'input'){
11441 Roo.each(this.tickItems, function(v,k){
11443 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
11444 _this.tickItems.splice(k, 1);
11454 this.tickItems.push(r.data);
11459 this.onSelect(r, index);
11461 if(doFocus !== false && !this.blockFocus){
11462 this.inputEl().focus();
11467 restrictHeight : function(){
11468 //this.innerList.dom.style.height = '';
11469 //var inner = this.innerList.dom;
11470 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
11471 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
11472 //this.list.beginUpdate();
11473 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
11474 this.list.alignTo(this.inputEl(), this.listAlign);
11475 this.list.alignTo(this.inputEl(), this.listAlign);
11476 //this.list.endUpdate();
11480 onEmptyResults : function(){
11485 * Returns true if the dropdown list is expanded, else false.
11487 isExpanded : function(){
11488 return this.list.isVisible();
11492 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
11493 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11494 * @param {String} value The data value of the item to select
11495 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11496 * selected item if it is not currently in view (defaults to true)
11497 * @return {Boolean} True if the value matched an item in the list, else false
11499 selectByValue : function(v, scrollIntoView){
11500 if(v !== undefined && v !== null){
11501 var r = this.findRecord(this.valueField || this.displayField, v);
11503 this.select(this.store.indexOf(r), scrollIntoView);
11511 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
11512 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
11513 * @param {Number} index The zero-based index of the list item to select
11514 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
11515 * selected item if it is not currently in view (defaults to true)
11517 select : function(index, scrollIntoView){
11518 this.selectedIndex = index;
11519 this.view.select(index);
11520 if(scrollIntoView !== false){
11521 var el = this.view.getNode(index);
11522 if(el && !this.multiple && !this.tickable){
11523 this.list.scrollChildIntoView(el, false);
11529 selectNext : function(){
11530 var ct = this.store.getCount();
11532 if(this.selectedIndex == -1){
11534 }else if(this.selectedIndex < ct-1){
11535 this.select(this.selectedIndex+1);
11541 selectPrev : function(){
11542 var ct = this.store.getCount();
11544 if(this.selectedIndex == -1){
11546 }else if(this.selectedIndex != 0){
11547 this.select(this.selectedIndex-1);
11553 onKeyUp : function(e){
11554 if(this.editable !== false && !e.isSpecialKey()){
11555 this.lastKey = e.getKey();
11556 this.dqTask.delay(this.queryDelay);
11561 validateBlur : function(){
11562 return !this.list || !this.list.isVisible();
11566 initQuery : function(){
11567 this.doQuery(this.getRawValue());
11571 doForce : function(){
11572 if(this.inputEl().dom.value.length > 0){
11573 this.inputEl().dom.value =
11574 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
11580 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
11581 * query allowing the query action to be canceled if needed.
11582 * @param {String} query The SQL query to execute
11583 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
11584 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
11585 * saved in the current store (defaults to false)
11587 doQuery : function(q, forceAll){
11589 if(q === undefined || q === null){
11594 forceAll: forceAll,
11598 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
11603 forceAll = qe.forceAll;
11604 if(forceAll === true || (q.length >= this.minChars)){
11606 this.hasQuery = true;
11608 if(this.lastQuery != q || this.alwaysQuery){
11609 this.lastQuery = q;
11610 if(this.mode == 'local'){
11611 this.selectedIndex = -1;
11613 this.store.clearFilter();
11615 this.store.filter(this.displayField, q);
11619 this.store.baseParams[this.queryParam] = q;
11621 var options = {params : this.getParams(q)};
11624 options.add = true;
11625 options.params.start = this.page * this.pageSize;
11628 this.store.load(options);
11630 * this code will make the page width larger, at the beginning, the list not align correctly,
11631 * we should expand the list on onLoad
11632 * so command out it
11637 this.selectedIndex = -1;
11642 this.loadNext = false;
11646 getParams : function(q){
11648 //p[this.queryParam] = q;
11652 p.limit = this.pageSize;
11658 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
11660 collapse : function(){
11661 if(!this.isExpanded()){
11669 this.cancelBtn.hide();
11670 this.trigger.show();
11673 Roo.get(document).un('mousedown', this.collapseIf, this);
11674 Roo.get(document).un('mousewheel', this.collapseIf, this);
11675 if (!this.editable) {
11676 Roo.get(document).un('keydown', this.listKeyPress, this);
11678 this.fireEvent('collapse', this);
11682 collapseIf : function(e){
11683 var in_combo = e.within(this.el);
11684 var in_list = e.within(this.list);
11685 var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
11687 if (in_combo || in_list || is_list) {
11688 //e.stopPropagation();
11693 this.onTickableFooterButtonClick(e, false, false);
11701 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
11703 expand : function(){
11705 if(this.isExpanded() || !this.hasFocus){
11709 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
11710 this.list.setWidth(lw);
11717 this.restrictHeight();
11721 this.tickItems = Roo.apply([], this.item);
11724 this.cancelBtn.show();
11725 this.trigger.hide();
11729 Roo.get(document).on('mousedown', this.collapseIf, this);
11730 Roo.get(document).on('mousewheel', this.collapseIf, this);
11731 if (!this.editable) {
11732 Roo.get(document).on('keydown', this.listKeyPress, this);
11735 this.fireEvent('expand', this);
11739 // Implements the default empty TriggerField.onTriggerClick function
11740 onTriggerClick : function(e)
11742 Roo.log('trigger click');
11744 if(this.disabled || !this.triggerList){
11749 this.loadNext = false;
11751 if(this.isExpanded()){
11753 if (!this.blockFocus) {
11754 this.inputEl().focus();
11758 this.hasFocus = true;
11759 if(this.triggerAction == 'all') {
11760 this.doQuery(this.allQuery, true);
11762 this.doQuery(this.getRawValue());
11764 if (!this.blockFocus) {
11765 this.inputEl().focus();
11770 onTickableTriggerClick : function(e)
11777 this.loadNext = false;
11778 this.hasFocus = true;
11780 if(this.triggerAction == 'all') {
11781 this.doQuery(this.allQuery, true);
11783 this.doQuery(this.getRawValue());
11787 onSearchFieldClick : function(e)
11789 if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
11794 this.loadNext = false;
11795 this.hasFocus = true;
11797 if(this.triggerAction == 'all') {
11798 this.doQuery(this.allQuery, true);
11800 this.doQuery(this.getRawValue());
11804 listKeyPress : function(e)
11806 //Roo.log('listkeypress');
11807 // scroll to first matching element based on key pres..
11808 if (e.isSpecialKey()) {
11811 var k = String.fromCharCode(e.getKey()).toUpperCase();
11814 var csel = this.view.getSelectedNodes();
11815 var cselitem = false;
11817 var ix = this.view.indexOf(csel[0]);
11818 cselitem = this.store.getAt(ix);
11819 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
11825 this.store.each(function(v) {
11827 // start at existing selection.
11828 if (cselitem.id == v.id) {
11834 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
11835 match = this.store.indexOf(v);
11841 if (match === false) {
11842 return true; // no more action?
11845 this.view.select(match);
11846 var sn = Roo.get(this.view.getSelectedNodes()[0])
11847 //sn.scrollIntoView(sn.dom.parentNode, false);
11850 onViewScroll : function(e, t){
11852 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){
11856 this.hasQuery = true;
11858 this.loading = this.list.select('.loading', true).first();
11860 if(this.loading === null){
11861 this.list.createChild({
11863 cls: 'loading select2-more-results select2-active',
11864 html: 'Loading more results...'
11867 this.loading = this.list.select('.loading', true).first();
11869 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
11871 this.loading.hide();
11874 this.loading.show();
11879 this.loadNext = true;
11881 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
11886 addItem : function(o)
11888 var dv = ''; // display value
11890 if (this.displayField) {
11891 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
11893 // this is an error condition!!!
11894 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
11901 var choice = this.choices.createChild({
11903 cls: 'select2-search-choice',
11912 cls: 'select2-search-choice-close',
11917 }, this.searchField);
11919 var close = choice.select('a.select2-search-choice-close', true).first()
11921 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
11929 this.inputEl().dom.value = '';
11933 onRemoveItem : function(e, _self, o)
11935 e.preventDefault();
11936 var index = this.item.indexOf(o.data) * 1;
11939 Roo.log('not this item?!');
11943 this.item.splice(index, 1);
11948 this.fireEvent('remove', this, e);
11952 syncValue : function()
11954 if(!this.item.length){
11961 Roo.each(this.item, function(i){
11962 if(_this.valueField){
11963 value.push(i[_this.valueField]);
11970 this.value = value.join(',');
11972 if(this.hiddenField){
11973 this.hiddenField.dom.value = this.value;
11977 clearItem : function()
11979 if(!this.multiple){
11985 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
11992 inputEl: function ()
11995 return this.searchField;
11997 return this.el.select('input.form-control',true).first();
12001 onTickableFooterButtonClick : function(e, btn, el)
12003 e.preventDefault();
12005 if(btn && btn.name == 'cancel'){
12006 this.tickItems = Roo.apply([], this.item);
12015 Roo.each(this.tickItems, function(o){
12026 * @cfg {Boolean} grow
12030 * @cfg {Number} growMin
12034 * @cfg {Number} growMax
12044 * Ext JS Library 1.1.1
12045 * Copyright(c) 2006-2007, Ext JS, LLC.
12047 * Originally Released Under LGPL - original licence link has changed is not relivant.
12050 * <script type="text/javascript">
12055 * @extends Roo.util.Observable
12056 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
12057 * This class also supports single and multi selection modes. <br>
12058 * Create a data model bound view:
12060 var store = new Roo.data.Store(...);
12062 var view = new Roo.View({
12064 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
12066 singleSelect: true,
12067 selectedClass: "ydataview-selected",
12071 // listen for node click?
12072 view.on("click", function(vw, index, node, e){
12073 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
12077 dataModel.load("foobar.xml");
12079 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
12081 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
12082 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
12084 * Note: old style constructor is still suported (container, template, config)
12087 * Create a new View
12088 * @param {Object} config The config object
12091 Roo.View = function(config, depreciated_tpl, depreciated_config){
12093 this.parent = false;
12095 if (typeof(depreciated_tpl) == 'undefined') {
12096 // new way.. - universal constructor.
12097 Roo.apply(this, config);
12098 this.el = Roo.get(this.el);
12101 this.el = Roo.get(config);
12102 this.tpl = depreciated_tpl;
12103 Roo.apply(this, depreciated_config);
12105 this.wrapEl = this.el.wrap().wrap();
12106 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
12109 if(typeof(this.tpl) == "string"){
12110 this.tpl = new Roo.Template(this.tpl);
12112 // support xtype ctors..
12113 this.tpl = new Roo.factory(this.tpl, Roo);
12117 this.tpl.compile();
12122 * @event beforeclick
12123 * Fires before a click is processed. Returns false to cancel the default action.
12124 * @param {Roo.View} this
12125 * @param {Number} index The index of the target node
12126 * @param {HTMLElement} node The target node
12127 * @param {Roo.EventObject} e The raw event object
12129 "beforeclick" : true,
12132 * Fires when a template node is clicked.
12133 * @param {Roo.View} this
12134 * @param {Number} index The index of the target node
12135 * @param {HTMLElement} node The target node
12136 * @param {Roo.EventObject} e The raw event object
12141 * Fires when a template node is double clicked.
12142 * @param {Roo.View} this
12143 * @param {Number} index The index of the target node
12144 * @param {HTMLElement} node The target node
12145 * @param {Roo.EventObject} e The raw event object
12149 * @event contextmenu
12150 * Fires when a template node is right clicked.
12151 * @param {Roo.View} this
12152 * @param {Number} index The index of the target node
12153 * @param {HTMLElement} node The target node
12154 * @param {Roo.EventObject} e The raw event object
12156 "contextmenu" : true,
12158 * @event selectionchange
12159 * Fires when the selected nodes change.
12160 * @param {Roo.View} this
12161 * @param {Array} selections Array of the selected nodes
12163 "selectionchange" : true,
12166 * @event beforeselect
12167 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
12168 * @param {Roo.View} this
12169 * @param {HTMLElement} node The node to be selected
12170 * @param {Array} selections Array of currently selected nodes
12172 "beforeselect" : true,
12174 * @event preparedata
12175 * Fires on every row to render, to allow you to change the data.
12176 * @param {Roo.View} this
12177 * @param {Object} data to be rendered (change this)
12179 "preparedata" : true
12187 "click": this.onClick,
12188 "dblclick": this.onDblClick,
12189 "contextmenu": this.onContextMenu,
12193 this.selections = [];
12195 this.cmp = new Roo.CompositeElementLite([]);
12197 this.store = Roo.factory(this.store, Roo.data);
12198 this.setStore(this.store, true);
12201 if ( this.footer && this.footer.xtype) {
12203 var fctr = this.wrapEl.appendChild(document.createElement("div"));
12205 this.footer.dataSource = this.store
12206 this.footer.container = fctr;
12207 this.footer = Roo.factory(this.footer, Roo);
12208 fctr.insertFirst(this.el);
12210 // this is a bit insane - as the paging toolbar seems to detach the el..
12211 // dom.parentNode.parentNode.parentNode
12212 // they get detached?
12216 Roo.View.superclass.constructor.call(this);
12221 Roo.extend(Roo.View, Roo.util.Observable, {
12224 * @cfg {Roo.data.Store} store Data store to load data from.
12229 * @cfg {String|Roo.Element} el The container element.
12234 * @cfg {String|Roo.Template} tpl The template used by this View
12238 * @cfg {String} dataName the named area of the template to use as the data area
12239 * Works with domtemplates roo-name="name"
12243 * @cfg {String} selectedClass The css class to add to selected nodes
12245 selectedClass : "x-view-selected",
12247 * @cfg {String} emptyText The empty text to show when nothing is loaded.
12252 * @cfg {String} text to display on mask (default Loading)
12256 * @cfg {Boolean} multiSelect Allow multiple selection
12258 multiSelect : false,
12260 * @cfg {Boolean} singleSelect Allow single selection
12262 singleSelect: false,
12265 * @cfg {Boolean} toggleSelect - selecting
12267 toggleSelect : false,
12270 * @cfg {Boolean} tickable - selecting
12275 * Returns the element this view is bound to.
12276 * @return {Roo.Element}
12278 getEl : function(){
12279 return this.wrapEl;
12285 * Refreshes the view. - called by datachanged on the store. - do not call directly.
12287 refresh : function(){
12288 Roo.log('refresh');
12291 // if we are using something like 'domtemplate', then
12292 // the what gets used is:
12293 // t.applySubtemplate(NAME, data, wrapping data..)
12294 // the outer template then get' applied with
12295 // the store 'extra data'
12296 // and the body get's added to the
12297 // roo-name="data" node?
12298 // <span class='roo-tpl-{name}'></span> ?????
12302 this.clearSelections();
12303 this.el.update("");
12305 var records = this.store.getRange();
12306 if(records.length < 1) {
12308 // is this valid?? = should it render a template??
12310 this.el.update(this.emptyText);
12314 if (this.dataName) {
12315 this.el.update(t.apply(this.store.meta)); //????
12316 el = this.el.child('.roo-tpl-' + this.dataName);
12319 for(var i = 0, len = records.length; i < len; i++){
12320 var data = this.prepareData(records[i].data, i, records[i]);
12321 this.fireEvent("preparedata", this, data, i, records[i]);
12323 var d = Roo.apply({}, data);
12326 Roo.apply(d, {'roo-id' : Roo.id()});
12330 Roo.each(this.parent.item, function(item){
12331 if(item[_this.parent.valueField] != data[_this.parent.valueField]){
12334 Roo.apply(d, {'roo-data-checked' : 'checked'});
12338 html[html.length] = Roo.util.Format.trim(
12340 t.applySubtemplate(this.dataName, d, this.store.meta) :
12347 el.update(html.join(""));
12348 this.nodes = el.dom.childNodes;
12349 this.updateIndexes(0);
12354 * Function to override to reformat the data that is sent to
12355 * the template for each node.
12356 * DEPRICATED - use the preparedata event handler.
12357 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
12358 * a JSON object for an UpdateManager bound view).
12360 prepareData : function(data, index, record)
12362 this.fireEvent("preparedata", this, data, index, record);
12366 onUpdate : function(ds, record){
12367 Roo.log('on update');
12368 this.clearSelections();
12369 var index = this.store.indexOf(record);
12370 var n = this.nodes[index];
12371 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
12372 n.parentNode.removeChild(n);
12373 this.updateIndexes(index, index);
12379 onAdd : function(ds, records, index)
12381 Roo.log(['on Add', ds, records, index] );
12382 this.clearSelections();
12383 if(this.nodes.length == 0){
12387 var n = this.nodes[index];
12388 for(var i = 0, len = records.length; i < len; i++){
12389 var d = this.prepareData(records[i].data, i, records[i]);
12391 this.tpl.insertBefore(n, d);
12394 this.tpl.append(this.el, d);
12397 this.updateIndexes(index);
12400 onRemove : function(ds, record, index){
12401 Roo.log('onRemove');
12402 this.clearSelections();
12403 var el = this.dataName ?
12404 this.el.child('.roo-tpl-' + this.dataName) :
12407 el.dom.removeChild(this.nodes[index]);
12408 this.updateIndexes(index);
12412 * Refresh an individual node.
12413 * @param {Number} index
12415 refreshNode : function(index){
12416 this.onUpdate(this.store, this.store.getAt(index));
12419 updateIndexes : function(startIndex, endIndex){
12420 var ns = this.nodes;
12421 startIndex = startIndex || 0;
12422 endIndex = endIndex || ns.length - 1;
12423 for(var i = startIndex; i <= endIndex; i++){
12424 ns[i].nodeIndex = i;
12429 * Changes the data store this view uses and refresh the view.
12430 * @param {Store} store
12432 setStore : function(store, initial){
12433 if(!initial && this.store){
12434 this.store.un("datachanged", this.refresh);
12435 this.store.un("add", this.onAdd);
12436 this.store.un("remove", this.onRemove);
12437 this.store.un("update", this.onUpdate);
12438 this.store.un("clear", this.refresh);
12439 this.store.un("beforeload", this.onBeforeLoad);
12440 this.store.un("load", this.onLoad);
12441 this.store.un("loadexception", this.onLoad);
12445 store.on("datachanged", this.refresh, this);
12446 store.on("add", this.onAdd, this);
12447 store.on("remove", this.onRemove, this);
12448 store.on("update", this.onUpdate, this);
12449 store.on("clear", this.refresh, this);
12450 store.on("beforeload", this.onBeforeLoad, this);
12451 store.on("load", this.onLoad, this);
12452 store.on("loadexception", this.onLoad, this);
12460 * onbeforeLoad - masks the loading area.
12463 onBeforeLoad : function(store,opts)
12465 Roo.log('onBeforeLoad');
12467 this.el.update("");
12469 this.el.mask(this.mask ? this.mask : "Loading" );
12471 onLoad : function ()
12478 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
12479 * @param {HTMLElement} node
12480 * @return {HTMLElement} The template node
12482 findItemFromChild : function(node){
12483 var el = this.dataName ?
12484 this.el.child('.roo-tpl-' + this.dataName,true) :
12487 if(!node || node.parentNode == el){
12490 var p = node.parentNode;
12491 while(p && p != el){
12492 if(p.parentNode == el){
12501 onClick : function(e){
12502 var item = this.findItemFromChild(e.getTarget());
12504 var index = this.indexOf(item);
12505 if(this.onItemClick(item, index, e) !== false){
12506 this.fireEvent("click", this, index, item, e);
12509 this.clearSelections();
12514 onContextMenu : function(e){
12515 var item = this.findItemFromChild(e.getTarget());
12517 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
12522 onDblClick : function(e){
12523 var item = this.findItemFromChild(e.getTarget());
12525 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
12529 onItemClick : function(item, index, e)
12531 if(this.fireEvent("beforeclick", this, index, item, e) === false){
12534 if (this.toggleSelect) {
12535 var m = this.isSelected(item) ? 'unselect' : 'select';
12538 _t[m](item, true, false);
12541 if(this.multiSelect || this.singleSelect){
12542 if(this.multiSelect && e.shiftKey && this.lastSelection){
12543 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
12545 this.select(item, this.multiSelect && e.ctrlKey);
12546 this.lastSelection = item;
12549 if(!this.tickable){
12550 e.preventDefault();
12558 * Get the number of selected nodes.
12561 getSelectionCount : function(){
12562 return this.selections.length;
12566 * Get the currently selected nodes.
12567 * @return {Array} An array of HTMLElements
12569 getSelectedNodes : function(){
12570 return this.selections;
12574 * Get the indexes of the selected nodes.
12577 getSelectedIndexes : function(){
12578 var indexes = [], s = this.selections;
12579 for(var i = 0, len = s.length; i < len; i++){
12580 indexes.push(s[i].nodeIndex);
12586 * Clear all selections
12587 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
12589 clearSelections : function(suppressEvent){
12590 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
12591 this.cmp.elements = this.selections;
12592 this.cmp.removeClass(this.selectedClass);
12593 this.selections = [];
12594 if(!suppressEvent){
12595 this.fireEvent("selectionchange", this, this.selections);
12601 * Returns true if the passed node is selected
12602 * @param {HTMLElement/Number} node The node or node index
12603 * @return {Boolean}
12605 isSelected : function(node){
12606 var s = this.selections;
12610 node = this.getNode(node);
12611 return s.indexOf(node) !== -1;
12616 * @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
12617 * @param {Boolean} keepExisting (optional) true to keep existing selections
12618 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12620 select : function(nodeInfo, keepExisting, suppressEvent){
12621 Roo.log('running Roo.View select!!!!!!!!!!!!!!!!!!!!!!1');
12622 if(nodeInfo instanceof Array){
12624 this.clearSelections(true);
12626 for(var i = 0, len = nodeInfo.length; i < len; i++){
12627 this.select(nodeInfo[i], true, true);
12631 var node = this.getNode(nodeInfo);
12632 if(!node || this.isSelected(node)){
12633 return; // already selected.
12636 this.clearSelections(true);
12639 Roo.log(this.parent);
12640 Roo.log(this.list);
12641 // var el = this.view.getNode(index);
12642 // if(el && !this.multiple && !this.tickable){
12643 // this.list.scrollChildIntoView(el, false);
12646 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
12647 Roo.fly(node).addClass(this.selectedClass);
12648 this.selections.push(node);
12649 if(!suppressEvent){
12650 this.fireEvent("selectionchange", this, this.selections);
12658 * @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
12659 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
12660 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
12662 unselect : function(nodeInfo, keepExisting, suppressEvent)
12664 if(nodeInfo instanceof Array){
12665 Roo.each(this.selections, function(s) {
12666 this.unselect(s, nodeInfo);
12670 var node = this.getNode(nodeInfo);
12671 if(!node || !this.isSelected(node)){
12672 Roo.log("not selected");
12673 return; // not selected.
12677 Roo.each(this.selections, function(s) {
12679 Roo.fly(node).removeClass(this.selectedClass);
12686 this.selections= ns;
12687 this.fireEvent("selectionchange", this, this.selections);
12691 * Gets a template node.
12692 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12693 * @return {HTMLElement} The node or null if it wasn't found
12695 getNode : function(nodeInfo){
12696 if(typeof nodeInfo == "string"){
12697 return document.getElementById(nodeInfo);
12698 }else if(typeof nodeInfo == "number"){
12699 return this.nodes[nodeInfo];
12705 * Gets a range template nodes.
12706 * @param {Number} startIndex
12707 * @param {Number} endIndex
12708 * @return {Array} An array of nodes
12710 getNodes : function(start, end){
12711 var ns = this.nodes;
12712 start = start || 0;
12713 end = typeof end == "undefined" ? ns.length - 1 : end;
12716 for(var i = start; i <= end; i++){
12720 for(var i = start; i >= end; i--){
12728 * Finds the index of the passed node
12729 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
12730 * @return {Number} The index of the node or -1
12732 indexOf : function(node){
12733 node = this.getNode(node);
12734 if(typeof node.nodeIndex == "number"){
12735 return node.nodeIndex;
12737 var ns = this.nodes;
12738 for(var i = 0, len = ns.length; i < len; i++){
12749 * based on jquery fullcalendar
12753 Roo.bootstrap = Roo.bootstrap || {};
12755 * @class Roo.bootstrap.Calendar
12756 * @extends Roo.bootstrap.Component
12757 * Bootstrap Calendar class
12758 * @cfg {Boolean} loadMask (true|false) default false
12759 * @cfg {Object} header generate the user specific header of the calendar, default false
12762 * Create a new Container
12763 * @param {Object} config The config object
12768 Roo.bootstrap.Calendar = function(config){
12769 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
12773 * Fires when a date is selected
12774 * @param {DatePicker} this
12775 * @param {Date} date The selected date
12779 * @event monthchange
12780 * Fires when the displayed month changes
12781 * @param {DatePicker} this
12782 * @param {Date} date The selected month
12784 'monthchange': true,
12786 * @event evententer
12787 * Fires when mouse over an event
12788 * @param {Calendar} this
12789 * @param {event} Event
12791 'evententer': true,
12793 * @event eventleave
12794 * Fires when the mouse leaves an
12795 * @param {Calendar} this
12798 'eventleave': true,
12800 * @event eventclick
12801 * Fires when the mouse click an
12802 * @param {Calendar} this
12811 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
12814 * @cfg {Number} startDay
12815 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
12823 getAutoCreate : function(){
12826 var fc_button = function(name, corner, style, content ) {
12827 return Roo.apply({},{
12829 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
12831 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
12834 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
12845 style : 'width:100%',
12852 cls : 'fc-header-left',
12854 fc_button('prev', 'left', 'arrow', '‹' ),
12855 fc_button('next', 'right', 'arrow', '›' ),
12856 { tag: 'span', cls: 'fc-header-space' },
12857 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
12865 cls : 'fc-header-center',
12869 cls: 'fc-header-title',
12872 html : 'month / year'
12880 cls : 'fc-header-right',
12882 /* fc_button('month', 'left', '', 'month' ),
12883 fc_button('week', '', '', 'week' ),
12884 fc_button('day', 'right', '', 'day' )
12896 header = this.header;
12899 var cal_heads = function() {
12901 // fixme - handle this.
12903 for (var i =0; i < Date.dayNames.length; i++) {
12904 var d = Date.dayNames[i];
12907 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
12908 html : d.substring(0,3)
12912 ret[0].cls += ' fc-first';
12913 ret[6].cls += ' fc-last';
12916 var cal_cell = function(n) {
12919 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
12924 cls: 'fc-day-number',
12928 cls: 'fc-day-content',
12932 style: 'position: relative;' // height: 17px;
12944 var cal_rows = function() {
12947 for (var r = 0; r < 6; r++) {
12954 for (var i =0; i < Date.dayNames.length; i++) {
12955 var d = Date.dayNames[i];
12956 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
12959 row.cn[0].cls+=' fc-first';
12960 row.cn[0].cn[0].style = 'min-height:90px';
12961 row.cn[6].cls+=' fc-last';
12965 ret[0].cls += ' fc-first';
12966 ret[4].cls += ' fc-prev-last';
12967 ret[5].cls += ' fc-last';
12974 cls: 'fc-border-separate',
12975 style : 'width:100%',
12983 cls : 'fc-first fc-last',
13001 cls : 'fc-content',
13002 style : "position: relative;",
13005 cls : 'fc-view fc-view-month fc-grid',
13006 style : 'position: relative',
13007 unselectable : 'on',
13010 cls : 'fc-event-container',
13011 style : 'position:absolute;z-index:8;top:0;left:0;'
13029 initEvents : function()
13032 throw "can not find store for calendar";
13038 style: "text-align:center",
13042 style: "background-color:white;width:50%;margin:250 auto",
13046 src: Roo.rootURL + '/images/ux/lightbox/loading.gif'
13057 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
13059 var size = this.el.select('.fc-content', true).first().getSize();
13060 this.maskEl.setSize(size.width, size.height);
13061 this.maskEl.enableDisplayMode("block");
13062 if(!this.loadMask){
13063 this.maskEl.hide();
13066 this.store = Roo.factory(this.store, Roo.data);
13067 this.store.on('load', this.onLoad, this);
13068 this.store.on('beforeload', this.onBeforeLoad, this);
13072 this.cells = this.el.select('.fc-day',true);
13073 //Roo.log(this.cells);
13074 this.textNodes = this.el.query('.fc-day-number');
13075 this.cells.addClassOnOver('fc-state-hover');
13077 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
13078 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
13079 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
13080 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
13082 this.on('monthchange', this.onMonthChange, this);
13084 this.update(new Date().clearTime());
13087 resize : function() {
13088 var sz = this.el.getSize();
13090 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
13091 this.el.select('.fc-day-content div',true).setHeight(34);
13096 showPrevMonth : function(e){
13097 this.update(this.activeDate.add("mo", -1));
13099 showToday : function(e){
13100 this.update(new Date().clearTime());
13103 showNextMonth : function(e){
13104 this.update(this.activeDate.add("mo", 1));
13108 showPrevYear : function(){
13109 this.update(this.activeDate.add("y", -1));
13113 showNextYear : function(){
13114 this.update(this.activeDate.add("y", 1));
13119 update : function(date)
13121 var vd = this.activeDate;
13122 this.activeDate = date;
13123 // if(vd && this.el){
13124 // var t = date.getTime();
13125 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
13126 // Roo.log('using add remove');
13128 // this.fireEvent('monthchange', this, date);
13130 // this.cells.removeClass("fc-state-highlight");
13131 // this.cells.each(function(c){
13132 // if(c.dateValue == t){
13133 // c.addClass("fc-state-highlight");
13134 // setTimeout(function(){
13135 // try{c.dom.firstChild.focus();}catch(e){}
13145 var days = date.getDaysInMonth();
13147 var firstOfMonth = date.getFirstDateOfMonth();
13148 var startingPos = firstOfMonth.getDay()-this.startDay;
13150 if(startingPos < this.startDay){
13154 var pm = date.add(Date.MONTH, -1);
13155 var prevStart = pm.getDaysInMonth()-startingPos;
13157 this.cells = this.el.select('.fc-day',true);
13158 this.textNodes = this.el.query('.fc-day-number');
13159 this.cells.addClassOnOver('fc-state-hover');
13161 var cells = this.cells.elements;
13162 var textEls = this.textNodes;
13164 Roo.each(cells, function(cell){
13165 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
13168 days += startingPos;
13170 // convert everything to numbers so it's fast
13171 var day = 86400000;
13172 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
13175 //Roo.log(prevStart);
13177 var today = new Date().clearTime().getTime();
13178 var sel = date.clearTime().getTime();
13179 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
13180 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
13181 var ddMatch = this.disabledDatesRE;
13182 var ddText = this.disabledDatesText;
13183 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
13184 var ddaysText = this.disabledDaysText;
13185 var format = this.format;
13187 var setCellClass = function(cal, cell){
13191 //Roo.log('set Cell Class');
13193 var t = d.getTime();
13197 cell.dateValue = t;
13199 cell.className += " fc-today";
13200 cell.className += " fc-state-highlight";
13201 cell.title = cal.todayText;
13204 // disable highlight in other month..
13205 //cell.className += " fc-state-highlight";
13210 cell.className = " fc-state-disabled";
13211 cell.title = cal.minText;
13215 cell.className = " fc-state-disabled";
13216 cell.title = cal.maxText;
13220 if(ddays.indexOf(d.getDay()) != -1){
13221 cell.title = ddaysText;
13222 cell.className = " fc-state-disabled";
13225 if(ddMatch && format){
13226 var fvalue = d.dateFormat(format);
13227 if(ddMatch.test(fvalue)){
13228 cell.title = ddText.replace("%0", fvalue);
13229 cell.className = " fc-state-disabled";
13233 if (!cell.initialClassName) {
13234 cell.initialClassName = cell.dom.className;
13237 cell.dom.className = cell.initialClassName + ' ' + cell.className;
13242 for(; i < startingPos; i++) {
13243 textEls[i].innerHTML = (++prevStart);
13244 d.setDate(d.getDate()+1);
13246 cells[i].className = "fc-past fc-other-month";
13247 setCellClass(this, cells[i]);
13252 for(; i < days; i++){
13253 intDay = i - startingPos + 1;
13254 textEls[i].innerHTML = (intDay);
13255 d.setDate(d.getDate()+1);
13257 cells[i].className = ''; // "x-date-active";
13258 setCellClass(this, cells[i]);
13262 for(; i < 42; i++) {
13263 textEls[i].innerHTML = (++extraDays);
13264 d.setDate(d.getDate()+1);
13266 cells[i].className = "fc-future fc-other-month";
13267 setCellClass(this, cells[i]);
13270 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
13272 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
13274 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
13275 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
13277 if(totalRows != 6){
13278 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
13279 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
13282 this.fireEvent('monthchange', this, date);
13286 if(!this.internalRender){
13287 var main = this.el.dom.firstChild;
13288 var w = main.offsetWidth;
13289 this.el.setWidth(w + this.el.getBorderWidth("lr"));
13290 Roo.fly(main).setWidth(w);
13291 this.internalRender = true;
13292 // opera does not respect the auto grow header center column
13293 // then, after it gets a width opera refuses to recalculate
13294 // without a second pass
13295 if(Roo.isOpera && !this.secondPass){
13296 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
13297 this.secondPass = true;
13298 this.update.defer(10, this, [date]);
13305 findCell : function(dt) {
13306 dt = dt.clearTime().getTime();
13308 this.cells.each(function(c){
13309 //Roo.log("check " +c.dateValue + '?=' + dt);
13310 if(c.dateValue == dt){
13320 findCells : function(ev) {
13321 var s = ev.start.clone().clearTime().getTime();
13323 var e= ev.end.clone().clearTime().getTime();
13326 this.cells.each(function(c){
13327 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
13329 if(c.dateValue > e){
13332 if(c.dateValue < s){
13341 // findBestRow: function(cells)
13345 // for (var i =0 ; i < cells.length;i++) {
13346 // ret = Math.max(cells[i].rows || 0,ret);
13353 addItem : function(ev)
13355 // look for vertical location slot in
13356 var cells = this.findCells(ev);
13358 // ev.row = this.findBestRow(cells);
13360 // work out the location.
13364 for(var i =0; i < cells.length; i++) {
13366 cells[i].row = cells[0].row;
13369 cells[i].row = cells[i].row + 1;
13379 if (crow.start.getY() == cells[i].getY()) {
13381 crow.end = cells[i];
13398 cells[0].events.push(ev);
13400 this.calevents.push(ev);
13403 clearEvents: function() {
13405 if(!this.calevents){
13409 Roo.each(this.cells.elements, function(c){
13415 Roo.each(this.calevents, function(e) {
13416 Roo.each(e.els, function(el) {
13417 el.un('mouseenter' ,this.onEventEnter, this);
13418 el.un('mouseleave' ,this.onEventLeave, this);
13423 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
13429 renderEvents: function()
13433 this.cells.each(function(c) {
13442 if(c.row != c.events.length){
13443 r = 4 - (4 - (c.row - c.events.length));
13446 c.events = ev.slice(0, r);
13447 c.more = ev.slice(r);
13449 if(c.more.length && c.more.length == 1){
13450 c.events.push(c.more.pop());
13453 c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
13457 this.cells.each(function(c) {
13459 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
13462 for (var e = 0; e < c.events.length; e++){
13463 var ev = c.events[e];
13464 var rows = ev.rows;
13466 for(var i = 0; i < rows.length; i++) {
13468 // how many rows should it span..
13471 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
13472 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
13474 unselectable : "on",
13477 cls: 'fc-event-inner',
13481 // cls: 'fc-event-time',
13482 // html : cells.length > 1 ? '' : ev.time
13486 cls: 'fc-event-title',
13487 html : String.format('{0}', ev.title)
13494 cls: 'ui-resizable-handle ui-resizable-e',
13495 html : '  '
13502 cfg.cls += ' fc-event-start';
13504 if ((i+1) == rows.length) {
13505 cfg.cls += ' fc-event-end';
13508 var ctr = _this.el.select('.fc-event-container',true).first();
13509 var cg = ctr.createChild(cfg);
13511 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
13512 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
13514 var r = (c.more.length) ? 1 : 0;
13515 cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);
13516 cg.setWidth(ebox.right - sbox.x -2);
13518 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
13519 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
13520 cg.on('click', _this.onEventClick, _this, ev);
13531 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
13532 style : 'position: absolute',
13533 unselectable : "on",
13536 cls: 'fc-event-inner',
13540 cls: 'fc-event-title',
13548 cls: 'ui-resizable-handle ui-resizable-e',
13549 html : '  '
13555 var ctr = _this.el.select('.fc-event-container',true).first();
13556 var cg = ctr.createChild(cfg);
13558 var sbox = c.select('.fc-day-content',true).first().getBox();
13559 var ebox = c.select('.fc-day-content',true).first().getBox();
13561 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);
13562 cg.setWidth(ebox.right - sbox.x -2);
13564 cg.on('click', _this.onMoreEventClick, _this, c.more);
13574 onEventEnter: function (e, el,event,d) {
13575 this.fireEvent('evententer', this, el, event);
13578 onEventLeave: function (e, el,event,d) {
13579 this.fireEvent('eventleave', this, el, event);
13582 onEventClick: function (e, el,event,d) {
13583 this.fireEvent('eventclick', this, el, event);
13586 onMonthChange: function () {
13590 onMoreEventClick: function(e, el, more)
13594 this.calpopover.placement = 'right';
13595 this.calpopover.setTitle('More');
13597 this.calpopover.setContent('');
13599 var ctr = this.calpopover.el.select('.popover-content', true).first();
13601 Roo.each(more, function(m){
13603 cls : 'fc-event-hori fc-event-draggable',
13606 var cg = ctr.createChild(cfg);
13608 cg.on('click', _this.onEventClick, _this, m);
13611 this.calpopover.show(el);
13616 onLoad: function ()
13618 this.calevents = [];
13621 if(this.store.getCount() > 0){
13622 this.store.data.each(function(d){
13625 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
13626 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
13627 time : d.data.start_time,
13628 title : d.data.title,
13629 description : d.data.description,
13630 venue : d.data.venue
13635 this.renderEvents();
13637 if(this.calevents.length && this.loadMask){
13638 this.maskEl.hide();
13642 onBeforeLoad: function()
13644 this.clearEvents();
13646 this.maskEl.show();
13660 * @class Roo.bootstrap.Popover
13661 * @extends Roo.bootstrap.Component
13662 * Bootstrap Popover class
13663 * @cfg {String} html contents of the popover (or false to use children..)
13664 * @cfg {String} title of popover (or false to hide)
13665 * @cfg {String} placement how it is placed
13666 * @cfg {String} trigger click || hover (or false to trigger manually)
13667 * @cfg {String} over what (parent or false to trigger manually.)
13668 * @cfg {Number} delay - delay before showing
13671 * Create a new Popover
13672 * @param {Object} config The config object
13675 Roo.bootstrap.Popover = function(config){
13676 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
13679 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
13681 title: 'Fill in a title',
13684 placement : 'right',
13685 trigger : 'hover', // hover
13691 can_build_overlaid : false,
13693 getChildContainer : function()
13695 return this.el.select('.popover-content',true).first();
13698 getAutoCreate : function(){
13699 Roo.log('make popover?');
13701 cls : 'popover roo-dynamic',
13702 style: 'display:block',
13708 cls : 'popover-inner',
13712 cls: 'popover-title',
13716 cls : 'popover-content',
13727 setTitle: function(str)
13729 this.el.select('.popover-title',true).first().dom.innerHTML = str;
13731 setContent: function(str)
13733 this.el.select('.popover-content',true).first().dom.innerHTML = str;
13735 // as it get's added to the bottom of the page.
13736 onRender : function(ct, position)
13738 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
13740 var cfg = Roo.apply({}, this.getAutoCreate());
13744 cfg.cls += ' ' + this.cls;
13747 cfg.style = this.style;
13749 Roo.log("adding to ")
13750 this.el = Roo.get(document.body).createChild(cfg, position);
13756 initEvents : function()
13758 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
13759 this.el.enableDisplayMode('block');
13761 if (this.over === false) {
13764 if (this.triggers === false) {
13767 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13768 var triggers = this.trigger ? this.trigger.split(' ') : [];
13769 Roo.each(triggers, function(trigger) {
13771 if (trigger == 'click') {
13772 on_el.on('click', this.toggle, this);
13773 } else if (trigger != 'manual') {
13774 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
13775 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
13777 on_el.on(eventIn ,this.enter, this);
13778 on_el.on(eventOut, this.leave, this);
13789 toggle : function () {
13790 this.hoverState == 'in' ? this.leave() : this.enter();
13793 enter : function () {
13796 clearTimeout(this.timeout);
13798 this.hoverState = 'in'
13800 if (!this.delay || !this.delay.show) {
13805 this.timeout = setTimeout(function () {
13806 if (_t.hoverState == 'in') {
13809 }, this.delay.show)
13811 leave : function() {
13812 clearTimeout(this.timeout);
13814 this.hoverState = 'out'
13816 if (!this.delay || !this.delay.hide) {
13821 this.timeout = setTimeout(function () {
13822 if (_t.hoverState == 'out') {
13825 }, this.delay.hide)
13828 show : function (on_el)
13831 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
13834 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
13835 if (this.html !== false) {
13836 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
13838 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
13839 if (!this.title.length) {
13840 this.el.select('.popover-title',true).hide();
13843 var placement = typeof this.placement == 'function' ?
13844 this.placement.call(this, this.el, on_el) :
13847 var autoToken = /\s?auto?\s?/i;
13848 var autoPlace = autoToken.test(placement);
13850 placement = placement.replace(autoToken, '') || 'top';
13854 //this.el.setXY([0,0]);
13856 this.el.dom.style.display='block';
13857 this.el.addClass(placement);
13859 //this.el.appendTo(on_el);
13861 var p = this.getPosition();
13862 var box = this.el.getBox();
13867 var align = Roo.bootstrap.Popover.alignment[placement]
13868 this.el.alignTo(on_el, align[0],align[1]);
13869 //var arrow = this.el.select('.arrow',true).first();
13870 //arrow.set(align[2],
13872 this.el.addClass('in');
13873 this.hoverState = null;
13875 if (this.el.hasClass('fade')) {
13882 this.el.setXY([0,0]);
13883 this.el.removeClass('in');
13890 Roo.bootstrap.Popover.alignment = {
13891 'left' : ['r-l', [-10,0], 'right'],
13892 'right' : ['l-r', [10,0], 'left'],
13893 'bottom' : ['t-b', [0,10], 'top'],
13894 'top' : [ 'b-t', [0,-10], 'bottom']
13905 * @class Roo.bootstrap.Progress
13906 * @extends Roo.bootstrap.Component
13907 * Bootstrap Progress class
13908 * @cfg {Boolean} striped striped of the progress bar
13909 * @cfg {Boolean} active animated of the progress bar
13913 * Create a new Progress
13914 * @param {Object} config The config object
13917 Roo.bootstrap.Progress = function(config){
13918 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
13921 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
13926 getAutoCreate : function(){
13934 cfg.cls += ' progress-striped';
13938 cfg.cls += ' active';
13957 * @class Roo.bootstrap.ProgressBar
13958 * @extends Roo.bootstrap.Component
13959 * Bootstrap ProgressBar class
13960 * @cfg {Number} aria_valuenow aria-value now
13961 * @cfg {Number} aria_valuemin aria-value min
13962 * @cfg {Number} aria_valuemax aria-value max
13963 * @cfg {String} label label for the progress bar
13964 * @cfg {String} panel (success | info | warning | danger )
13965 * @cfg {String} role role of the progress bar
13966 * @cfg {String} sr_only text
13970 * Create a new ProgressBar
13971 * @param {Object} config The config object
13974 Roo.bootstrap.ProgressBar = function(config){
13975 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
13978 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
13982 aria_valuemax : 100,
13988 getAutoCreate : function()
13993 cls: 'progress-bar',
13994 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
14006 cfg.role = this.role;
14009 if(this.aria_valuenow){
14010 cfg['aria-valuenow'] = this.aria_valuenow;
14013 if(this.aria_valuemin){
14014 cfg['aria-valuemin'] = this.aria_valuemin;
14017 if(this.aria_valuemax){
14018 cfg['aria-valuemax'] = this.aria_valuemax;
14021 if(this.label && !this.sr_only){
14022 cfg.html = this.label;
14026 cfg.cls += ' progress-bar-' + this.panel;
14032 update : function(aria_valuenow)
14034 this.aria_valuenow = aria_valuenow;
14036 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
14051 * @class Roo.bootstrap.TabGroup
14052 * @extends Roo.bootstrap.Column
14053 * Bootstrap Column class
14054 * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
14055 * @cfg {Boolean} carousel true to make the group behave like a carousel
14058 * Create a new TabGroup
14059 * @param {Object} config The config object
14062 Roo.bootstrap.TabGroup = function(config){
14063 Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
14065 this.navId = Roo.id();
14068 Roo.bootstrap.TabGroup.register(this);
14072 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column, {
14075 transition : false,
14077 getAutoCreate : function()
14079 var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
14081 cfg.cls += ' tab-content';
14083 if (this.carousel) {
14084 cfg.cls += ' carousel slide';
14086 cls : 'carousel-inner'
14093 getChildContainer : function()
14095 return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
14099 * register a Navigation item
14100 * @param {Roo.bootstrap.NavItem} the navitem to add
14102 register : function(item)
14104 this.tabs.push( item);
14105 item.navId = this.navId; // not really needed..
14109 getActivePanel : function()
14112 Roo.each(this.tabs, function(t) {
14122 getPanelByName : function(n)
14125 Roo.each(this.tabs, function(t) {
14126 if (t.tabId == n) {
14134 indexOfPanel : function(p)
14137 Roo.each(this.tabs, function(t,i) {
14138 if (t.tabId == p.tabId) {
14147 * show a specific panel
14148 * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
14149 * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
14151 showPanel : function (pan)
14154 if (typeof(pan) == 'number') {
14155 pan = this.tabs[pan];
14157 if (typeof(pan) == 'string') {
14158 pan = this.getPanelByName(pan);
14160 if (pan.tabId == this.getActivePanel().tabId) {
14163 var cur = this.getActivePanel();
14165 if (false === cur.fireEvent('beforedeactivate')) {
14169 if (this.carousel) {
14170 this.transition = true;
14171 var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur) ? 'next' : 'prev';
14172 var lr = dir == 'next' ? 'left' : 'right';
14173 pan.el.addClass(dir); // or prev
14174 pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
14175 cur.el.addClass(lr); // or right
14176 pan.el.addClass(lr);
14179 cur.el.on('transitionend', function() {
14180 Roo.log("trans end?");
14182 pan.el.removeClass([lr,dir]);
14183 pan.setActive(true);
14185 cur.el.removeClass([lr]);
14186 cur.setActive(false);
14188 _this.transition = false;
14190 }, this, { single: true } );
14194 cur.setActive(false);
14195 pan.setActive(true);
14199 showPanelNext : function()
14201 var i = this.indexOfPanel(this.getActivePanel());
14202 if (i > this.tabs.length) {
14205 this.showPanel(this.tabs[i+1]);
14207 showPanelPrev : function()
14209 var i = this.indexOfPanel(this.getActivePanel());
14213 this.showPanel(this.tabs[i-1]);
14224 Roo.apply(Roo.bootstrap.TabGroup, {
14228 * register a Navigation Group
14229 * @param {Roo.bootstrap.NavGroup} the navgroup to add
14231 register : function(navgrp)
14233 this.groups[navgrp.navId] = navgrp;
14237 * fetch a Navigation Group based on the navigation ID
14238 * if one does not exist , it will get created.
14239 * @param {string} the navgroup to add
14240 * @returns {Roo.bootstrap.NavGroup} the navgroup
14242 get: function(navId) {
14243 if (typeof(this.groups[navId]) == 'undefined') {
14244 this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
14246 return this.groups[navId] ;
14261 * @class Roo.bootstrap.TabPanel
14262 * @extends Roo.bootstrap.Component
14263 * Bootstrap TabPanel class
14264 * @cfg {Boolean} active panel active
14265 * @cfg {String} html panel content
14266 * @cfg {String} tabId unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
14267 * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
14271 * Create a new TabPanel
14272 * @param {Object} config The config object
14275 Roo.bootstrap.TabPanel = function(config){
14276 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
14280 * Fires when the active status changes
14281 * @param {Roo.bootstrap.TabPanel} this
14282 * @param {Boolean} state the new state
14287 * @event beforedeactivate
14288 * Fires before a tab is de-activated - can be used to do validation on a form.
14289 * @param {Roo.bootstrap.TabPanel} this
14290 * @return {Boolean} false if there is an error
14293 'beforedeactivate': true
14296 this.tabId = this.tabId || Roo.id();
14300 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
14307 getAutoCreate : function(){
14310 // item is needed for carousel - not sure if it has any effect otherwise
14311 cls: 'tab-pane item',
14312 html: this.html || ''
14316 cfg.cls += ' active';
14320 cfg.tabId = this.tabId;
14327 initEvents: function()
14329 Roo.log('-------- init events on tab panel ---------');
14331 var p = this.parent();
14332 this.navId = this.navId || p.navId;
14334 if (typeof(this.navId) != 'undefined') {
14335 // not really needed.. but just in case.. parent should be a NavGroup.
14336 var tg = Roo.bootstrap.TabGroup.get(this.navId);
14337 Roo.log(['register', tg, this]);
14343 onRender : function(ct, position)
14345 // Roo.log("Call onRender: " + this.xtype);
14347 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
14355 setActive: function(state)
14357 Roo.log("panel - set active " + this.tabId + "=" + state);
14359 this.active = state;
14361 this.el.removeClass('active');
14363 } else if (!this.el.hasClass('active')) {
14364 this.el.addClass('active');
14366 this.fireEvent('changed', this, state);
14383 * @class Roo.bootstrap.DateField
14384 * @extends Roo.bootstrap.Input
14385 * Bootstrap DateField class
14386 * @cfg {Number} weekStart default 0
14387 * @cfg {Number} weekStart default 0
14388 * @cfg {Number} viewMode default empty, (months|years)
14389 * @cfg {Number} minViewMode default empty, (months|years)
14390 * @cfg {Number} startDate default -Infinity
14391 * @cfg {Number} endDate default Infinity
14392 * @cfg {Boolean} todayHighlight default false
14393 * @cfg {Boolean} todayBtn default false
14394 * @cfg {Boolean} calendarWeeks default false
14395 * @cfg {Object} daysOfWeekDisabled default empty
14397 * @cfg {Boolean} keyboardNavigation default true
14398 * @cfg {String} language default en
14401 * Create a new DateField
14402 * @param {Object} config The config object
14405 Roo.bootstrap.DateField = function(config){
14406 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
14410 * Fires when this field show.
14411 * @param {Roo.bootstrap.DateField} this
14412 * @param {Mixed} date The date value
14417 * Fires when this field hide.
14418 * @param {Roo.bootstrap.DateField} this
14419 * @param {Mixed} date The date value
14424 * Fires when select a date.
14425 * @param {Roo.bootstrap.DateField} this
14426 * @param {Mixed} date The date value
14432 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
14435 * @cfg {String} format
14436 * The default date format string which can be overriden for localization support. The format must be
14437 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
14441 * @cfg {String} altFormats
14442 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
14443 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
14445 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
14453 todayHighlight : false,
14459 keyboardNavigation: true,
14461 calendarWeeks: false,
14463 startDate: -Infinity,
14467 daysOfWeekDisabled: [],
14471 UTCDate: function()
14473 return new Date(Date.UTC.apply(Date, arguments));
14476 UTCToday: function()
14478 var today = new Date();
14479 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
14482 getDate: function() {
14483 var d = this.getUTCDate();
14484 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
14487 getUTCDate: function() {
14491 setDate: function(d) {
14492 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
14495 setUTCDate: function(d) {
14497 this.setValue(this.formatDate(this.date));
14500 onRender: function(ct, position)
14503 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
14505 this.language = this.language || 'en';
14506 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
14507 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
14509 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
14510 this.format = this.format || 'm/d/y';
14511 this.isInline = false;
14512 this.isInput = true;
14513 this.component = this.el.select('.add-on', true).first() || false;
14514 this.component = (this.component && this.component.length === 0) ? false : this.component;
14515 this.hasInput = this.component && this.inputEL().length;
14517 if (typeof(this.minViewMode === 'string')) {
14518 switch (this.minViewMode) {
14520 this.minViewMode = 1;
14523 this.minViewMode = 2;
14526 this.minViewMode = 0;
14531 if (typeof(this.viewMode === 'string')) {
14532 switch (this.viewMode) {
14545 this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
14547 // this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
14549 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14551 this.picker().on('mousedown', this.onMousedown, this);
14552 this.picker().on('click', this.onClick, this);
14554 this.picker().addClass('datepicker-dropdown');
14556 this.startViewMode = this.viewMode;
14559 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
14560 if(!this.calendarWeeks){
14565 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
14566 v.attr('colspan', function(i, val){
14567 return parseInt(val) + 1;
14572 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
14574 this.setStartDate(this.startDate);
14575 this.setEndDate(this.endDate);
14577 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
14584 if(this.isInline) {
14589 picker : function()
14591 return this.pickerEl;
14592 // return this.el.select('.datepicker', true).first();
14595 fillDow: function()
14597 var dowCnt = this.weekStart;
14606 if(this.calendarWeeks){
14614 while (dowCnt < this.weekStart + 7) {
14618 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
14622 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
14625 fillMonths: function()
14628 var months = this.picker().select('>.datepicker-months td', true).first();
14630 months.dom.innerHTML = '';
14636 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
14639 months.createChild(month);
14646 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;
14648 if (this.date < this.startDate) {
14649 this.viewDate = new Date(this.startDate);
14650 } else if (this.date > this.endDate) {
14651 this.viewDate = new Date(this.endDate);
14653 this.viewDate = new Date(this.date);
14661 var d = new Date(this.viewDate),
14662 year = d.getUTCFullYear(),
14663 month = d.getUTCMonth(),
14664 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
14665 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
14666 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
14667 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
14668 currentDate = this.date && this.date.valueOf(),
14669 today = this.UTCToday();
14671 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
14673 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
14675 // this.picker.select('>tfoot th.today').
14676 // .text(dates[this.language].today)
14677 // .toggle(this.todayBtn !== false);
14679 this.updateNavArrows();
14682 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
14684 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
14686 prevMonth.setUTCDate(day);
14688 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
14690 var nextMonth = new Date(prevMonth);
14692 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
14694 nextMonth = nextMonth.valueOf();
14696 var fillMonths = false;
14698 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
14700 while(prevMonth.valueOf() < nextMonth) {
14703 if (prevMonth.getUTCDay() === this.weekStart) {
14705 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
14713 if(this.calendarWeeks){
14714 // ISO 8601: First week contains first thursday.
14715 // ISO also states week starts on Monday, but we can be more abstract here.
14717 // Start of current week: based on weekstart/current date
14718 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
14719 // Thursday of this week
14720 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
14721 // First Thursday of year, year from thursday
14722 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
14723 // Calendar week: ms between thursdays, div ms per day, div 7 days
14724 calWeek = (th - yth) / 864e5 / 7 + 1;
14726 fillMonths.cn.push({
14734 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
14736 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
14739 if (this.todayHighlight &&
14740 prevMonth.getUTCFullYear() == today.getFullYear() &&
14741 prevMonth.getUTCMonth() == today.getMonth() &&
14742 prevMonth.getUTCDate() == today.getDate()) {
14743 clsName += ' today';
14746 if (currentDate && prevMonth.valueOf() === currentDate) {
14747 clsName += ' active';
14750 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
14751 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
14752 clsName += ' disabled';
14755 fillMonths.cn.push({
14757 cls: 'day ' + clsName,
14758 html: prevMonth.getDate()
14761 prevMonth.setDate(prevMonth.getDate()+1);
14764 var currentYear = this.date && this.date.getUTCFullYear();
14765 var currentMonth = this.date && this.date.getUTCMonth();
14767 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
14769 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
14770 v.removeClass('active');
14772 if(currentYear === year && k === currentMonth){
14773 v.addClass('active');
14776 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
14777 v.addClass('disabled');
14783 year = parseInt(year/10, 10) * 10;
14785 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
14787 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
14790 for (var i = -1; i < 11; i++) {
14791 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
14793 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
14801 showMode: function(dir)
14804 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
14806 Roo.each(this.picker().select('>div',true).elements, function(v){
14807 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14810 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
14815 if(this.isInline) return;
14817 this.picker().removeClass(['bottom', 'top']);
14819 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
14821 * place to the top of element!
14825 this.picker().addClass('top');
14826 this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
14831 this.picker().addClass('bottom');
14833 this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
14836 parseDate : function(value)
14838 if(!value || value instanceof Date){
14841 var v = Date.parseDate(value, this.format);
14842 if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
14843 v = Date.parseDate(value, 'Y-m-d');
14845 if(!v && this.altFormats){
14846 if(!this.altFormatsArray){
14847 this.altFormatsArray = this.altFormats.split("|");
14849 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
14850 v = Date.parseDate(value, this.altFormatsArray[i]);
14856 formatDate : function(date, fmt)
14858 return (!date || !(date instanceof Date)) ?
14859 date : date.dateFormat(fmt || this.format);
14862 onFocus : function()
14864 Roo.bootstrap.DateField.superclass.onFocus.call(this);
14868 onBlur : function()
14870 Roo.bootstrap.DateField.superclass.onBlur.call(this);
14872 var d = this.inputEl().getValue();
14881 this.picker().show();
14885 this.fireEvent('show', this, this.date);
14890 if(this.isInline) return;
14891 this.picker().hide();
14892 this.viewMode = this.startViewMode;
14895 this.fireEvent('hide', this, this.date);
14899 onMousedown: function(e)
14901 e.stopPropagation();
14902 e.preventDefault();
14907 Roo.bootstrap.DateField.superclass.keyup.call(this);
14911 setValue: function(v)
14914 // v can be a string or a date..
14917 var d = new Date(this.parseDate(v) ).clearTime();
14921 if(isNaN(d.getTime())){
14922 this.date = this.viewDate = '';
14923 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
14927 v = this.formatDate(d);
14929 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
14931 this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
14935 this.fireEvent('select', this, this.date);
14939 getValue: function()
14941 return this.formatDate(this.date);
14944 fireKey: function(e)
14946 if (!this.picker().isVisible()){
14947 if (e.keyCode == 27) // allow escape to hide and re-show picker
14952 var dateChanged = false,
14954 newDate, newViewDate;
14959 e.preventDefault();
14963 if (!this.keyboardNavigation) break;
14964 dir = e.keyCode == 37 ? -1 : 1;
14967 newDate = this.moveYear(this.date, dir);
14968 newViewDate = this.moveYear(this.viewDate, dir);
14969 } else if (e.shiftKey){
14970 newDate = this.moveMonth(this.date, dir);
14971 newViewDate = this.moveMonth(this.viewDate, dir);
14973 newDate = new Date(this.date);
14974 newDate.setUTCDate(this.date.getUTCDate() + dir);
14975 newViewDate = new Date(this.viewDate);
14976 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
14978 if (this.dateWithinRange(newDate)){
14979 this.date = newDate;
14980 this.viewDate = newViewDate;
14981 this.setValue(this.formatDate(this.date));
14983 e.preventDefault();
14984 dateChanged = true;
14989 if (!this.keyboardNavigation) break;
14990 dir = e.keyCode == 38 ? -1 : 1;
14992 newDate = this.moveYear(this.date, dir);
14993 newViewDate = this.moveYear(this.viewDate, dir);
14994 } else if (e.shiftKey){
14995 newDate = this.moveMonth(this.date, dir);
14996 newViewDate = this.moveMonth(this.viewDate, dir);
14998 newDate = new Date(this.date);
14999 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
15000 newViewDate = new Date(this.viewDate);
15001 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
15003 if (this.dateWithinRange(newDate)){
15004 this.date = newDate;
15005 this.viewDate = newViewDate;
15006 this.setValue(this.formatDate(this.date));
15008 e.preventDefault();
15009 dateChanged = true;
15013 this.setValue(this.formatDate(this.date));
15015 e.preventDefault();
15018 this.setValue(this.formatDate(this.date));
15032 onClick: function(e)
15034 e.stopPropagation();
15035 e.preventDefault();
15037 var target = e.getTarget();
15039 if(target.nodeName.toLowerCase() === 'i'){
15040 target = Roo.get(target).dom.parentNode;
15043 var nodeName = target.nodeName;
15044 var className = target.className;
15045 var html = target.innerHTML;
15046 //Roo.log(nodeName);
15048 switch(nodeName.toLowerCase()) {
15050 switch(className) {
15056 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
15057 switch(this.viewMode){
15059 this.viewDate = this.moveMonth(this.viewDate, dir);
15063 this.viewDate = this.moveYear(this.viewDate, dir);
15069 var date = new Date();
15070 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
15072 this.setValue(this.formatDate(this.date));
15079 if (className.indexOf('disabled') < 0) {
15080 this.viewDate.setUTCDate(1);
15081 if (className.indexOf('month') > -1) {
15082 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
15084 var year = parseInt(html, 10) || 0;
15085 this.viewDate.setUTCFullYear(year);
15094 //Roo.log(className);
15095 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
15096 var day = parseInt(html, 10) || 1;
15097 var year = this.viewDate.getUTCFullYear(),
15098 month = this.viewDate.getUTCMonth();
15100 if (className.indexOf('old') > -1) {
15107 } else if (className.indexOf('new') > -1) {
15115 //Roo.log([year,month,day]);
15116 this.date = this.UTCDate(year, month, day,0,0,0,0);
15117 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
15119 //Roo.log(this.formatDate(this.date));
15120 this.setValue(this.formatDate(this.date));
15127 setStartDate: function(startDate)
15129 this.startDate = startDate || -Infinity;
15130 if (this.startDate !== -Infinity) {
15131 this.startDate = this.parseDate(this.startDate);
15134 this.updateNavArrows();
15137 setEndDate: function(endDate)
15139 this.endDate = endDate || Infinity;
15140 if (this.endDate !== Infinity) {
15141 this.endDate = this.parseDate(this.endDate);
15144 this.updateNavArrows();
15147 setDaysOfWeekDisabled: function(daysOfWeekDisabled)
15149 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
15150 if (typeof(this.daysOfWeekDisabled) !== 'object') {
15151 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
15153 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
15154 return parseInt(d, 10);
15157 this.updateNavArrows();
15160 updateNavArrows: function()
15162 var d = new Date(this.viewDate),
15163 year = d.getUTCFullYear(),
15164 month = d.getUTCMonth();
15166 Roo.each(this.picker().select('.prev', true).elements, function(v){
15168 switch (this.viewMode) {
15171 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
15177 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
15184 Roo.each(this.picker().select('.next', true).elements, function(v){
15186 switch (this.viewMode) {
15189 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
15195 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
15203 moveMonth: function(date, dir)
15205 if (!dir) return date;
15206 var new_date = new Date(date.valueOf()),
15207 day = new_date.getUTCDate(),
15208 month = new_date.getUTCMonth(),
15209 mag = Math.abs(dir),
15211 dir = dir > 0 ? 1 : -1;
15214 // If going back one month, make sure month is not current month
15215 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
15217 return new_date.getUTCMonth() == month;
15219 // If going forward one month, make sure month is as expected
15220 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
15222 return new_date.getUTCMonth() != new_month;
15224 new_month = month + dir;
15225 new_date.setUTCMonth(new_month);
15226 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
15227 if (new_month < 0 || new_month > 11)
15228 new_month = (new_month + 12) % 12;
15230 // For magnitudes >1, move one month at a time...
15231 for (var i=0; i<mag; i++)
15232 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
15233 new_date = this.moveMonth(new_date, dir);
15234 // ...then reset the day, keeping it in the new month
15235 new_month = new_date.getUTCMonth();
15236 new_date.setUTCDate(day);
15238 return new_month != new_date.getUTCMonth();
15241 // Common date-resetting loop -- if date is beyond end of month, make it
15244 new_date.setUTCDate(--day);
15245 new_date.setUTCMonth(new_month);
15250 moveYear: function(date, dir)
15252 return this.moveMonth(date, dir*12);
15255 dateWithinRange: function(date)
15257 return date >= this.startDate && date <= this.endDate;
15263 this.picker().remove();
15268 Roo.apply(Roo.bootstrap.DateField, {
15279 html: '<i class="fa fa-arrow-left"/>'
15289 html: '<i class="fa fa-arrow-right"/>'
15331 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
15332 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
15333 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
15334 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
15335 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
15348 navFnc: 'FullYear',
15353 navFnc: 'FullYear',
15358 Roo.apply(Roo.bootstrap.DateField, {
15362 cls: 'datepicker dropdown-menu',
15366 cls: 'datepicker-days',
15370 cls: 'table-condensed',
15372 Roo.bootstrap.DateField.head,
15376 Roo.bootstrap.DateField.footer
15383 cls: 'datepicker-months',
15387 cls: 'table-condensed',
15389 Roo.bootstrap.DateField.head,
15390 Roo.bootstrap.DateField.content,
15391 Roo.bootstrap.DateField.footer
15398 cls: 'datepicker-years',
15402 cls: 'table-condensed',
15404 Roo.bootstrap.DateField.head,
15405 Roo.bootstrap.DateField.content,
15406 Roo.bootstrap.DateField.footer
15425 * @class Roo.bootstrap.TimeField
15426 * @extends Roo.bootstrap.Input
15427 * Bootstrap DateField class
15431 * Create a new TimeField
15432 * @param {Object} config The config object
15435 Roo.bootstrap.TimeField = function(config){
15436 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
15440 * Fires when this field show.
15441 * @param {Roo.bootstrap.DateField} this
15442 * @param {Mixed} date The date value
15447 * Fires when this field hide.
15448 * @param {Roo.bootstrap.DateField} this
15449 * @param {Mixed} date The date value
15454 * Fires when select a date.
15455 * @param {Roo.bootstrap.DateField} this
15456 * @param {Mixed} date The date value
15462 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
15465 * @cfg {String} format
15466 * The default time format string which can be overriden for localization support. The format must be
15467 * valid according to {@link Date#parseDate} (defaults to 'H:i').
15471 onRender: function(ct, position)
15474 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
15476 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
15478 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15480 this.pop = this.picker().select('>.datepicker-time',true).first();
15481 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
15483 this.picker().on('mousedown', this.onMousedown, this);
15484 this.picker().on('click', this.onClick, this);
15486 this.picker().addClass('datepicker-dropdown');
15491 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
15492 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
15493 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
15494 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
15495 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
15496 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
15500 fireKey: function(e){
15501 if (!this.picker().isVisible()){
15502 if (e.keyCode == 27) // allow escape to hide and re-show picker
15507 e.preventDefault();
15515 this.onTogglePeriod();
15518 this.onIncrementMinutes();
15521 this.onDecrementMinutes();
15530 onClick: function(e) {
15531 e.stopPropagation();
15532 e.preventDefault();
15535 picker : function()
15537 return this.el.select('.datepicker', true).first();
15540 fillTime: function()
15542 var time = this.pop.select('tbody', true).first();
15544 time.dom.innerHTML = '';
15559 cls: 'hours-up glyphicon glyphicon-chevron-up'
15579 cls: 'minutes-up glyphicon glyphicon-chevron-up'
15600 cls: 'timepicker-hour',
15615 cls: 'timepicker-minute',
15630 cls: 'btn btn-primary period',
15652 cls: 'hours-down glyphicon glyphicon-chevron-down'
15672 cls: 'minutes-down glyphicon glyphicon-chevron-down'
15690 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
15697 var hours = this.time.getHours();
15698 var minutes = this.time.getMinutes();
15711 hours = hours - 12;
15715 hours = '0' + hours;
15719 minutes = '0' + minutes;
15722 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
15723 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
15724 this.pop.select('button', true).first().dom.innerHTML = period;
15730 this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
15732 var cls = ['bottom'];
15734 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
15741 if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
15746 this.picker().addClass(cls.join('-'));
15750 Roo.each(cls, function(c){
15752 _this.picker().setTop(_this.inputEl().getHeight());
15756 _this.picker().setTop(0 - _this.picker().getHeight());
15761 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
15765 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
15772 onFocus : function()
15774 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
15778 onBlur : function()
15780 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
15786 this.picker().show();
15791 this.fireEvent('show', this, this.date);
15796 this.picker().hide();
15799 this.fireEvent('hide', this, this.date);
15802 setTime : function()
15805 this.setValue(this.time.format(this.format));
15807 this.fireEvent('select', this, this.date);
15812 onMousedown: function(e){
15813 e.stopPropagation();
15814 e.preventDefault();
15817 onIncrementHours: function()
15819 Roo.log('onIncrementHours');
15820 this.time = this.time.add(Date.HOUR, 1);
15825 onDecrementHours: function()
15827 Roo.log('onDecrementHours');
15828 this.time = this.time.add(Date.HOUR, -1);
15832 onIncrementMinutes: function()
15834 Roo.log('onIncrementMinutes');
15835 this.time = this.time.add(Date.MINUTE, 1);
15839 onDecrementMinutes: function()
15841 Roo.log('onDecrementMinutes');
15842 this.time = this.time.add(Date.MINUTE, -1);
15846 onTogglePeriod: function()
15848 Roo.log('onTogglePeriod');
15849 this.time = this.time.add(Date.HOUR, 12);
15856 Roo.apply(Roo.bootstrap.TimeField, {
15886 cls: 'btn btn-info ok',
15898 Roo.apply(Roo.bootstrap.TimeField, {
15902 cls: 'datepicker dropdown-menu',
15906 cls: 'datepicker-time',
15910 cls: 'table-condensed',
15912 Roo.bootstrap.TimeField.content,
15913 Roo.bootstrap.TimeField.footer
15932 * @class Roo.bootstrap.CheckBox
15933 * @extends Roo.bootstrap.Input
15934 * Bootstrap CheckBox class
15936 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
15937 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
15938 * @cfg {String} boxLabel The text that appears beside the checkbox
15939 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
15940 * @cfg {Boolean} checked initnal the element
15944 * Create a new CheckBox
15945 * @param {Object} config The config object
15948 Roo.bootstrap.CheckBox = function(config){
15949 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
15954 * Fires when the element is checked or unchecked.
15955 * @param {Roo.bootstrap.CheckBox} this This input
15956 * @param {Boolean} checked The new checked value
15962 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
15964 inputType: 'checkbox',
15971 getAutoCreate : function()
15973 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
15979 cfg.cls = 'form-group checkbox' //input-group
15987 type : this.inputType,
15988 value : (!this.checked) ? this.valueOff : this.inputValue,
15989 cls : 'roo-checkbox', //'form-box',
15990 placeholder : this.placeholder || ''
15994 if (this.weight) { // Validity check?
15995 cfg.cls += " checkbox-" + this.weight;
15998 if (this.disabled) {
15999 input.disabled=true;
16003 input.checked = this.checked;
16007 input.name = this.name;
16011 input.cls += ' input-' + this.size;
16015 ['xs','sm','md','lg'].map(function(size){
16016 if (settings[size]) {
16017 cfg.cls += ' col-' + size + '-' + settings[size];
16023 var inputblock = input;
16028 if (this.before || this.after) {
16031 cls : 'input-group',
16035 inputblock.cn.push({
16037 cls : 'input-group-addon',
16041 inputblock.cn.push(input);
16043 inputblock.cn.push({
16045 cls : 'input-group-addon',
16052 if (align ==='left' && this.fieldLabel.length) {
16053 Roo.log("left and has label");
16059 cls : 'control-label col-md-' + this.labelWidth,
16060 html : this.fieldLabel
16064 cls : "col-md-" + (12 - this.labelWidth),
16071 } else if ( this.fieldLabel.length) {
16076 tag: this.boxLabel ? 'span' : 'label',
16078 cls: 'control-label box-input-label',
16079 //cls : 'input-group-addon',
16080 html : this.fieldLabel
16090 Roo.log(" no label && no align");
16091 cfg.cn = [ inputblock ] ;
16100 html: this.boxLabel
16112 * return the real input element.
16114 inputEl: function ()
16116 return this.el.select('input.roo-checkbox',true).first();
16121 return this.el.select('label.control-label',true).first();
16124 initEvents : function()
16126 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
16128 this.inputEl().on('click', this.onClick, this);
16132 onClick : function()
16134 this.setChecked(!this.checked);
16137 setChecked : function(state,suppressEvent)
16139 this.checked = state;
16141 this.inputEl().dom.checked = state;
16143 if(suppressEvent !== true){
16144 this.fireEvent('check', this, state);
16147 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16151 setValue : function(v,suppressEvent)
16153 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
16167 * @class Roo.bootstrap.Radio
16168 * @extends Roo.bootstrap.CheckBox
16169 * Bootstrap Radio class
16172 * Create a new Radio
16173 * @param {Object} config The config object
16176 Roo.bootstrap.Radio = function(config){
16177 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
16181 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
16183 inputType: 'radio',
16187 getAutoCreate : function()
16189 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
16195 cfg.cls = 'form-group radio' //input-group
16200 type : this.inputType,
16201 value : (!this.checked) ? this.valueOff : this.inputValue,
16203 placeholder : this.placeholder || ''
16206 if (this.weight) { // Validity check?
16207 cfg.cls += " radio-" + this.weight;
16209 if (this.disabled) {
16210 input.disabled=true;
16214 input.checked = this.checked;
16218 input.name = this.name;
16222 input.cls += ' input-' + this.size;
16226 ['xs','sm','md','lg'].map(function(size){
16227 if (settings[size]) {
16228 cfg.cls += ' col-' + size + '-' + settings[size];
16232 var inputblock = input;
16234 if (this.before || this.after) {
16237 cls : 'input-group',
16241 inputblock.cn.push({
16243 cls : 'input-group-addon',
16247 inputblock.cn.push(input);
16249 inputblock.cn.push({
16251 cls : 'input-group-addon',
16258 if (align ==='left' && this.fieldLabel.length) {
16259 Roo.log("left and has label");
16265 cls : 'control-label col-md-' + this.labelWidth,
16266 html : this.fieldLabel
16270 cls : "col-md-" + (12 - this.labelWidth),
16277 } else if ( this.fieldLabel.length) {
16284 cls: 'control-label box-input-label',
16285 //cls : 'input-group-addon',
16286 html : this.fieldLabel
16296 Roo.log(" no label && no align");
16311 html: this.boxLabel
16318 inputEl: function ()
16320 return this.el.select('input.roo-radio',true).first();
16322 onClick : function()
16324 this.setChecked(true);
16327 setChecked : function(state,suppressEvent)
16330 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16331 v.dom.checked = false;
16335 this.checked = state;
16336 this.inputEl().dom.checked = state;
16338 if(suppressEvent !== true){
16339 this.fireEvent('check', this, state);
16342 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
16346 getGroupValue : function()
16349 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
16350 if(v.dom.checked == true){
16351 value = v.dom.value;
16359 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
16360 * @return {Mixed} value The field value
16362 getValue : function(){
16363 return this.getGroupValue();
16369 //<script type="text/javascript">
16372 * Based Ext JS Library 1.1.1
16373 * Copyright(c) 2006-2007, Ext JS, LLC.
16379 * @class Roo.HtmlEditorCore
16380 * @extends Roo.Component
16381 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
16383 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
16386 Roo.HtmlEditorCore = function(config){
16389 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
16394 * @event initialize
16395 * Fires when the editor is fully initialized (including the iframe)
16396 * @param {Roo.HtmlEditorCore} this
16401 * Fires when the editor is first receives the focus. Any insertion must wait
16402 * until after this event.
16403 * @param {Roo.HtmlEditorCore} this
16407 * @event beforesync
16408 * Fires before the textarea is updated with content from the editor iframe. Return false
16409 * to cancel the sync.
16410 * @param {Roo.HtmlEditorCore} this
16411 * @param {String} html
16415 * @event beforepush
16416 * Fires before the iframe editor is updated with content from the textarea. Return false
16417 * to cancel the push.
16418 * @param {Roo.HtmlEditorCore} this
16419 * @param {String} html
16424 * Fires when the textarea is updated with content from the editor iframe.
16425 * @param {Roo.HtmlEditorCore} this
16426 * @param {String} html
16431 * Fires when the iframe editor is updated with content from the textarea.
16432 * @param {Roo.HtmlEditorCore} this
16433 * @param {String} html
16438 * @event editorevent
16439 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
16440 * @param {Roo.HtmlEditorCore} this
16445 // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
16447 // defaults : white / black...
16448 this.applyBlacklists();
16455 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
16459 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
16465 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
16470 * @cfg {Number} height (in pixels)
16474 * @cfg {Number} width (in pixels)
16479 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
16482 stylesheets: false,
16487 // private properties
16488 validationEvent : false,
16490 initialized : false,
16492 sourceEditMode : false,
16493 onFocus : Roo.emptyFn,
16495 hideMode:'offsets',
16499 // blacklist + whitelisted elements..
16506 * Protected method that will not generally be called directly. It
16507 * is called when the editor initializes the iframe with HTML contents. Override this method if you
16508 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
16510 getDocMarkup : function(){
16513 Roo.log(this.stylesheets);
16515 // inherit styels from page...??
16516 if (this.stylesheets === false) {
16518 Roo.get(document.head).select('style').each(function(node) {
16519 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16522 Roo.get(document.head).select('link').each(function(node) {
16523 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
16526 } else if (!this.stylesheets.length) {
16528 st = '<style type="text/css">' +
16529 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16532 Roo.each(this.stylesheets, function(s) {
16533 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
16538 st += '<style type="text/css">' +
16539 'IMG { cursor: pointer } ' +
16543 return '<html><head>' + st +
16544 //<style type="text/css">' +
16545 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
16547 ' </head><body class="roo-htmleditor-body"></body></html>';
16551 onRender : function(ct, position)
16554 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
16555 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
16558 this.el.dom.style.border = '0 none';
16559 this.el.dom.setAttribute('tabIndex', -1);
16560 this.el.addClass('x-hidden hide');
16564 if(Roo.isIE){ // fix IE 1px bogus margin
16565 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
16569 this.frameId = Roo.id();
16573 var iframe = this.owner.wrap.createChild({
16575 cls: 'form-control', // bootstrap..
16577 name: this.frameId,
16578 frameBorder : 'no',
16579 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
16584 this.iframe = iframe.dom;
16586 this.assignDocWin();
16588 this.doc.designMode = 'on';
16591 this.doc.write(this.getDocMarkup());
16595 var task = { // must defer to wait for browser to be ready
16597 //console.log("run task?" + this.doc.readyState);
16598 this.assignDocWin();
16599 if(this.doc.body || this.doc.readyState == 'complete'){
16601 this.doc.designMode="on";
16605 Roo.TaskMgr.stop(task);
16606 this.initEditor.defer(10, this);
16613 Roo.TaskMgr.start(task);
16620 onResize : function(w, h)
16622 Roo.log('resize: ' +w + ',' + h );
16623 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
16627 if(typeof w == 'number'){
16629 this.iframe.style.width = w + 'px';
16631 if(typeof h == 'number'){
16633 this.iframe.style.height = h + 'px';
16635 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
16642 * Toggles the editor between standard and source edit mode.
16643 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16645 toggleSourceEdit : function(sourceEditMode){
16647 this.sourceEditMode = sourceEditMode === true;
16649 if(this.sourceEditMode){
16651 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
16654 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
16655 //this.iframe.className = '';
16658 //this.setSize(this.owner.wrap.getSize());
16659 //this.fireEvent('editmodechange', this, this.sourceEditMode);
16666 * Protected method that will not generally be called directly. If you need/want
16667 * custom HTML cleanup, this is the method you should override.
16668 * @param {String} html The HTML to be cleaned
16669 * return {String} The cleaned HTML
16671 cleanHtml : function(html){
16672 html = String(html);
16673 if(html.length > 5){
16674 if(Roo.isSafari){ // strip safari nonsense
16675 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
16678 if(html == ' '){
16685 * HTML Editor -> Textarea
16686 * Protected method that will not generally be called directly. Syncs the contents
16687 * of the editor iframe with the textarea.
16689 syncValue : function(){
16690 if(this.initialized){
16691 var bd = (this.doc.body || this.doc.documentElement);
16692 //this.cleanUpPaste(); -- this is done else where and causes havoc..
16693 var html = bd.innerHTML;
16695 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
16696 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
16698 html = '<div style="'+m[0]+'">' + html + '</div>';
16701 html = this.cleanHtml(html);
16702 // fix up the special chars.. normaly like back quotes in word...
16703 // however we do not want to do this with chinese..
16704 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
16705 var cc = b.charCodeAt();
16707 (cc >= 0x4E00 && cc < 0xA000 ) ||
16708 (cc >= 0x3400 && cc < 0x4E00 ) ||
16709 (cc >= 0xf900 && cc < 0xfb00 )
16715 if(this.owner.fireEvent('beforesync', this, html) !== false){
16716 this.el.dom.value = html;
16717 this.owner.fireEvent('sync', this, html);
16723 * Protected method that will not generally be called directly. Pushes the value of the textarea
16724 * into the iframe editor.
16726 pushValue : function(){
16727 if(this.initialized){
16728 var v = this.el.dom.value.trim();
16730 // if(v.length < 1){
16734 if(this.owner.fireEvent('beforepush', this, v) !== false){
16735 var d = (this.doc.body || this.doc.documentElement);
16737 this.cleanUpPaste();
16738 this.el.dom.value = d.innerHTML;
16739 this.owner.fireEvent('push', this, v);
16745 deferFocus : function(){
16746 this.focus.defer(10, this);
16750 focus : function(){
16751 if(this.win && !this.sourceEditMode){
16758 assignDocWin: function()
16760 var iframe = this.iframe;
16763 this.doc = iframe.contentWindow.document;
16764 this.win = iframe.contentWindow;
16766 // if (!Roo.get(this.frameId)) {
16769 // this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16770 // this.win = Roo.get(this.frameId).dom.contentWindow;
16772 if (!Roo.get(this.frameId) && !iframe.contentDocument) {
16776 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
16777 this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
16782 initEditor : function(){
16783 //console.log("INIT EDITOR");
16784 this.assignDocWin();
16788 this.doc.designMode="on";
16790 this.doc.write(this.getDocMarkup());
16793 var dbody = (this.doc.body || this.doc.documentElement);
16794 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
16795 // this copies styles from the containing element into thsi one..
16796 // not sure why we need all of this..
16797 //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
16799 //var ss = this.el.getStyles( 'background-image', 'background-repeat');
16800 //ss['background-attachment'] = 'fixed'; // w3c
16801 dbody.bgProperties = 'fixed'; // ie
16802 //Roo.DomHelper.applyStyles(dbody, ss);
16803 Roo.EventManager.on(this.doc, {
16804 //'mousedown': this.onEditorEvent,
16805 'mouseup': this.onEditorEvent,
16806 'dblclick': this.onEditorEvent,
16807 'click': this.onEditorEvent,
16808 'keyup': this.onEditorEvent,
16813 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
16815 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
16816 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
16818 this.initialized = true;
16820 this.owner.fireEvent('initialize', this);
16825 onDestroy : function(){
16831 //for (var i =0; i < this.toolbars.length;i++) {
16832 // // fixme - ask toolbars for heights?
16833 // this.toolbars[i].onDestroy();
16836 //this.wrap.dom.innerHTML = '';
16837 //this.wrap.remove();
16842 onFirstFocus : function(){
16844 this.assignDocWin();
16847 this.activated = true;
16850 if(Roo.isGecko){ // prevent silly gecko errors
16852 var s = this.win.getSelection();
16853 if(!s.focusNode || s.focusNode.nodeType != 3){
16854 var r = s.getRangeAt(0);
16855 r.selectNodeContents((this.doc.body || this.doc.documentElement));
16860 this.execCmd('useCSS', true);
16861 this.execCmd('styleWithCSS', false);
16864 this.owner.fireEvent('activate', this);
16868 adjustFont: function(btn){
16869 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
16870 //if(Roo.isSafari){ // safari
16873 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
16874 if(Roo.isSafari){ // safari
16875 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
16876 v = (v < 10) ? 10 : v;
16877 v = (v > 48) ? 48 : v;
16878 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
16883 v = Math.max(1, v+adjust);
16885 this.execCmd('FontSize', v );
16888 onEditorEvent : function(e){
16889 this.owner.fireEvent('editorevent', this, e);
16890 // this.updateToolbar();
16891 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
16894 insertTag : function(tg)
16896 // could be a bit smarter... -> wrap the current selected tRoo..
16897 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
16899 range = this.createRange(this.getSelection());
16900 var wrappingNode = this.doc.createElement(tg.toLowerCase());
16901 wrappingNode.appendChild(range.extractContents());
16902 range.insertNode(wrappingNode);
16909 this.execCmd("formatblock", tg);
16913 insertText : function(txt)
16917 var range = this.createRange();
16918 range.deleteContents();
16919 //alert(Sender.getAttribute('label'));
16921 range.insertNode(this.doc.createTextNode(txt));
16927 * Executes a Midas editor command on the editor document and performs necessary focus and
16928 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
16929 * @param {String} cmd The Midas command
16930 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16932 relayCmd : function(cmd, value){
16934 this.execCmd(cmd, value);
16935 this.owner.fireEvent('editorevent', this);
16936 //this.updateToolbar();
16937 this.owner.deferFocus();
16941 * Executes a Midas editor command directly on the editor document.
16942 * For visual commands, you should use {@link #relayCmd} instead.
16943 * <b>This should only be called after the editor is initialized.</b>
16944 * @param {String} cmd The Midas command
16945 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
16947 execCmd : function(cmd, value){
16948 this.doc.execCommand(cmd, false, value === undefined ? null : value);
16955 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
16957 * @param {String} text | dom node..
16959 insertAtCursor : function(text)
16964 if(!this.activated){
16970 var r = this.doc.selection.createRange();
16981 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
16985 // from jquery ui (MIT licenced)
16987 var win = this.win;
16989 if (win.getSelection && win.getSelection().getRangeAt) {
16990 range = win.getSelection().getRangeAt(0);
16991 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
16992 range.insertNode(node);
16993 } else if (win.document.selection && win.document.selection.createRange) {
16994 // no firefox support
16995 var txt = typeof(text) == 'string' ? text : text.outerHTML;
16996 win.document.selection.createRange().pasteHTML(txt);
16998 // no firefox support
16999 var txt = typeof(text) == 'string' ? text : text.outerHTML;
17000 this.execCmd('InsertHTML', txt);
17009 mozKeyPress : function(e){
17011 var c = e.getCharCode(), cmd;
17014 c = String.fromCharCode(c).toLowerCase();
17028 this.cleanUpPaste.defer(100, this);
17036 e.preventDefault();
17044 fixKeys : function(){ // load time branching for fastest keydown performance
17046 return function(e){
17047 var k = e.getKey(), r;
17050 r = this.doc.selection.createRange();
17053 r.pasteHTML('    ');
17060 r = this.doc.selection.createRange();
17062 var target = r.parentElement();
17063 if(!target || target.tagName.toLowerCase() != 'li'){
17065 r.pasteHTML('<br />');
17071 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17072 this.cleanUpPaste.defer(100, this);
17078 }else if(Roo.isOpera){
17079 return function(e){
17080 var k = e.getKey();
17084 this.execCmd('InsertHTML','    ');
17087 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17088 this.cleanUpPaste.defer(100, this);
17093 }else if(Roo.isSafari){
17094 return function(e){
17095 var k = e.getKey();
17099 this.execCmd('InsertText','\t');
17103 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
17104 this.cleanUpPaste.defer(100, this);
17112 getAllAncestors: function()
17114 var p = this.getSelectedNode();
17117 a.push(p); // push blank onto stack..
17118 p = this.getParentElement();
17122 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
17126 a.push(this.doc.body);
17130 lastSelNode : false,
17133 getSelection : function()
17135 this.assignDocWin();
17136 return Roo.isIE ? this.doc.selection : this.win.getSelection();
17139 getSelectedNode: function()
17141 // this may only work on Gecko!!!
17143 // should we cache this!!!!
17148 var range = this.createRange(this.getSelection()).cloneRange();
17151 var parent = range.parentElement();
17153 var testRange = range.duplicate();
17154 testRange.moveToElementText(parent);
17155 if (testRange.inRange(range)) {
17158 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
17161 parent = parent.parentElement;
17166 // is ancestor a text element.
17167 var ac = range.commonAncestorContainer;
17168 if (ac.nodeType == 3) {
17169 ac = ac.parentNode;
17172 var ar = ac.childNodes;
17175 var other_nodes = [];
17176 var has_other_nodes = false;
17177 for (var i=0;i<ar.length;i++) {
17178 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
17181 // fullly contained node.
17183 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
17188 // probably selected..
17189 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
17190 other_nodes.push(ar[i]);
17194 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
17199 has_other_nodes = true;
17201 if (!nodes.length && other_nodes.length) {
17202 nodes= other_nodes;
17204 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
17210 createRange: function(sel)
17212 // this has strange effects when using with
17213 // top toolbar - not sure if it's a great idea.
17214 //this.editor.contentWindow.focus();
17215 if (typeof sel != "undefined") {
17217 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
17219 return this.doc.createRange();
17222 return this.doc.createRange();
17225 getParentElement: function()
17228 this.assignDocWin();
17229 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
17231 var range = this.createRange(sel);
17234 var p = range.commonAncestorContainer;
17235 while (p.nodeType == 3) { // text node
17246 * Range intersection.. the hard stuff...
17250 * [ -- selected range --- ]
17254 * if end is before start or hits it. fail.
17255 * if start is after end or hits it fail.
17257 * if either hits (but other is outside. - then it's not
17263 // @see http://www.thismuchiknow.co.uk/?p=64.
17264 rangeIntersectsNode : function(range, node)
17266 var nodeRange = node.ownerDocument.createRange();
17268 nodeRange.selectNode(node);
17270 nodeRange.selectNodeContents(node);
17273 var rangeStartRange = range.cloneRange();
17274 rangeStartRange.collapse(true);
17276 var rangeEndRange = range.cloneRange();
17277 rangeEndRange.collapse(false);
17279 var nodeStartRange = nodeRange.cloneRange();
17280 nodeStartRange.collapse(true);
17282 var nodeEndRange = nodeRange.cloneRange();
17283 nodeEndRange.collapse(false);
17285 return rangeStartRange.compareBoundaryPoints(
17286 Range.START_TO_START, nodeEndRange) == -1 &&
17287 rangeEndRange.compareBoundaryPoints(
17288 Range.START_TO_START, nodeStartRange) == 1;
17292 rangeCompareNode : function(range, node)
17294 var nodeRange = node.ownerDocument.createRange();
17296 nodeRange.selectNode(node);
17298 nodeRange.selectNodeContents(node);
17302 range.collapse(true);
17304 nodeRange.collapse(true);
17306 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
17307 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
17309 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
17311 var nodeIsBefore = ss == 1;
17312 var nodeIsAfter = ee == -1;
17314 if (nodeIsBefore && nodeIsAfter)
17316 if (!nodeIsBefore && nodeIsAfter)
17317 return 1; //right trailed.
17319 if (nodeIsBefore && !nodeIsAfter)
17320 return 2; // left trailed.
17325 // private? - in a new class?
17326 cleanUpPaste : function()
17328 // cleans up the whole document..
17329 Roo.log('cleanuppaste');
17331 this.cleanUpChildren(this.doc.body);
17332 var clean = this.cleanWordChars(this.doc.body.innerHTML);
17333 if (clean != this.doc.body.innerHTML) {
17334 this.doc.body.innerHTML = clean;
17339 cleanWordChars : function(input) {// change the chars to hex code
17340 var he = Roo.HtmlEditorCore;
17342 var output = input;
17343 Roo.each(he.swapCodes, function(sw) {
17344 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
17346 output = output.replace(swapper, sw[1]);
17353 cleanUpChildren : function (n)
17355 if (!n.childNodes.length) {
17358 for (var i = n.childNodes.length-1; i > -1 ; i--) {
17359 this.cleanUpChild(n.childNodes[i]);
17366 cleanUpChild : function (node)
17369 //console.log(node);
17370 if (node.nodeName == "#text") {
17371 // clean up silly Windows -- stuff?
17374 if (node.nodeName == "#comment") {
17375 node.parentNode.removeChild(node);
17376 // clean up silly Windows -- stuff?
17379 var lcname = node.tagName.toLowerCase();
17380 // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
17381 // whitelist of tags..
17383 if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
17385 node.parentNode.removeChild(node);
17390 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
17392 // remove <a name=....> as rendering on yahoo mailer is borked with this.
17393 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
17395 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
17396 // remove_keep_children = true;
17399 if (remove_keep_children) {
17400 this.cleanUpChildren(node);
17401 // inserts everything just before this node...
17402 while (node.childNodes.length) {
17403 var cn = node.childNodes[0];
17404 node.removeChild(cn);
17405 node.parentNode.insertBefore(cn, node);
17407 node.parentNode.removeChild(node);
17411 if (!node.attributes || !node.attributes.length) {
17412 this.cleanUpChildren(node);
17416 function cleanAttr(n,v)
17419 if (v.match(/^\./) || v.match(/^\//)) {
17422 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
17425 if (v.match(/^#/)) {
17428 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
17429 node.removeAttribute(n);
17433 var cwhite = this.cwhite;
17434 var cblack = this.cblack;
17436 function cleanStyle(n,v)
17438 if (v.match(/expression/)) { //XSS?? should we even bother..
17439 node.removeAttribute(n);
17443 var parts = v.split(/;/);
17446 Roo.each(parts, function(p) {
17447 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
17451 var l = p.split(':').shift().replace(/\s+/g,'');
17452 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
17454 if ( cwhite.length && cblack.indexOf(l) > -1) {
17455 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17456 //node.removeAttribute(n);
17460 // only allow 'c whitelisted system attributes'
17461 if ( cwhite.length && cwhite.indexOf(l) < 0) {
17462 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
17463 //node.removeAttribute(n);
17473 if (clean.length) {
17474 node.setAttribute(n, clean.join(';'));
17476 node.removeAttribute(n);
17482 for (var i = node.attributes.length-1; i > -1 ; i--) {
17483 var a = node.attributes[i];
17486 if (a.name.toLowerCase().substr(0,2)=='on') {
17487 node.removeAttribute(a.name);
17490 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
17491 node.removeAttribute(a.name);
17494 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
17495 cleanAttr(a.name,a.value); // fixme..
17498 if (a.name == 'style') {
17499 cleanStyle(a.name,a.value);
17502 /// clean up MS crap..
17503 // tecnically this should be a list of valid class'es..
17506 if (a.name == 'class') {
17507 if (a.value.match(/^Mso/)) {
17508 node.className = '';
17511 if (a.value.match(/body/)) {
17512 node.className = '';
17523 this.cleanUpChildren(node);
17528 * Clean up MS wordisms...
17530 cleanWord : function(node)
17533 var cleanWordChildren = function()
17535 if (!node.childNodes.length) {
17538 for (var i = node.childNodes.length-1; i > -1 ; i--) {
17539 _t.cleanWord(node.childNodes[i]);
17545 this.cleanWord(this.doc.body);
17548 if (node.nodeName == "#text") {
17549 // clean up silly Windows -- stuff?
17552 if (node.nodeName == "#comment") {
17553 node.parentNode.removeChild(node);
17554 // clean up silly Windows -- stuff?
17558 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
17559 node.parentNode.removeChild(node);
17563 // remove - but keep children..
17564 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
17565 while (node.childNodes.length) {
17566 var cn = node.childNodes[0];
17567 node.removeChild(cn);
17568 node.parentNode.insertBefore(cn, node);
17570 node.parentNode.removeChild(node);
17571 cleanWordChildren();
17575 if (node.className.length) {
17577 var cn = node.className.split(/\W+/);
17579 Roo.each(cn, function(cls) {
17580 if (cls.match(/Mso[a-zA-Z]+/)) {
17585 node.className = cna.length ? cna.join(' ') : '';
17587 node.removeAttribute("class");
17591 if (node.hasAttribute("lang")) {
17592 node.removeAttribute("lang");
17595 if (node.hasAttribute("style")) {
17597 var styles = node.getAttribute("style").split(";");
17599 Roo.each(styles, function(s) {
17600 if (!s.match(/:/)) {
17603 var kv = s.split(":");
17604 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
17607 // what ever is left... we allow.
17610 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
17611 if (!nstyle.length) {
17612 node.removeAttribute('style');
17616 cleanWordChildren();
17620 domToHTML : function(currentElement, depth, nopadtext) {
17622 depth = depth || 0;
17623 nopadtext = nopadtext || false;
17625 if (!currentElement) {
17626 return this.domToHTML(this.doc.body);
17629 //Roo.log(currentElement);
17631 var allText = false;
17632 var nodeName = currentElement.nodeName;
17633 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
17635 if (nodeName == '#text') {
17636 return currentElement.nodeValue;
17641 if (nodeName != 'BODY') {
17644 // Prints the node tagName, such as <A>, <IMG>, etc
17647 for(i = 0; i < currentElement.attributes.length;i++) {
17649 var aname = currentElement.attributes.item(i).name;
17650 if (!currentElement.attributes.item(i).value.length) {
17653 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
17656 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
17665 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
17668 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
17673 // Traverse the tree
17675 var currentElementChild = currentElement.childNodes.item(i);
17676 var allText = true;
17677 var innerHTML = '';
17679 while (currentElementChild) {
17680 // Formatting code (indent the tree so it looks nice on the screen)
17681 var nopad = nopadtext;
17682 if (lastnode == 'SPAN') {
17686 if (currentElementChild.nodeName == '#text') {
17687 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
17688 if (!nopad && toadd.length > 80) {
17689 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
17691 innerHTML += toadd;
17694 currentElementChild = currentElement.childNodes.item(i);
17700 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
17702 // Recursively traverse the tree structure of the child node
17703 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
17704 lastnode = currentElementChild.nodeName;
17706 currentElementChild=currentElement.childNodes.item(i);
17712 // The remaining code is mostly for formatting the tree
17713 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
17718 ret+= "</"+tagName+">";
17724 applyBlacklists : function()
17726 var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white : [];
17727 var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black : [];
17731 Roo.each(Roo.HtmlEditorCore.white, function(tag) {
17732 if (b.indexOf(tag) > -1) {
17735 this.white.push(tag);
17739 Roo.each(w, function(tag) {
17740 if (b.indexOf(tag) > -1) {
17743 if (this.white.indexOf(tag) > -1) {
17746 this.white.push(tag);
17751 Roo.each(Roo.HtmlEditorCore.black, function(tag) {
17752 if (w.indexOf(tag) > -1) {
17755 this.black.push(tag);
17759 Roo.each(b, function(tag) {
17760 if (w.indexOf(tag) > -1) {
17763 if (this.black.indexOf(tag) > -1) {
17766 this.black.push(tag);
17771 w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite : [];
17772 b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack : [];
17776 Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
17777 if (b.indexOf(tag) > -1) {
17780 this.cwhite.push(tag);
17784 Roo.each(w, function(tag) {
17785 if (b.indexOf(tag) > -1) {
17788 if (this.cwhite.indexOf(tag) > -1) {
17791 this.cwhite.push(tag);
17796 Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
17797 if (w.indexOf(tag) > -1) {
17800 this.cblack.push(tag);
17804 Roo.each(b, function(tag) {
17805 if (w.indexOf(tag) > -1) {
17808 if (this.cblack.indexOf(tag) > -1) {
17811 this.cblack.push(tag);
17816 // hide stuff that is not compatible
17830 * @event specialkey
17834 * @cfg {String} fieldClass @hide
17837 * @cfg {String} focusClass @hide
17840 * @cfg {String} autoCreate @hide
17843 * @cfg {String} inputType @hide
17846 * @cfg {String} invalidClass @hide
17849 * @cfg {String} invalidText @hide
17852 * @cfg {String} msgFx @hide
17855 * @cfg {String} validateOnBlur @hide
17859 Roo.HtmlEditorCore.white = [
17860 'area', 'br', 'img', 'input', 'hr', 'wbr',
17862 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
17863 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
17864 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
17865 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
17866 'table', 'ul', 'xmp',
17868 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
17871 'dir', 'menu', 'ol', 'ul', 'dl',
17877 Roo.HtmlEditorCore.black = [
17878 // 'embed', 'object', // enable - backend responsiblity to clean thiese
17880 'base', 'basefont', 'bgsound', 'blink', 'body',
17881 'frame', 'frameset', 'head', 'html', 'ilayer',
17882 'iframe', 'layer', 'link', 'meta', 'object',
17883 'script', 'style' ,'title', 'xml' // clean later..
17885 Roo.HtmlEditorCore.clean = [
17886 'script', 'style', 'title', 'xml'
17888 Roo.HtmlEditorCore.remove = [
17893 Roo.HtmlEditorCore.ablack = [
17897 Roo.HtmlEditorCore.aclean = [
17898 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
17902 Roo.HtmlEditorCore.pwhite= [
17903 'http', 'https', 'mailto'
17906 // white listed style attributes.
17907 Roo.HtmlEditorCore.cwhite= [
17908 // 'text-align', /// default is to allow most things..
17914 // black listed style attributes.
17915 Roo.HtmlEditorCore.cblack= [
17916 // 'font-size' -- this can be set by the project
17920 Roo.HtmlEditorCore.swapCodes =[
17939 * @class Roo.bootstrap.HtmlEditor
17940 * @extends Roo.bootstrap.TextArea
17941 * Bootstrap HtmlEditor class
17944 * Create a new HtmlEditor
17945 * @param {Object} config The config object
17948 Roo.bootstrap.HtmlEditor = function(config){
17949 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
17950 if (!this.toolbars) {
17951 this.toolbars = [];
17953 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
17956 * @event initialize
17957 * Fires when the editor is fully initialized (including the iframe)
17958 * @param {HtmlEditor} this
17963 * Fires when the editor is first receives the focus. Any insertion must wait
17964 * until after this event.
17965 * @param {HtmlEditor} this
17969 * @event beforesync
17970 * Fires before the textarea is updated with content from the editor iframe. Return false
17971 * to cancel the sync.
17972 * @param {HtmlEditor} this
17973 * @param {String} html
17977 * @event beforepush
17978 * Fires before the iframe editor is updated with content from the textarea. Return false
17979 * to cancel the push.
17980 * @param {HtmlEditor} this
17981 * @param {String} html
17986 * Fires when the textarea is updated with content from the editor iframe.
17987 * @param {HtmlEditor} this
17988 * @param {String} html
17993 * Fires when the iframe editor is updated with content from the textarea.
17994 * @param {HtmlEditor} this
17995 * @param {String} html
17999 * @event editmodechange
18000 * Fires when the editor switches edit modes
18001 * @param {HtmlEditor} this
18002 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
18004 editmodechange: true,
18006 * @event editorevent
18007 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
18008 * @param {HtmlEditor} this
18012 * @event firstfocus
18013 * Fires when on first focus - needed by toolbars..
18014 * @param {HtmlEditor} this
18019 * Auto save the htmlEditor value as a file into Events
18020 * @param {HtmlEditor} this
18024 * @event savedpreview
18025 * preview the saved version of htmlEditor
18026 * @param {HtmlEditor} this
18033 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
18037 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
18042 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
18047 * @cfg {Number} height (in pixels)
18051 * @cfg {Number} width (in pixels)
18056 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
18059 stylesheets: false,
18064 // private properties
18065 validationEvent : false,
18067 initialized : false,
18070 onFocus : Roo.emptyFn,
18072 hideMode:'offsets',
18075 tbContainer : false,
18077 toolbarContainer :function() {
18078 return this.wrap.select('.x-html-editor-tb',true).first();
18082 * Protected method that will not generally be called directly. It
18083 * is called when the editor creates its toolbar. Override this method if you need to
18084 * add custom toolbar buttons.
18085 * @param {HtmlEditor} editor
18087 createToolbar : function(){
18089 Roo.log("create toolbars");
18091 this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
18092 this.toolbars[0].render(this.toolbarContainer());
18096 // if (!editor.toolbars || !editor.toolbars.length) {
18097 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
18100 // for (var i =0 ; i < editor.toolbars.length;i++) {
18101 // editor.toolbars[i] = Roo.factory(
18102 // typeof(editor.toolbars[i]) == 'string' ?
18103 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
18104 // Roo.bootstrap.HtmlEditor);
18105 // editor.toolbars[i].init(editor);
18111 onRender : function(ct, position)
18113 // Roo.log("Call onRender: " + this.xtype);
18115 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
18117 this.wrap = this.inputEl().wrap({
18118 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
18121 this.editorcore.onRender(ct, position);
18123 if (this.resizable) {
18124 this.resizeEl = new Roo.Resizable(this.wrap, {
18128 minHeight : this.height,
18129 height: this.height,
18130 handles : this.resizable,
18133 resize : function(r, w, h) {
18134 _t.onResize(w,h); // -something
18140 this.createToolbar(this);
18143 if(!this.width && this.resizable){
18144 this.setSize(this.wrap.getSize());
18146 if (this.resizeEl) {
18147 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
18148 // should trigger onReize..
18154 onResize : function(w, h)
18156 Roo.log('resize: ' +w + ',' + h );
18157 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
18161 if(this.inputEl() ){
18162 if(typeof w == 'number'){
18163 var aw = w - this.wrap.getFrameWidth('lr');
18164 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
18167 if(typeof h == 'number'){
18168 var tbh = -11; // fixme it needs to tool bar size!
18169 for (var i =0; i < this.toolbars.length;i++) {
18170 // fixme - ask toolbars for heights?
18171 tbh += this.toolbars[i].el.getHeight();
18172 //if (this.toolbars[i].footer) {
18173 // tbh += this.toolbars[i].footer.el.getHeight();
18181 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
18182 ah -= 5; // knock a few pixes off for look..
18183 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
18187 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
18188 this.editorcore.onResize(ew,eh);
18193 * Toggles the editor between standard and source edit mode.
18194 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
18196 toggleSourceEdit : function(sourceEditMode)
18198 this.editorcore.toggleSourceEdit(sourceEditMode);
18200 if(this.editorcore.sourceEditMode){
18201 Roo.log('editor - showing textarea');
18204 // Roo.log(this.syncValue());
18206 this.inputEl().removeClass(['hide', 'x-hidden']);
18207 this.inputEl().dom.removeAttribute('tabIndex');
18208 this.inputEl().focus();
18210 Roo.log('editor - hiding textarea');
18212 // Roo.log(this.pushValue());
18215 this.inputEl().addClass(['hide', 'x-hidden']);
18216 this.inputEl().dom.setAttribute('tabIndex', -1);
18217 //this.deferFocus();
18220 if(this.resizable){
18221 this.setSize(this.wrap.getSize());
18224 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
18227 // private (for BoxComponent)
18228 adjustSize : Roo.BoxComponent.prototype.adjustSize,
18230 // private (for BoxComponent)
18231 getResizeEl : function(){
18235 // private (for BoxComponent)
18236 getPositionEl : function(){
18241 initEvents : function(){
18242 this.originalValue = this.getValue();
18246 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18249 // markInvalid : Roo.emptyFn,
18251 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
18254 // clearInvalid : Roo.emptyFn,
18256 setValue : function(v){
18257 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
18258 this.editorcore.pushValue();
18263 deferFocus : function(){
18264 this.focus.defer(10, this);
18268 focus : function(){
18269 this.editorcore.focus();
18275 onDestroy : function(){
18281 for (var i =0; i < this.toolbars.length;i++) {
18282 // fixme - ask toolbars for heights?
18283 this.toolbars[i].onDestroy();
18286 this.wrap.dom.innerHTML = '';
18287 this.wrap.remove();
18292 onFirstFocus : function(){
18293 //Roo.log("onFirstFocus");
18294 this.editorcore.onFirstFocus();
18295 for (var i =0; i < this.toolbars.length;i++) {
18296 this.toolbars[i].onFirstFocus();
18302 syncValue : function()
18304 this.editorcore.syncValue();
18307 pushValue : function()
18309 this.editorcore.pushValue();
18313 // hide stuff that is not compatible
18327 * @event specialkey
18331 * @cfg {String} fieldClass @hide
18334 * @cfg {String} focusClass @hide
18337 * @cfg {String} autoCreate @hide
18340 * @cfg {String} inputType @hide
18343 * @cfg {String} invalidClass @hide
18346 * @cfg {String} invalidText @hide
18349 * @cfg {String} msgFx @hide
18352 * @cfg {String} validateOnBlur @hide
18361 Roo.namespace('Roo.bootstrap.htmleditor');
18363 * @class Roo.bootstrap.HtmlEditorToolbar1
18368 new Roo.bootstrap.HtmlEditor({
18371 new Roo.bootstrap.HtmlEditorToolbar1({
18372 disable : { fonts: 1 , format: 1, ..., ... , ...],
18378 * @cfg {Object} disable List of elements to disable..
18379 * @cfg {Array} btns List of additional buttons.
18383 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
18386 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
18389 Roo.apply(this, config);
18391 // default disabled, based on 'good practice'..
18392 this.disable = this.disable || {};
18393 Roo.applyIf(this.disable, {
18396 specialElements : true
18398 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
18400 this.editor = config.editor;
18401 this.editorcore = config.editor.editorcore;
18403 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
18405 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
18406 // dont call parent... till later.
18408 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
18413 editorcore : false,
18418 "h1","h2","h3","h4","h5","h6",
18420 "abbr", "acronym", "address", "cite", "samp", "var",
18424 onRender : function(ct, position)
18426 // Roo.log("Call onRender: " + this.xtype);
18428 Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
18430 this.el.dom.style.marginBottom = '0';
18432 var editorcore = this.editorcore;
18433 var editor= this.editor;
18436 var btn = function(id,cmd , toggle, handler){
18438 var event = toggle ? 'toggle' : 'click';
18443 xns: Roo.bootstrap,
18446 enableToggle:toggle !== false,
18448 pressed : toggle ? false : null,
18451 a.listeners[toggle ? 'toggle' : 'click'] = function() {
18452 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
18461 xns: Roo.bootstrap,
18462 glyphicon : 'font',
18466 xns: Roo.bootstrap,
18470 Roo.each(this.formats, function(f) {
18471 style.menu.items.push({
18473 xns: Roo.bootstrap,
18474 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
18479 editorcore.insertTag(this.tagname);
18486 children.push(style);
18489 btn('bold',false,true);
18490 btn('italic',false,true);
18491 btn('align-left', 'justifyleft',true);
18492 btn('align-center', 'justifycenter',true);
18493 btn('align-right' , 'justifyright',true);
18494 btn('link', false, false, function(btn) {
18495 //Roo.log("create link?");
18496 var url = prompt(this.createLinkText, this.defaultLinkValue);
18497 if(url && url != 'http:/'+'/'){
18498 this.editorcore.relayCmd('createlink', url);
18501 btn('list','insertunorderedlist',true);
18502 btn('pencil', false,true, function(btn){
18505 this.toggleSourceEdit(btn.pressed);
18511 xns: Roo.bootstrap,
18516 xns: Roo.bootstrap,
18521 cog.menu.items.push({
18523 xns: Roo.bootstrap,
18524 html : Clean styles,
18529 editorcore.insertTag(this.tagname);
18538 this.xtype = 'NavSimplebar';
18540 for(var i=0;i< children.length;i++) {
18542 this.buttons.add(this.addxtypeChild(children[i]));
18546 editor.on('editorevent', this.updateToolbar, this);
18548 onBtnClick : function(id)
18550 this.editorcore.relayCmd(id);
18551 this.editorcore.focus();
18555 * Protected method that will not generally be called directly. It triggers
18556 * a toolbar update by reading the markup state of the current selection in the editor.
18558 updateToolbar: function(){
18560 if(!this.editorcore.activated){
18561 this.editor.onFirstFocus(); // is this neeed?
18565 var btns = this.buttons;
18566 var doc = this.editorcore.doc;
18567 btns.get('bold').setActive(doc.queryCommandState('bold'));
18568 btns.get('italic').setActive(doc.queryCommandState('italic'));
18569 //btns.get('underline').setActive(doc.queryCommandState('underline'));
18571 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
18572 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
18573 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
18575 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
18576 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
18579 var ans = this.editorcore.getAllAncestors();
18580 if (this.formatCombo) {
18583 var store = this.formatCombo.store;
18584 this.formatCombo.setValue("");
18585 for (var i =0; i < ans.length;i++) {
18586 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
18588 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
18596 // hides menus... - so this cant be on a menu...
18597 Roo.bootstrap.MenuMgr.hideAll();
18599 Roo.bootstrap.MenuMgr.hideAll();
18600 //this.editorsyncValue();
18602 onFirstFocus: function() {
18603 this.buttons.each(function(item){
18607 toggleSourceEdit : function(sourceEditMode){
18610 if(sourceEditMode){
18611 Roo.log("disabling buttons");
18612 this.buttons.each( function(item){
18613 if(item.cmd != 'pencil'){
18619 Roo.log("enabling buttons");
18620 if(this.editorcore.initialized){
18621 this.buttons.each( function(item){
18627 Roo.log("calling toggole on editor");
18628 // tell the editor that it's been pressed..
18629 this.editor.toggleSourceEdit(sourceEditMode);
18639 * @class Roo.bootstrap.Table.AbstractSelectionModel
18640 * @extends Roo.util.Observable
18641 * Abstract base class for grid SelectionModels. It provides the interface that should be
18642 * implemented by descendant classes. This class should not be directly instantiated.
18645 Roo.bootstrap.Table.AbstractSelectionModel = function(){
18646 this.locked = false;
18647 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
18651 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
18652 /** @ignore Called by the grid automatically. Do not call directly. */
18653 init : function(grid){
18659 * Locks the selections.
18662 this.locked = true;
18666 * Unlocks the selections.
18668 unlock : function(){
18669 this.locked = false;
18673 * Returns true if the selections are locked.
18674 * @return {Boolean}
18676 isLocked : function(){
18677 return this.locked;
18681 * @extends Roo.bootstrap.Table.AbstractSelectionModel
18682 * @class Roo.bootstrap.Table.RowSelectionModel
18683 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
18684 * It supports multiple selections and keyboard selection/navigation.
18686 * @param {Object} config
18689 Roo.bootstrap.Table.RowSelectionModel = function(config){
18690 Roo.apply(this, config);
18691 this.selections = new Roo.util.MixedCollection(false, function(o){
18696 this.lastActive = false;
18700 * @event selectionchange
18701 * Fires when the selection changes
18702 * @param {SelectionModel} this
18704 "selectionchange" : true,
18706 * @event afterselectionchange
18707 * Fires after the selection changes (eg. by key press or clicking)
18708 * @param {SelectionModel} this
18710 "afterselectionchange" : true,
18712 * @event beforerowselect
18713 * Fires when a row is selected being selected, return false to cancel.
18714 * @param {SelectionModel} this
18715 * @param {Number} rowIndex The selected index
18716 * @param {Boolean} keepExisting False if other selections will be cleared
18718 "beforerowselect" : true,
18721 * Fires when a row is selected.
18722 * @param {SelectionModel} this
18723 * @param {Number} rowIndex The selected index
18724 * @param {Roo.data.Record} r The record
18726 "rowselect" : true,
18728 * @event rowdeselect
18729 * Fires when a row is deselected.
18730 * @param {SelectionModel} this
18731 * @param {Number} rowIndex The selected index
18733 "rowdeselect" : true
18735 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
18736 this.locked = false;
18739 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
18741 * @cfg {Boolean} singleSelect
18742 * True to allow selection of only one row at a time (defaults to false)
18744 singleSelect : false,
18747 initEvents : function(){
18749 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
18750 this.grid.on("mousedown", this.handleMouseDown, this);
18751 }else{ // allow click to work like normal
18752 this.grid.on("rowclick", this.handleDragableRowClick, this);
18755 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
18756 "up" : function(e){
18758 this.selectPrevious(e.shiftKey);
18759 }else if(this.last !== false && this.lastActive !== false){
18760 var last = this.last;
18761 this.selectRange(this.last, this.lastActive-1);
18762 this.grid.getView().focusRow(this.lastActive);
18763 if(last !== false){
18767 this.selectFirstRow();
18769 this.fireEvent("afterselectionchange", this);
18771 "down" : function(e){
18773 this.selectNext(e.shiftKey);
18774 }else if(this.last !== false && this.lastActive !== false){
18775 var last = this.last;
18776 this.selectRange(this.last, this.lastActive+1);
18777 this.grid.getView().focusRow(this.lastActive);
18778 if(last !== false){
18782 this.selectFirstRow();
18784 this.fireEvent("afterselectionchange", this);
18789 var view = this.grid.view;
18790 view.on("refresh", this.onRefresh, this);
18791 view.on("rowupdated", this.onRowUpdated, this);
18792 view.on("rowremoved", this.onRemove, this);
18796 onRefresh : function(){
18797 var ds = this.grid.dataSource, i, v = this.grid.view;
18798 var s = this.selections;
18799 s.each(function(r){
18800 if((i = ds.indexOfId(r.id)) != -1){
18809 onRemove : function(v, index, r){
18810 this.selections.remove(r);
18814 onRowUpdated : function(v, index, r){
18815 if(this.isSelected(r)){
18816 v.onRowSelect(index);
18822 * @param {Array} records The records to select
18823 * @param {Boolean} keepExisting (optional) True to keep existing selections
18825 selectRecords : function(records, keepExisting){
18827 this.clearSelections();
18829 var ds = this.grid.dataSource;
18830 for(var i = 0, len = records.length; i < len; i++){
18831 this.selectRow(ds.indexOf(records[i]), true);
18836 * Gets the number of selected rows.
18839 getCount : function(){
18840 return this.selections.length;
18844 * Selects the first row in the grid.
18846 selectFirstRow : function(){
18851 * Select the last row.
18852 * @param {Boolean} keepExisting (optional) True to keep existing selections
18854 selectLastRow : function(keepExisting){
18855 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
18859 * Selects the row immediately following the last selected row.
18860 * @param {Boolean} keepExisting (optional) True to keep existing selections
18862 selectNext : function(keepExisting){
18863 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
18864 this.selectRow(this.last+1, keepExisting);
18865 this.grid.getView().focusRow(this.last);
18870 * Selects the row that precedes the last selected row.
18871 * @param {Boolean} keepExisting (optional) True to keep existing selections
18873 selectPrevious : function(keepExisting){
18875 this.selectRow(this.last-1, keepExisting);
18876 this.grid.getView().focusRow(this.last);
18881 * Returns the selected records
18882 * @return {Array} Array of selected records
18884 getSelections : function(){
18885 return [].concat(this.selections.items);
18889 * Returns the first selected record.
18892 getSelected : function(){
18893 return this.selections.itemAt(0);
18898 * Clears all selections.
18900 clearSelections : function(fast){
18901 if(this.locked) return;
18903 var ds = this.grid.dataSource;
18904 var s = this.selections;
18905 s.each(function(r){
18906 this.deselectRow(ds.indexOfId(r.id));
18910 this.selections.clear();
18917 * Selects all rows.
18919 selectAll : function(){
18920 if(this.locked) return;
18921 this.selections.clear();
18922 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
18923 this.selectRow(i, true);
18928 * Returns True if there is a selection.
18929 * @return {Boolean}
18931 hasSelection : function(){
18932 return this.selections.length > 0;
18936 * Returns True if the specified row is selected.
18937 * @param {Number/Record} record The record or index of the record to check
18938 * @return {Boolean}
18940 isSelected : function(index){
18941 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
18942 return (r && this.selections.key(r.id) ? true : false);
18946 * Returns True if the specified record id is selected.
18947 * @param {String} id The id of record to check
18948 * @return {Boolean}
18950 isIdSelected : function(id){
18951 return (this.selections.key(id) ? true : false);
18955 handleMouseDown : function(e, t){
18956 var view = this.grid.getView(), rowIndex;
18957 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
18960 if(e.shiftKey && this.last !== false){
18961 var last = this.last;
18962 this.selectRange(last, rowIndex, e.ctrlKey);
18963 this.last = last; // reset the last
18964 view.focusRow(rowIndex);
18966 var isSelected = this.isSelected(rowIndex);
18967 if(e.button !== 0 && isSelected){
18968 view.focusRow(rowIndex);
18969 }else if(e.ctrlKey && isSelected){
18970 this.deselectRow(rowIndex);
18971 }else if(!isSelected){
18972 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
18973 view.focusRow(rowIndex);
18976 this.fireEvent("afterselectionchange", this);
18979 handleDragableRowClick : function(grid, rowIndex, e)
18981 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
18982 this.selectRow(rowIndex, false);
18983 grid.view.focusRow(rowIndex);
18984 this.fireEvent("afterselectionchange", this);
18989 * Selects multiple rows.
18990 * @param {Array} rows Array of the indexes of the row to select
18991 * @param {Boolean} keepExisting (optional) True to keep existing selections
18993 selectRows : function(rows, keepExisting){
18995 this.clearSelections();
18997 for(var i = 0, len = rows.length; i < len; i++){
18998 this.selectRow(rows[i], true);
19003 * Selects a range of rows. All rows in between startRow and endRow are also selected.
19004 * @param {Number} startRow The index of the first row in the range
19005 * @param {Number} endRow The index of the last row in the range
19006 * @param {Boolean} keepExisting (optional) True to retain existing selections
19008 selectRange : function(startRow, endRow, keepExisting){
19009 if(this.locked) return;
19011 this.clearSelections();
19013 if(startRow <= endRow){
19014 for(var i = startRow; i <= endRow; i++){
19015 this.selectRow(i, true);
19018 for(var i = startRow; i >= endRow; i--){
19019 this.selectRow(i, true);
19025 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
19026 * @param {Number} startRow The index of the first row in the range
19027 * @param {Number} endRow The index of the last row in the range
19029 deselectRange : function(startRow, endRow, preventViewNotify){
19030 if(this.locked) return;
19031 for(var i = startRow; i <= endRow; i++){
19032 this.deselectRow(i, preventViewNotify);
19038 * @param {Number} row The index of the row to select
19039 * @param {Boolean} keepExisting (optional) True to keep existing selections
19041 selectRow : function(index, keepExisting, preventViewNotify){
19042 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
19043 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
19044 if(!keepExisting || this.singleSelect){
19045 this.clearSelections();
19047 var r = this.grid.dataSource.getAt(index);
19048 this.selections.add(r);
19049 this.last = this.lastActive = index;
19050 if(!preventViewNotify){
19051 this.grid.getView().onRowSelect(index);
19053 this.fireEvent("rowselect", this, index, r);
19054 this.fireEvent("selectionchange", this);
19060 * @param {Number} row The index of the row to deselect
19062 deselectRow : function(index, preventViewNotify){
19063 if(this.locked) return;
19064 if(this.last == index){
19067 if(this.lastActive == index){
19068 this.lastActive = false;
19070 var r = this.grid.dataSource.getAt(index);
19071 this.selections.remove(r);
19072 if(!preventViewNotify){
19073 this.grid.getView().onRowDeselect(index);
19075 this.fireEvent("rowdeselect", this, index);
19076 this.fireEvent("selectionchange", this);
19080 restoreLast : function(){
19082 this.last = this._last;
19087 acceptsNav : function(row, col, cm){
19088 return !cm.isHidden(col) && cm.isCellEditable(col, row);
19092 onEditorKey : function(field, e){
19093 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
19098 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
19100 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
19102 }else if(k == e.ENTER && !e.ctrlKey){
19106 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
19108 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
19110 }else if(k == e.ESC){
19114 g.startEditing(newCell[0], newCell[1]);
19119 * Ext JS Library 1.1.1
19120 * Copyright(c) 2006-2007, Ext JS, LLC.
19122 * Originally Released Under LGPL - original licence link has changed is not relivant.
19125 * <script type="text/javascript">
19129 * @class Roo.bootstrap.PagingToolbar
19131 * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
19133 * Create a new PagingToolbar
19134 * @param {Object} config The config object
19136 Roo.bootstrap.PagingToolbar = function(config)
19138 // old args format still supported... - xtype is prefered..
19139 // created from xtype...
19140 var ds = config.dataSource;
19141 this.toolbarItems = [];
19142 if (config.items) {
19143 this.toolbarItems = config.items;
19144 // config.items = [];
19147 Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
19154 this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
19158 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
19160 * @cfg {Roo.data.Store} dataSource
19161 * The underlying data store providing the paged data
19164 * @cfg {String/HTMLElement/Element} container
19165 * container The id or element that will contain the toolbar
19168 * @cfg {Boolean} displayInfo
19169 * True to display the displayMsg (defaults to false)
19172 * @cfg {Number} pageSize
19173 * The number of records to display per page (defaults to 20)
19177 * @cfg {String} displayMsg
19178 * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
19180 displayMsg : 'Displaying {0} - {1} of {2}',
19182 * @cfg {String} emptyMsg
19183 * The message to display when no records are found (defaults to "No data to display")
19185 emptyMsg : 'No data to display',
19187 * Customizable piece of the default paging text (defaults to "Page")
19190 beforePageText : "Page",
19192 * Customizable piece of the default paging text (defaults to "of %0")
19195 afterPageText : "of {0}",
19197 * Customizable piece of the default paging text (defaults to "First Page")
19200 firstText : "First Page",
19202 * Customizable piece of the default paging text (defaults to "Previous Page")
19205 prevText : "Previous Page",
19207 * Customizable piece of the default paging text (defaults to "Next Page")
19210 nextText : "Next Page",
19212 * Customizable piece of the default paging text (defaults to "Last Page")
19215 lastText : "Last Page",
19217 * Customizable piece of the default paging text (defaults to "Refresh")
19220 refreshText : "Refresh",
19224 onRender : function(ct, position)
19226 Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
19227 this.navgroup.parentId = this.id;
19228 this.navgroup.onRender(this.el, null);
19229 // add the buttons to the navgroup
19231 if(this.displayInfo){
19232 Roo.log(this.el.select('ul.navbar-nav',true).first());
19233 this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
19234 this.displayEl = this.el.select('.x-paging-info', true).first();
19235 // var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
19236 // this.displayEl = navel.el.select('span',true).first();
19242 Roo.each(_this.buttons, function(e){
19243 Roo.factory(e).onRender(_this.el, null);
19247 Roo.each(_this.toolbarItems, function(e) {
19248 _this.navgroup.addItem(e);
19251 this.first = this.navgroup.addItem({
19252 tooltip: this.firstText,
19254 icon : 'fa fa-backward',
19256 listeners : { click : this.onClick.createDelegate(this, ["first"]) }
19259 this.prev = this.navgroup.addItem({
19260 tooltip: this.prevText,
19262 icon : 'fa fa-step-backward',
19264 listeners : { click : this.onClick.createDelegate(this, ["prev"]) }
19266 //this.addSeparator();
19269 var field = this.navgroup.addItem( {
19271 cls : 'x-paging-position',
19273 html : this.beforePageText +
19274 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
19275 '<span class="x-paging-after">' + String.format(this.afterPageText, 1) + '</span>'
19278 this.field = field.el.select('input', true).first();
19279 this.field.on("keydown", this.onPagingKeydown, this);
19280 this.field.on("focus", function(){this.dom.select();});
19283 this.afterTextEl = field.el.select('.x-paging-after',true).first();
19284 //this.field.setHeight(18);
19285 //this.addSeparator();
19286 this.next = this.navgroup.addItem({
19287 tooltip: this.nextText,
19289 html : ' <i class="fa fa-step-forward">',
19291 listeners : { click : this.onClick.createDelegate(this, ["next"]) }
19293 this.last = this.navgroup.addItem({
19294 tooltip: this.lastText,
19295 icon : 'fa fa-forward',
19298 listeners : { click : this.onClick.createDelegate(this, ["last"]) }
19300 //this.addSeparator();
19301 this.loading = this.navgroup.addItem({
19302 tooltip: this.refreshText,
19303 icon: 'fa fa-refresh',
19305 listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
19311 updateInfo : function(){
19312 if(this.displayEl){
19313 var count = this.ds.getCount();
19314 var msg = count == 0 ?
19318 this.cursor+1, this.cursor+count, this.ds.getTotalCount()
19320 this.displayEl.update(msg);
19325 onLoad : function(ds, r, o){
19326 this.cursor = o.params ? o.params.start : 0;
19327 var d = this.getPageData(),
19331 this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
19332 this.field.dom.value = ap;
19333 this.first.setDisabled(ap == 1);
19334 this.prev.setDisabled(ap == 1);
19335 this.next.setDisabled(ap == ps);
19336 this.last.setDisabled(ap == ps);
19337 this.loading.enable();
19342 getPageData : function(){
19343 var total = this.ds.getTotalCount();
19346 activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
19347 pages : total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
19352 onLoadError : function(){
19353 this.loading.enable();
19357 onPagingKeydown : function(e){
19358 var k = e.getKey();
19359 var d = this.getPageData();
19361 var v = this.field.dom.value, pageNum;
19362 if(!v || isNaN(pageNum = parseInt(v, 10))){
19363 this.field.dom.value = d.activePage;
19366 pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
19367 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19370 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))
19372 var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
19373 this.field.dom.value = pageNum;
19374 this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
19377 else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19379 var v = this.field.dom.value, pageNum;
19380 var increment = (e.shiftKey) ? 10 : 1;
19381 if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
19383 if(!v || isNaN(pageNum = parseInt(v, 10))) {
19384 this.field.dom.value = d.activePage;
19387 else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
19389 this.field.dom.value = parseInt(v, 10) + increment;
19390 pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
19391 this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
19398 beforeLoad : function(){
19400 this.loading.disable();
19405 onClick : function(which){
19412 ds.load({params:{start: 0, limit: this.pageSize}});
19415 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
19418 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
19421 var total = ds.getTotalCount();
19422 var extra = total % this.pageSize;
19423 var lastStart = extra ? (total - extra) : total-this.pageSize;
19424 ds.load({params:{start: lastStart, limit: this.pageSize}});
19427 ds.load({params:{start: this.cursor, limit: this.pageSize}});
19433 * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
19434 * @param {Roo.data.Store} store The data store to unbind
19436 unbind : function(ds){
19437 ds.un("beforeload", this.beforeLoad, this);
19438 ds.un("load", this.onLoad, this);
19439 ds.un("loadexception", this.onLoadError, this);
19440 ds.un("remove", this.updateInfo, this);
19441 ds.un("add", this.updateInfo, this);
19442 this.ds = undefined;
19446 * Binds the paging toolbar to the specified {@link Roo.data.Store}
19447 * @param {Roo.data.Store} store The data store to bind
19449 bind : function(ds){
19450 ds.on("beforeload", this.beforeLoad, this);
19451 ds.on("load", this.onLoad, this);
19452 ds.on("loadexception", this.onLoadError, this);
19453 ds.on("remove", this.updateInfo, this);
19454 ds.on("add", this.updateInfo, this);
19465 * @class Roo.bootstrap.MessageBar
19466 * @extends Roo.bootstrap.Component
19467 * Bootstrap MessageBar class
19468 * @cfg {String} html contents of the MessageBar
19469 * @cfg {String} weight (info | success | warning | danger) default info
19470 * @cfg {String} beforeClass insert the bar before the given class
19471 * @cfg {Boolean} closable (true | false) default false
19472 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
19475 * Create a new Element
19476 * @param {Object} config The config object
19479 Roo.bootstrap.MessageBar = function(config){
19480 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
19483 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
19489 beforeClass: 'bootstrap-sticky-wrap',
19491 getAutoCreate : function(){
19495 cls: 'alert alert-dismissable alert-' + this.weight,
19500 html: this.html || ''
19506 cfg.cls += ' alert-messages-fixed';
19520 onRender : function(ct, position)
19522 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19525 var cfg = Roo.apply({}, this.getAutoCreate());
19529 cfg.cls += ' ' + this.cls;
19532 cfg.style = this.style;
19534 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
19536 this.el.setVisibilityMode(Roo.Element.DISPLAY);
19539 this.el.select('>button.close').on('click', this.hide, this);
19545 if (!this.rendered) {
19551 this.fireEvent('show', this);
19557 if (!this.rendered) {
19563 this.fireEvent('hide', this);
19566 update : function()
19568 // var e = this.el.dom.firstChild;
19570 // if(this.closable){
19571 // e = e.nextSibling;
19574 // e.data = this.html || '';
19576 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
19592 * @class Roo.bootstrap.Graph
19593 * @extends Roo.bootstrap.Component
19594 * Bootstrap Graph class
19598 @cfg {String} graphtype bar | vbar | pie
19599 @cfg {number} g_x coodinator | centre x (pie)
19600 @cfg {number} g_y coodinator | centre y (pie)
19601 @cfg {number} g_r radius (pie)
19602 @cfg {number} g_height height of the chart (respected by all elements in the set)
19603 @cfg {number} g_width width of the chart (respected by all elements in the set)
19604 @cfg {Object} title The title of the chart
19607 -opts (object) options for the chart
19609 o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
19610 o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
19612 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.
19613 o stacked (boolean) whether or not to tread values as in a stacked bar chart
19615 o stretch (boolean)
19617 -opts (object) options for the pie
19620 o startAngle (number)
19621 o endAngle (number)
19625 * Create a new Input
19626 * @param {Object} config The config object
19629 Roo.bootstrap.Graph = function(config){
19630 Roo.bootstrap.Graph.superclass.constructor.call(this, config);
19636 * The img click event for the img.
19637 * @param {Roo.EventObject} e
19643 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component, {
19654 //g_colors: this.colors,
19661 getAutoCreate : function(){
19672 onRender : function(ct,position){
19673 Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
19674 this.raphael = Raphael(this.el.dom);
19676 // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19677 // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19678 // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
19679 // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
19681 r.text(160, 10, "Single Series Chart").attr(txtattr);
19682 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
19683 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
19684 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
19686 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
19687 r.barchart(330, 10, 300, 220, data1);
19688 r.barchart(10, 250, 300, 220, data2, {stacked: true});
19689 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
19692 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19693 // r.barchart(30, 30, 560, 250, xdata, {
19694 // labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
19695 // axis : "0 0 1 1",
19696 // axisxlabels : xdata
19697 // //yvalues : cols,
19700 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
19702 // this.load(null,xdata,{
19703 // axis : "0 0 1 1",
19704 // axisxlabels : xdata
19709 load : function(graphtype,xdata,opts){
19710 this.raphael.clear();
19712 graphtype = this.graphtype;
19717 var r = this.raphael,
19718 fin = function () {
19719 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
19721 fout = function () {
19722 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
19724 pfin = function() {
19725 this.sector.stop();
19726 this.sector.scale(1.1, 1.1, this.cx, this.cy);
19729 this.label[0].stop();
19730 this.label[0].attr({ r: 7.5 });
19731 this.label[1].attr({ "font-weight": 800 });
19734 pfout = function() {
19735 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
19738 this.label[0].animate({ r: 5 }, 500, "bounce");
19739 this.label[1].attr({ "font-weight": 400 });
19745 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19748 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
19751 // opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west",
19752 // href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
19754 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
19761 this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
19766 setTitle: function(o)
19771 initEvents: function() {
19774 this.el.on('click', this.onClick, this);
19778 onClick : function(e)
19780 Roo.log('img onclick');
19781 this.fireEvent('click', this, e);
19793 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19796 * @class Roo.bootstrap.dash.NumberBox
19797 * @extends Roo.bootstrap.Component
19798 * Bootstrap NumberBox class
19799 * @cfg {String} bgcolor Box background color, such as (aqua | green | yellow | red etc..) Default aqua
19800 * @cfg {String} headline Box headline
19801 * @cfg {String} content Box content
19802 * @cfg {String} icon Box icon
19803 * @cfg {String} footer Footer text
19804 * @cfg {String} fhref Footer href
19807 * Create a new NumberBox
19808 * @param {Object} config The config object
19812 Roo.bootstrap.dash.NumberBox = function(config){
19813 Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
19817 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component, {
19827 getAutoCreate : function(){
19831 cls : 'small-box bg-' + this.bgcolor,
19839 cls : 'roo-headline',
19840 html : this.headline
19844 cls : 'roo-content',
19845 html : this.content
19859 cls : 'ion ' + this.icon
19868 cls : 'small-box-footer',
19869 href : this.fhref || '#',
19873 cfg.cn.push(footer);
19880 onRender : function(ct,position){
19881 Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
19888 setHeadline: function (value)
19890 this.el.select('.roo-headline',true).first().dom.innerHTML = value;
19893 setFooter: function (value, href)
19895 this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
19898 this.el.select('a.small-box-footer',true).first().attr('href', href);
19903 setContent: function (value)
19905 this.el.select('.roo-content',true).first().dom.innerHTML = value;
19908 initEvents: function()
19922 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
19925 * @class Roo.bootstrap.dash.TabBox
19926 * @extends Roo.bootstrap.Component
19927 * Bootstrap TabBox class
19928 * @cfg {String} title Title of the TabBox
19929 * @cfg {String} icon Icon of the TabBox
19930 * @cfg {Boolean} showtabs (true|false) show the tabs default true
19931 * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
19934 * Create a new TabBox
19935 * @param {Object} config The config object
19939 Roo.bootstrap.dash.TabBox = function(config){
19940 Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
19945 * When a pane is added
19946 * @param {Roo.bootstrap.dash.TabPane} pane
19950 * @event activatepane
19951 * When a pane is activated
19952 * @param {Roo.bootstrap.dash.TabPane} pane
19954 "activatepane" : true
19962 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component, {
19967 tabScrollable : false,
19969 getChildContainer : function()
19971 return this.el.select('.tab-content', true).first();
19974 getAutoCreate : function(){
19978 cls: 'pull-left header',
19986 cls: 'fa ' + this.icon
19992 cls: 'nav nav-tabs pull-right',
19998 if(this.tabScrollable){
20005 cls: 'nav nav-tabs pull-right',
20016 cls: 'nav-tabs-custom',
20021 cls: 'tab-content no-padding',
20029 initEvents : function()
20031 //Roo.log('add add pane handler');
20032 this.on('addpane', this.onAddPane, this);
20035 * Updates the box title
20036 * @param {String} html to set the title to.
20038 setTitle : function(value)
20040 this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
20042 onAddPane : function(pane)
20044 this.panes.push(pane);
20045 //Roo.log('addpane');
20047 // tabs are rendere left to right..
20048 if(!this.showtabs){
20052 var ctr = this.el.select('.nav-tabs', true).first();
20055 var existing = ctr.select('.nav-tab',true);
20056 var qty = existing.getCount();;
20059 var tab = ctr.createChild({
20061 cls : 'nav-tab' + (qty ? '' : ' active'),
20069 }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
20072 tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
20074 pane.el.addClass('active');
20079 onTabClick : function(ev,un,ob,pane)
20081 //Roo.log('tab - prev default');
20082 ev.preventDefault();
20085 this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
20086 pane.tab.addClass('active');
20087 //Roo.log(pane.title);
20088 this.getChildContainer().select('.tab-pane',true).removeClass('active');
20089 // technically we should have a deactivate event.. but maybe add later.
20090 // and it should not de-activate the selected tab...
20091 this.fireEvent('activatepane', pane);
20092 pane.el.addClass('active');
20093 pane.fireEvent('activate');
20098 getActivePane : function()
20101 Roo.each(this.panes, function(p) {
20102 if(p.el.hasClass('active')){
20123 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
20125 * @class Roo.bootstrap.TabPane
20126 * @extends Roo.bootstrap.Component
20127 * Bootstrap TabPane class
20128 * @cfg {Boolean} active (false | true) Default false
20129 * @cfg {String} title title of panel
20133 * Create a new TabPane
20134 * @param {Object} config The config object
20137 Roo.bootstrap.dash.TabPane = function(config){
20138 Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
20144 * When a pane is activated
20145 * @param {Roo.bootstrap.dash.TabPane} pane
20152 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component, {
20157 // the tabBox that this is attached to.
20160 getAutoCreate : function()
20168 cfg.cls += ' active';
20173 initEvents : function()
20175 //Roo.log('trigger add pane handler');
20176 this.parent().fireEvent('addpane', this)
20180 * Updates the tab title
20181 * @param {String} html to set the title to.
20183 setTitle: function(str)
20189 this.tab.select('a', true).first().dom.innerHTML = str;
20206 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20209 * @class Roo.bootstrap.menu.Menu
20210 * @extends Roo.bootstrap.Component
20211 * Bootstrap Menu class - container for Menu
20212 * @cfg {String} html Text of the menu
20213 * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
20214 * @cfg {String} icon Font awesome icon
20215 * @cfg {String} pos Menu align to (top | bottom) default bottom
20219 * Create a new Menu
20220 * @param {Object} config The config object
20224 Roo.bootstrap.menu.Menu = function(config){
20225 Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
20229 * @event beforeshow
20230 * Fires before this menu is displayed
20231 * @param {Roo.bootstrap.menu.Menu} this
20235 * @event beforehide
20236 * Fires before this menu is hidden
20237 * @param {Roo.bootstrap.menu.Menu} this
20242 * Fires after this menu is displayed
20243 * @param {Roo.bootstrap.menu.Menu} this
20248 * Fires after this menu is hidden
20249 * @param {Roo.bootstrap.menu.Menu} this
20254 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
20255 * @param {Roo.bootstrap.menu.Menu} this
20256 * @param {Roo.EventObject} e
20263 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component, {
20267 weight : 'default',
20272 getChildContainer : function() {
20273 if(this.isSubMenu){
20277 return this.el.select('ul.dropdown-menu', true).first();
20280 getAutoCreate : function()
20285 cls : 'roo-menu-text',
20293 cls : 'fa ' + this.icon
20304 cls : 'dropdown-button btn btn-' + this.weight,
20309 cls : 'dropdown-toggle btn btn-' + this.weight,
20319 cls : 'dropdown-menu'
20325 if(this.pos == 'top'){
20326 cfg.cls += ' dropup';
20329 if(this.isSubMenu){
20332 cls : 'dropdown-menu'
20339 onRender : function(ct, position)
20341 this.isSubMenu = ct.hasClass('dropdown-submenu');
20343 Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
20346 initEvents : function()
20348 if(this.isSubMenu){
20352 this.hidden = true;
20354 this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
20355 this.triggerEl.on('click', this.onTriggerPress, this);
20357 this.buttonEl = this.el.select('button.dropdown-button', true).first();
20358 this.buttonEl.on('click', this.onClick, this);
20364 if(this.isSubMenu){
20368 return this.el.select('ul.dropdown-menu', true).first();
20371 onClick : function(e)
20373 this.fireEvent("click", this, e);
20376 onTriggerPress : function(e)
20378 if (this.isVisible()) {
20385 isVisible : function(){
20386 return !this.hidden;
20391 this.fireEvent("beforeshow", this);
20393 this.hidden = false;
20394 this.el.addClass('open');
20396 Roo.get(document).on("mouseup", this.onMouseUp, this);
20398 this.fireEvent("show", this);
20405 this.fireEvent("beforehide", this);
20407 this.hidden = true;
20408 this.el.removeClass('open');
20410 Roo.get(document).un("mouseup", this.onMouseUp);
20412 this.fireEvent("hide", this);
20415 onMouseUp : function()
20429 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20432 * @class Roo.bootstrap.menu.Item
20433 * @extends Roo.bootstrap.Component
20434 * Bootstrap MenuItem class
20435 * @cfg {Boolean} submenu (true | false) default false
20436 * @cfg {String} html text of the item
20437 * @cfg {String} href the link
20438 * @cfg {Boolean} disable (true | false) default false
20439 * @cfg {Boolean} preventDefault (true | false) default true
20440 * @cfg {String} icon Font awesome icon
20441 * @cfg {String} pos Submenu align to (left | right) default right
20445 * Create a new Item
20446 * @param {Object} config The config object
20450 Roo.bootstrap.menu.Item = function(config){
20451 Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
20455 * Fires when the mouse is hovering over this menu
20456 * @param {Roo.bootstrap.menu.Item} this
20457 * @param {Roo.EventObject} e
20462 * Fires when the mouse exits this menu
20463 * @param {Roo.bootstrap.menu.Item} this
20464 * @param {Roo.EventObject} e
20470 * The raw click event for the entire grid.
20471 * @param {Roo.EventObject} e
20477 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component, {
20482 preventDefault: true,
20487 getAutoCreate : function()
20492 cls : 'roo-menu-item-text',
20500 cls : 'fa ' + this.icon
20509 href : this.href || '#',
20516 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
20520 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
20522 if(this.pos == 'left'){
20523 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
20530 initEvents : function()
20532 this.el.on('mouseover', this.onMouseOver, this);
20533 this.el.on('mouseout', this.onMouseOut, this);
20535 this.el.select('a', true).first().on('click', this.onClick, this);
20539 onClick : function(e)
20541 if(this.preventDefault){
20542 e.preventDefault();
20545 this.fireEvent("click", this, e);
20548 onMouseOver : function(e)
20550 if(this.submenu && this.pos == 'left'){
20551 this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
20554 this.fireEvent("mouseover", this, e);
20557 onMouseOut : function(e)
20559 this.fireEvent("mouseout", this, e);
20571 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
20574 * @class Roo.bootstrap.menu.Separator
20575 * @extends Roo.bootstrap.Component
20576 * Bootstrap Separator class
20579 * Create a new Separator
20580 * @param {Object} config The config object
20584 Roo.bootstrap.menu.Separator = function(config){
20585 Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
20588 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component, {
20590 getAutoCreate : function(){
20611 * @class Roo.bootstrap.Tooltip
20612 * Bootstrap Tooltip class
20613 * This is basic at present - all componets support it by default, however they should add tooltipEl() method
20614 * to determine which dom element triggers the tooltip.
20616 * It needs to add support for additional attributes like tooltip-position
20619 * Create a new Toolti
20620 * @param {Object} config The config object
20623 Roo.bootstrap.Tooltip = function(config){
20624 Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
20627 Roo.apply(Roo.bootstrap.Tooltip, {
20629 * @function init initialize tooltip monitoring.
20633 currentTip : false,
20634 currentRegion : false,
20640 Roo.get(document).on('mouseover', this.enter ,this);
20641 Roo.get(document).on('mouseout', this.leave, this);
20644 this.currentTip = new Roo.bootstrap.Tooltip();
20647 enter : function(ev)
20649 var dom = ev.getTarget();
20650 //Roo.log(['enter',dom]);
20651 var el = Roo.fly(dom);
20652 if (this.currentEl) {
20654 //Roo.log(this.currentEl);
20655 //Roo.log(this.currentEl.contains(dom));
20656 if (this.currentEl == el) {
20659 if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
20667 if (this.currentTip.el) {
20668 this.currentTip.el.hide(); // force hiding...
20671 if (!el.attr('tooltip')) { // parents who have tip?
20674 this.currentEl = el;
20675 this.currentTip.bind(el);
20676 this.currentRegion = Roo.lib.Region.getRegion(dom);
20677 this.currentTip.enter();
20680 leave : function(ev)
20682 var dom = ev.getTarget();
20683 //Roo.log(['leave',dom]);
20684 if (!this.currentEl) {
20689 if (dom != this.currentEl.dom) {
20692 var xy = ev.getXY();
20693 if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0] ))) {
20696 // only activate leave if mouse cursor is outside... bounding box..
20701 if (this.currentTip) {
20702 this.currentTip.leave();
20704 //Roo.log('clear currentEl');
20705 this.currentEl = false;
20710 'left' : ['r-l', [-2,0], 'right'],
20711 'right' : ['l-r', [2,0], 'left'],
20712 'bottom' : ['t-b', [0,2], 'top'],
20713 'top' : [ 'b-t', [0,-2], 'bottom']
20719 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component, {
20724 delay : null, // can be { show : 300 , hide: 500}
20728 hoverState : null, //???
20730 placement : 'bottom',
20732 getAutoCreate : function(){
20739 cls : 'tooltip-arrow'
20742 cls : 'tooltip-inner'
20749 bind : function(el)
20755 enter : function () {
20757 if (this.timeout != null) {
20758 clearTimeout(this.timeout);
20761 this.hoverState = 'in'
20762 //Roo.log("enter - show");
20763 if (!this.delay || !this.delay.show) {
20768 this.timeout = setTimeout(function () {
20769 if (_t.hoverState == 'in') {
20772 }, this.delay.show);
20776 clearTimeout(this.timeout);
20778 this.hoverState = 'out'
20779 if (!this.delay || !this.delay.hide) {
20785 this.timeout = setTimeout(function () {
20786 //Roo.log("leave - timeout");
20788 if (_t.hoverState == 'out') {
20790 Roo.bootstrap.Tooltip.currentEl = false;
20798 this.render(document.body);
20801 //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
20802 this.el.select('.tooltip-inner',true).first().dom.innerHTML = this.bindEl.attr('tooltip');
20804 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
20806 var placement = typeof this.placement == 'function' ?
20807 this.placement.call(this, this.el, on_el) :
20810 var autoToken = /\s?auto?\s?/i;
20811 var autoPlace = autoToken.test(placement);
20813 placement = placement.replace(autoToken, '') || 'top';
20817 //this.el.setXY([0,0]);
20819 //this.el.dom.style.display='block';
20820 this.el.addClass(placement);
20822 //this.el.appendTo(on_el);
20824 var p = this.getPosition();
20825 var box = this.el.getBox();
20830 var align = Roo.bootstrap.Tooltip.alignment[placement]
20831 this.el.alignTo(this.bindEl, align[0],align[1]);
20832 //var arrow = this.el.select('.arrow',true).first();
20833 //arrow.set(align[2],
20835 this.el.addClass('in fade');
20836 this.hoverState = null;
20838 if (this.el.hasClass('fade')) {
20849 //this.el.setXY([0,0]);
20850 this.el.removeClass('in');