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
21 * Do not use directly - it does not do anything..
22 * @param {Object} config The config object
27 Roo.bootstrap.Component = function(config){
28 Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
34 allowDomMove : false, // to stop relocations in parent onRender...
42 initEvents : function() { },
48 can_build_overlaid : true,
55 // returns the parent component..
56 return Roo.ComponentMgr.get(this.parentId)
62 onRender : function(ct, position)
64 // Roo.log("Call onRender: " + this.xtype);
66 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
69 if (this.el.attr('xtype')) {
70 this.el.attr('xtypex', this.el.attr('xtype'));
71 this.el.dom.removeAttribute('xtype');
81 var cfg = Roo.apply({}, this.getAutoCreate());
84 // fill in the extra attributes
85 if (this.xattr && typeof(this.xattr) =='object') {
86 for (var i in this.xattr) {
87 cfg[i] = this.xattr[i];
92 cfg.dataId = this.dataId;
96 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
99 if (this.style) { // fixme needs to support more complex style data.
100 cfg.style = this.style;
104 cfg.name = this.name;
107 this.el = ct.createChild(cfg, position);
109 if(this.tabIndex !== undefined){
110 this.el.dom.setAttribute('tabIndex', this.tabIndex);
117 getChildContainer : function()
123 addxtype : function(tree,cntr)
127 cn = Roo.factory(tree);
129 cn.parentType = this.xtype; //??
130 cn.parentId = this.id;
132 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
134 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
136 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
138 var build_from_html = Roo.XComponent.build_from_html;
140 var is_body = (tree.xtype == 'Body') ;
142 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
144 var self_cntr_el = Roo.get(this[cntr]());
146 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148 return this.addxtypeChild(tree,cntr);
151 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
154 return this.addxtypeChild(Roo.apply({}, tree),cntr);
157 Roo.log('skipping render');
165 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
171 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
175 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
180 addxtypeChild : function (tree, cntr)
182 Roo.log('addxtypeChild:' + cntr);
184 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
187 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188 (typeof(tree['flexy:foreach']) != 'undefined');
193 // render the element if it's not BODY.
194 if (tree.xtype != 'Body') {
196 cn = Roo.factory(tree);
198 cn.parentType = this.xtype; //??
199 cn.parentId = this.id;
201 var build_from_html = Roo.XComponent.build_from_html;
204 // does the container contain child eleemnts with 'xtype' attributes.
205 // that match this xtype..
206 // note - when we render we create these as well..
207 // so we should check to see if body has xtype set.
208 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
210 var self_cntr_el = Roo.get(this[cntr]());
211 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
213 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
220 //echild.dom.removeAttribute('xtype');
222 Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229 // if object has flexy:if - then it may or may not be rendered.
230 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
231 // skip a flexy if element.
232 Roo.log('skipping render');
235 // actually if flexy:foreach is found, we really want to create
236 // multiple copies here...
238 //Roo.log(this[cntr]());
239 cn.render(this[cntr]());
241 // then add the element..
249 if (typeof (tree.menu) != 'undefined') {
250 tree.menu.parentType = cn.xtype;
251 tree.menu.triggerEl = cn.el;
252 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
256 if (!tree.items || !tree.items.length) {
260 var items = tree.items;
263 //Roo.log(items.length);
265 for(var i =0;i < items.length;i++) {
266 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
287 * @class Roo.bootstrap.Body
288 * @extends Roo.bootstrap.Component
289 * Bootstrap Body class
293 * @param {Object} config The config object
296 Roo.bootstrap.Body = function(config){
297 Roo.bootstrap.Body.superclass.constructor.call(this, config);
298 this.el = Roo.get(document.body);
299 if (this.cls && this.cls.length) {
300 Roo.get(document.body).addClass(this.cls);
304 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
309 onRender : function(ct, position)
311 /* Roo.log("Roo.bootstrap.Body - onRender");
312 if (this.cls && this.cls.length) {
313 Roo.get(document.body).addClass(this.cls);
333 * @class Roo.bootstrap.ButtonGroup
334 * @extends Roo.bootstrap.Component
335 * Bootstrap ButtonGroup class
336 * @cfg {String} size lg | sm | xs (default empty normal)
337 * @cfg {String} align vertical | justified (default none)
338 * @cfg {String} direction up | down (default down)
339 * @cfg {Boolean} toolbar false | true
340 * @cfg {Boolean} btn true | false
345 * @param {Object} config The config object
348 Roo.bootstrap.ButtonGroup = function(config){
349 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
352 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
360 getAutoCreate : function(){
366 cfg.html = this.html || cfg.html;
377 if (['vertical','justified'].indexOf(this.align)!==-1) {
378 cfg.cls = 'btn-group-' + this.align;
380 if (this.align == 'justified') {
381 console.log(this.items);
385 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
386 cfg.cls += ' btn-group-' + this.size;
389 if (this.direction == 'up') {
390 cfg.cls += ' dropup' ;
406 * @class Roo.bootstrap.Button
407 * @extends Roo.bootstrap.Component
408 * Bootstrap Button class
409 * @cfg {String} html The button content
410 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
411 * @cfg {String} size empty | lg | sm | xs
412 * @cfg {String} tag empty | a | input | submit
413 * @cfg {String} href empty or href
414 * @cfg {Boolean} disabled false | true
415 * @cfg {Boolean} isClose false | true
416 * @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
417 * @cfg {String} badge text for badge
418 * @cfg {String} theme default (or empty) | glow
419 * @cfg {Boolean} inverse false | true
420 * @cfg {Boolean} toggle false | true
421 * @cfg {String} ontext text for on toggle state
422 * @cfg {String} offtext text for off toggle state
423 * @cfg {Boolean} defaulton true | false
424 * @cfg {Boolean} preventDefault (true | false) default true
425 * @cfg {Boolean} removeClass true | false remove the standard class..
426 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
429 * Create a new button
430 * @param {Object} config The config object
434 Roo.bootstrap.Button = function(config){
435 Roo.bootstrap.Button.superclass.constructor.call(this, config);
440 * When a butotn is pressed
441 * @param {Roo.EventObject} e
446 * After the button has been toggles
447 * @param {Roo.EventObject} e
448 * @param {boolean} pressed (also available as button.pressed)
454 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
472 preventDefault: true,
481 getAutoCreate : function(){
489 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
490 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
495 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
497 if (this.toggle == true) {
500 cls: 'slider-frame roo-button',
505 'data-off-text':'OFF',
506 cls: 'slider-button',
512 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
513 cfg.cls += ' '+this.weight;
522 cfg["aria-hidden"] = true;
524 cfg.html = "×";
530 if (this.theme==='default') {
531 cfg.cls = 'btn roo-button';
533 //if (this.parentType != 'Navbar') {
534 this.weight = this.weight.length ? this.weight : 'default';
536 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
538 cfg.cls += ' btn-' + this.weight;
540 } else if (this.theme==='glow') {
543 cfg.cls = 'btn-glow roo-button';
545 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
547 cfg.cls += ' ' + this.weight;
553 this.cls += ' inverse';
558 cfg.cls += ' active';
562 cfg.disabled = 'disabled';
566 Roo.log('changing to ul' );
568 this.glyphicon = 'caret';
571 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
573 //gsRoo.log(this.parentType);
574 if (this.parentType === 'Navbar' && !this.parent().bar) {
575 Roo.log('changing to li?');
584 href : this.href || '#'
587 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
588 cfg.cls += ' dropdown';
595 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
597 if (this.glyphicon) {
598 cfg.html = ' ' + cfg.html;
603 cls: 'glyphicon glyphicon-' + this.glyphicon
613 // cfg.cls='btn roo-button';
617 var value = cfg.html;
622 cls: 'glyphicon glyphicon-' + this.glyphicon,
641 cfg.cls += ' dropdown';
642 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
645 if (cfg.tag !== 'a' && this.href !== '') {
646 throw "Tag must be a to set href.";
647 } else if (this.href.length > 0) {
648 cfg.href = this.href;
651 if(this.removeClass){
656 cfg.target = this.target;
661 initEvents: function() {
662 // Roo.log('init events?');
663 // Roo.log(this.el.dom);
666 if (typeof (this.menu) != 'undefined') {
667 this.menu.parentType = this.xtype;
668 this.menu.triggerEl = this.el;
669 this.addxtype(Roo.apply({}, this.menu));
673 if (this.el.hasClass('roo-button')) {
674 this.el.on('click', this.onClick, this);
676 this.el.select('.roo-button').on('click', this.onClick, this);
679 if(this.removeClass){
680 this.el.on('click', this.onClick, this);
683 this.el.enableDisplayMode();
686 onClick : function(e)
692 Roo.log('button on click ');
693 if(this.preventDefault){
696 if (this.pressed === true || this.pressed === false) {
697 this.pressed = !this.pressed;
698 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
699 this.fireEvent('toggle', this, e, this.pressed);
703 this.fireEvent('click', this, e);
707 * Enables this button
711 this.disabled = false;
712 this.el.removeClass('disabled');
716 * Disable this button
720 this.disabled = true;
721 this.el.addClass('disabled');
724 * sets the active state on/off,
725 * @param {Boolean} state (optional) Force a particular state
727 setActive : function(v) {
729 this.el[v ? 'addClass' : 'removeClass']('active');
732 * toggles the current active state
734 toggleActive : function()
736 var active = this.el.hasClass('active');
737 this.setActive(!active);
741 setText : function(str)
743 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
766 * @class Roo.bootstrap.Column
767 * @extends Roo.bootstrap.Component
768 * Bootstrap Column class
769 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
770 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
771 * @cfg {Number} md colspan out of 12 for computer-sized screens
772 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
773 * @cfg {String} html content of column.
776 * Create a new Column
777 * @param {Object} config The config object
780 Roo.bootstrap.Column = function(config){
781 Roo.bootstrap.Column.superclass.constructor.call(this, config);
784 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
793 getAutoCreate : function(){
794 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
802 ['xs','sm','md','lg'].map(function(size){
803 if (settings[size]) {
804 cfg.cls += ' col-' + size + '-' + settings[size];
807 if (this.html.length) {
808 cfg.html = this.html;
827 * @class Roo.bootstrap.Container
828 * @extends Roo.bootstrap.Component
829 * Bootstrap Container class
830 * @cfg {Boolean} jumbotron is it a jumbotron element
831 * @cfg {String} html content of element
832 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
833 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
834 * @cfg {String} header content of header (for panel)
835 * @cfg {String} footer content of footer (for panel)
836 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
837 * @cfg {String} tag (header|aside|section) type of HTML tag.
841 * Create a new Container
842 * @param {Object} config The config object
845 Roo.bootstrap.Container = function(config){
846 Roo.bootstrap.Container.superclass.constructor.call(this, config);
849 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
860 getChildContainer : function() {
866 if (this.panel.length) {
867 return this.el.select('.panel-body',true).first();
874 getAutoCreate : function(){
877 tag : this.tag || 'div',
881 if (this.jumbotron) {
882 cfg.cls = 'jumbotron';
884 // - this is applied by the parent..
886 // cfg.cls = this.cls + '';
889 if (this.sticky.length) {
891 var bd = Roo.get(document.body);
892 if (!bd.hasClass('bootstrap-sticky')) {
893 bd.addClass('bootstrap-sticky');
894 Roo.select('html',true).setStyle('height', '100%');
897 cfg.cls += 'bootstrap-sticky-' + this.sticky;
901 if (this.well.length) {
905 cfg.cls +=' well well-' +this.well;
915 if (this.panel.length) {
916 cfg.cls += ' panel panel-' + this.panel;
918 if (this.header.length) {
921 cls : 'panel-heading',
937 if (this.footer.length) {
939 cls : 'panel-footer',
948 body.html = this.html || cfg.html;
950 if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
951 cfg.cls = 'container';
968 * @class Roo.bootstrap.Img
969 * @extends Roo.bootstrap.Component
970 * Bootstrap Img class
971 * @cfg {Boolean} imgResponsive false | true
972 * @cfg {String} border rounded | circle | thumbnail
973 * @cfg {String} src image source
974 * @cfg {String} alt image alternative text
975 * @cfg {String} href a tag href
976 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
980 * @param {Object} config The config object
983 Roo.bootstrap.Img = function(config){
984 Roo.bootstrap.Img.superclass.constructor.call(this, config);
990 * The img click event for the img.
991 * @param {Roo.EventObject} e
997 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
1005 getAutoCreate : function(){
1009 cls: (this.imgResponsive) ? 'img-responsive' : '',
1013 cfg.html = this.html || cfg.html;
1015 cfg.src = this.src || cfg.src;
1017 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1018 cfg.cls += ' img-' + this.border;
1035 a.target = this.target;
1041 return (this.href) ? a : cfg;
1044 initEvents: function() {
1047 this.el.on('click', this.onClick, this);
1051 onClick : function(e)
1053 Roo.log('img onclick');
1054 this.fireEvent('click', this, e);
1068 * @class Roo.bootstrap.Link
1069 * @extends Roo.bootstrap.Component
1070 * Bootstrap Link Class
1071 * @cfg {String} alt image alternative text
1072 * @cfg {String} href a tag href
1073 * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1074 * @cfg {String} html the content of the link.
1078 * Create a new Input
1079 * @param {Object} config The config object
1082 Roo.bootstrap.Link = function(config){
1083 Roo.bootstrap.Link.superclass.constructor.call(this, config);
1089 * The img click event for the img.
1090 * @param {Roo.EventObject} e
1096 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component, {
1101 getAutoCreate : function(){
1105 html : this.html || 'html-missing'
1112 cfg.href = this.href || '#';
1114 cfg.target = this.target;
1120 initEvents: function() {
1123 this.el.on('click', this.onClick, this);
1127 onClick : function(e)
1129 //Roo.log('img onclick');
1130 this.fireEvent('click', this, e);
1143 * @class Roo.bootstrap.Header
1144 * @extends Roo.bootstrap.Component
1145 * Bootstrap Header class
1146 * @cfg {String} html content of header
1147 * @cfg {Number} level (1|2|3|4|5|6) default 1
1150 * Create a new Header
1151 * @param {Object} config The config object
1155 Roo.bootstrap.Header = function(config){
1156 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1159 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1167 getAutoCreate : function(){
1170 tag: 'h' + (1 *this.level),
1171 html: this.html || 'fill in html'
1183 * Ext JS Library 1.1.1
1184 * Copyright(c) 2006-2007, Ext JS, LLC.
1186 * Originally Released Under LGPL - original licence link has changed is not relivant.
1189 * <script type="text/javascript">
1193 * @class Roo.bootstrap.MenuMgr
1194 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1197 Roo.bootstrap.MenuMgr = function(){
1198 var menus, active, groups = {}, attached = false, lastShow = new Date();
1200 // private - called when first menu is created
1203 active = new Roo.util.MixedCollection();
1204 Roo.get(document).addKeyListener(27, function(){
1205 if(active.length > 0){
1213 if(active && active.length > 0){
1214 var c = active.clone();
1224 if(active.length < 1){
1225 Roo.get(document).un("mouseup", onMouseDown);
1233 var last = active.last();
1234 lastShow = new Date();
1237 Roo.get(document).on("mouseup", onMouseDown);
1242 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1243 m.parentMenu.activeChild = m;
1244 }else if(last && last.isVisible()){
1245 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1250 function onBeforeHide(m){
1252 m.activeChild.hide();
1254 if(m.autoHideTimer){
1255 clearTimeout(m.autoHideTimer);
1256 delete m.autoHideTimer;
1261 function onBeforeShow(m){
1262 var pm = m.parentMenu;
1263 if(!pm && !m.allowOtherMenus){
1265 }else if(pm && pm.activeChild && active != m){
1266 pm.activeChild.hide();
1271 function onMouseDown(e){
1272 Roo.log("on MouseDown");
1273 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1281 function onBeforeCheck(mi, state){
1283 var g = groups[mi.group];
1284 for(var i = 0, l = g.length; i < l; i++){
1286 g[i].setChecked(false);
1295 * Hides all menus that are currently visible
1297 hideAll : function(){
1302 register : function(menu){
1306 menus[menu.id] = menu;
1307 menu.on("beforehide", onBeforeHide);
1308 menu.on("hide", onHide);
1309 menu.on("beforeshow", onBeforeShow);
1310 menu.on("show", onShow);
1312 if(g && menu.events["checkchange"]){
1316 groups[g].push(menu);
1317 menu.on("checkchange", onCheck);
1322 * Returns a {@link Roo.menu.Menu} object
1323 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1324 * be used to generate and return a new Menu instance.
1326 get : function(menu){
1327 if(typeof menu == "string"){ // menu id
1329 }else if(menu.events){ // menu instance
1332 /*else if(typeof menu.length == 'number'){ // array of menu items?
1333 return new Roo.bootstrap.Menu({items:menu});
1334 }else{ // otherwise, must be a config
1335 return new Roo.bootstrap.Menu(menu);
1342 unregister : function(menu){
1343 delete menus[menu.id];
1344 menu.un("beforehide", onBeforeHide);
1345 menu.un("hide", onHide);
1346 menu.un("beforeshow", onBeforeShow);
1347 menu.un("show", onShow);
1349 if(g && menu.events["checkchange"]){
1350 groups[g].remove(menu);
1351 menu.un("checkchange", onCheck);
1356 registerCheckable : function(menuItem){
1357 var g = menuItem.group;
1362 groups[g].push(menuItem);
1363 menuItem.on("beforecheckchange", onBeforeCheck);
1368 unregisterCheckable : function(menuItem){
1369 var g = menuItem.group;
1371 groups[g].remove(menuItem);
1372 menuItem.un("beforecheckchange", onBeforeCheck);
1384 * @class Roo.bootstrap.Menu
1385 * @extends Roo.bootstrap.Component
1386 * Bootstrap Menu class - container for MenuItems
1387 * @cfg {String} type (dropdown|treeview|submenu) type of menu
1391 * @param {Object} config The config object
1395 Roo.bootstrap.Menu = function(config){
1396 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1397 if (this.registerMenu) {
1398 Roo.bootstrap.MenuMgr.register(this);
1403 * Fires before this menu is displayed
1404 * @param {Roo.menu.Menu} this
1409 * Fires before this menu is hidden
1410 * @param {Roo.menu.Menu} this
1415 * Fires after this menu is displayed
1416 * @param {Roo.menu.Menu} this
1421 * Fires after this menu is hidden
1422 * @param {Roo.menu.Menu} this
1427 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1428 * @param {Roo.menu.Menu} this
1429 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1430 * @param {Roo.EventObject} e
1435 * Fires when the mouse is hovering over this menu
1436 * @param {Roo.menu.Menu} this
1437 * @param {Roo.EventObject} e
1438 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1443 * Fires when the mouse exits this menu
1444 * @param {Roo.menu.Menu} this
1445 * @param {Roo.EventObject} e
1446 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1451 * Fires when a menu item contained in this menu is clicked
1452 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1453 * @param {Roo.EventObject} e
1457 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1460 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1464 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1467 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1469 registerMenu : true,
1471 menuItems :false, // stores the menu items..
1477 getChildContainer : function() {
1481 getAutoCreate : function(){
1483 //if (['right'].indexOf(this.align)!==-1) {
1484 // cfg.cn[1].cls += ' pull-right'
1490 cls : 'dropdown-menu' ,
1491 style : 'z-index:1000'
1495 if (this.type === 'submenu') {
1496 cfg.cls = 'submenu active';
1498 if (this.type === 'treeview') {
1499 cfg.cls = 'treeview-menu';
1504 initEvents : function() {
1506 // Roo.log("ADD event");
1507 // Roo.log(this.triggerEl.dom);
1508 this.triggerEl.on('click', this.onTriggerPress, this);
1509 this.triggerEl.addClass('dropdown-toggle');
1510 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1512 this.el.on("mouseover", this.onMouseOver, this);
1513 this.el.on("mouseout", this.onMouseOut, this);
1517 findTargetItem : function(e){
1518 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1522 //Roo.log(t); Roo.log(t.id);
1524 //Roo.log(this.menuitems);
1525 return this.menuitems.get(t.id);
1527 //return this.items.get(t.menuItemId);
1532 onClick : function(e){
1533 Roo.log("menu.onClick");
1534 var t = this.findTargetItem(e);
1540 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1541 if(t == this.activeItem && t.shouldDeactivate(e)){
1542 this.activeItem.deactivate();
1543 delete this.activeItem;
1547 this.setActiveItem(t, true);
1554 Roo.log('pass click event');
1558 this.fireEvent("click", this, t, e);
1562 onMouseOver : function(e){
1563 var t = this.findTargetItem(e);
1566 // if(t.canActivate && !t.disabled){
1567 // this.setActiveItem(t, true);
1571 this.fireEvent("mouseover", this, e, t);
1573 isVisible : function(){
1574 return !this.hidden;
1576 onMouseOut : function(e){
1577 var t = this.findTargetItem(e);
1580 // if(t == this.activeItem && t.shouldDeactivate(e)){
1581 // this.activeItem.deactivate();
1582 // delete this.activeItem;
1585 this.fireEvent("mouseout", this, e, t);
1590 * Displays this menu relative to another element
1591 * @param {String/HTMLElement/Roo.Element} element The element to align to
1592 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1593 * the element (defaults to this.defaultAlign)
1594 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1596 show : function(el, pos, parentMenu){
1597 this.parentMenu = parentMenu;
1601 this.fireEvent("beforeshow", this);
1602 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1605 * Displays this menu at a specific xy position
1606 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1607 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1609 showAt : function(xy, parentMenu, /* private: */_e){
1610 this.parentMenu = parentMenu;
1615 this.fireEvent("beforeshow", this);
1617 //xy = this.el.adjustForConstraints(xy);
1619 //this.el.setXY(xy);
1621 this.hideMenuItems();
1622 this.hidden = false;
1623 this.triggerEl.addClass('open');
1625 this.fireEvent("show", this);
1631 this.doFocus.defer(50, this);
1635 doFocus : function(){
1637 this.focusEl.focus();
1642 * Hides this menu and optionally all parent menus
1643 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1645 hide : function(deep){
1647 this.hideMenuItems();
1648 if(this.el && this.isVisible()){
1649 this.fireEvent("beforehide", this);
1650 if(this.activeItem){
1651 this.activeItem.deactivate();
1652 this.activeItem = null;
1654 this.triggerEl.removeClass('open');;
1656 this.fireEvent("hide", this);
1658 if(deep === true && this.parentMenu){
1659 this.parentMenu.hide(true);
1663 onTriggerPress : function(e)
1666 Roo.log('trigger press');
1667 //Roo.log(e.getTarget());
1668 // Roo.log(this.triggerEl.dom);
1669 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1672 if (this.isVisible()) {
1676 this.show(this.triggerEl, false, false);
1685 hideMenuItems : function()
1687 //$(backdrop).remove()
1688 Roo.select('.open',true).each(function(aa) {
1690 aa.removeClass('open');
1691 //var parent = getParent($(this))
1692 //var relatedTarget = { relatedTarget: this }
1694 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1695 //if (e.isDefaultPrevented()) return
1696 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1699 addxtypeChild : function (tree, cntr) {
1700 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1702 this.menuitems.add(comp);
1723 * @class Roo.bootstrap.MenuItem
1724 * @extends Roo.bootstrap.Component
1725 * Bootstrap MenuItem class
1726 * @cfg {String} html the menu label
1727 * @cfg {String} href the link
1728 * @cfg {Boolean} preventDefault (true | false) default true
1732 * Create a new MenuItem
1733 * @param {Object} config The config object
1737 Roo.bootstrap.MenuItem = function(config){
1738 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1743 * The raw click event for the entire grid.
1744 * @param {Roo.EventObject} e
1750 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1754 preventDefault: true,
1756 getAutoCreate : function(){
1759 cls: 'dropdown-menu-item',
1768 if (this.parent().type == 'treeview') {
1769 cfg.cls = 'treeview-menu';
1772 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1773 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1777 initEvents: function() {
1779 //this.el.select('a').on('click', this.onClick, this);
1782 onClick : function(e)
1784 Roo.log('item on click ');
1785 //if(this.preventDefault){
1786 // e.preventDefault();
1788 //this.parent().hideMenuItems();
1790 this.fireEvent('click', this, e);
1809 * @class Roo.bootstrap.MenuSeparator
1810 * @extends Roo.bootstrap.Component
1811 * Bootstrap MenuSeparator class
1814 * Create a new MenuItem
1815 * @param {Object} config The config object
1819 Roo.bootstrap.MenuSeparator = function(config){
1820 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1823 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1825 getAutoCreate : function(){
1840 <div class="modal fade">
1841 <div class="modal-dialog">
1842 <div class="modal-content">
1843 <div class="modal-header">
1844 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1845 <h4 class="modal-title">Modal title</h4>
1847 <div class="modal-body">
1848 <p>One fine body…</p>
1850 <div class="modal-footer">
1851 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1852 <button type="button" class="btn btn-primary">Save changes</button>
1854 </div><!-- /.modal-content -->
1855 </div><!-- /.modal-dialog -->
1856 </div><!-- /.modal -->
1866 * @class Roo.bootstrap.Modal
1867 * @extends Roo.bootstrap.Component
1868 * Bootstrap Modal class
1869 * @cfg {String} title Title of dialog
1870 * @cfg {Boolean} specificTitle (true|false) default false
1871 * @cfg {Array} buttons Array of buttons or standard button set..
1872 * @cfg {String} buttonPosition (left|right|center) default right
1875 * Create a new Modal Dialog
1876 * @param {Object} config The config object
1879 Roo.bootstrap.Modal = function(config){
1880 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1885 * The raw btnclick event for the button
1886 * @param {Roo.EventObject} e
1890 this.buttons = this.buttons || [];
1893 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1895 title : 'test dialog',
1902 specificTitle: false,
1904 buttonPosition: 'right',
1906 onRender : function(ct, position)
1908 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1911 var cfg = Roo.apply({}, this.getAutoCreate());
1914 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1916 //if (!cfg.name.length) {
1920 cfg.cls += ' ' + this.cls;
1923 cfg.style = this.style;
1925 this.el = Roo.get(document.body).createChild(cfg, position);
1927 //var type = this.el.dom.type;
1929 if(this.tabIndex !== undefined){
1930 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1935 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1936 this.maskEl.enableDisplayMode("block");
1938 //this.el.addClass("x-dlg-modal");
1940 if (this.buttons.length) {
1941 Roo.each(this.buttons, function(bb) {
1942 b = Roo.apply({}, bb);
1943 b.xns = b.xns || Roo.bootstrap;
1944 b.xtype = b.xtype || 'Button';
1945 if (typeof(b.listeners) == 'undefined') {
1946 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1949 var btn = Roo.factory(b);
1951 btn.onRender(this.el.select('.modal-footer div').first());
1955 // render the children.
1958 if(typeof(this.items) != 'undefined'){
1959 var items = this.items;
1962 for(var i =0;i < items.length;i++) {
1963 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1967 this.items = nitems;
1969 this.body = this.el.select('.modal-body',true).first();
1970 this.close = this.el.select('.modal-header .close', true).first();
1971 this.footer = this.el.select('.modal-footer',true).first();
1973 //this.el.addClass([this.fieldClass, this.cls]);
1976 getAutoCreate : function(){
1981 html : this.html || ''
1986 cls : 'modal-title',
1990 if(this.specificTitle){
1996 style : 'display: none',
1999 cls: "modal-dialog",
2002 cls : "modal-content",
2005 cls : 'modal-header',
2017 cls : 'modal-footer',
2021 cls: 'btn-' + this.buttonPosition
2040 getChildContainer : function() {
2042 return this.el.select('.modal-body',true).first();
2045 getButtonContainer : function() {
2046 return this.el.select('.modal-footer div',true).first();
2049 initEvents : function()
2051 this.el.select('.modal-header .close').on('click', this.hide, this);
2053 // this.addxtype(this);
2057 if (!this.rendered) {
2061 this.el.addClass('on');
2062 this.el.removeClass('fade');
2063 this.el.setStyle('display', 'block');
2064 Roo.get(document.body).addClass("x-body-masked");
2065 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
2067 this.el.setStyle('zIndex', '10001');
2068 this.fireEvent('show', this);
2074 Roo.log('Modal hide?!');
2076 Roo.get(document.body).removeClass("x-body-masked");
2077 this.el.removeClass('on');
2078 this.el.addClass('fade');
2079 this.el.setStyle('display', 'none');
2080 this.fireEvent('hide', this);
2083 addButton : function(str, cb)
2087 var b = Roo.apply({}, { html : str } );
2088 b.xns = b.xns || Roo.bootstrap;
2089 b.xtype = b.xtype || 'Button';
2090 if (typeof(b.listeners) == 'undefined') {
2091 b.listeners = { click : cb.createDelegate(this) };
2094 var btn = Roo.factory(b);
2096 btn.onRender(this.el.select('.modal-footer div').first());
2102 setDefaultButton : function(btn)
2104 //this.el.select('.modal-footer').()
2106 resizeTo: function(w,h)
2110 setContentSize : function(w, h)
2114 onButtonClick: function(btn,e)
2117 this.fireEvent('btnclick', btn.name, e);
2119 setTitle: function(str) {
2120 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2126 Roo.apply(Roo.bootstrap.Modal, {
2128 * Button config that displays a single OK button
2137 * Button config that displays Yes and No buttons
2153 * Button config that displays OK and Cancel buttons
2168 * Button config that displays Yes, No and Cancel buttons
2190 * messagebox - can be used as a replace
2194 * @class Roo.MessageBox
2195 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2199 Roo.Msg.alert('Status', 'Changes saved successfully.');
2201 // Prompt for user data:
2202 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2204 // process text value...
2208 // Show a dialog using config options:
2210 title:'Save Changes?',
2211 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2212 buttons: Roo.Msg.YESNOCANCEL,
2219 Roo.bootstrap.MessageBox = function(){
2220 var dlg, opt, mask, waitTimer;
2221 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2222 var buttons, activeTextEl, bwidth;
2226 var handleButton = function(button){
2228 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2232 var handleHide = function(){
2234 dlg.el.removeClass(opt.cls);
2237 // Roo.TaskMgr.stop(waitTimer);
2238 // waitTimer = null;
2243 var updateButtons = function(b){
2246 buttons["ok"].hide();
2247 buttons["cancel"].hide();
2248 buttons["yes"].hide();
2249 buttons["no"].hide();
2250 //dlg.footer.dom.style.display = 'none';
2253 dlg.footer.dom.style.display = '';
2254 for(var k in buttons){
2255 if(typeof buttons[k] != "function"){
2258 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2259 width += buttons[k].el.getWidth()+15;
2269 var handleEsc = function(d, k, e){
2270 if(opt && opt.closable !== false){
2280 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2281 * @return {Roo.BasicDialog} The BasicDialog element
2283 getDialog : function(){
2285 dlg = new Roo.bootstrap.Modal( {
2288 //constraintoviewport:false,
2290 //collapsible : false,
2295 //buttonAlign:"center",
2296 closeClick : function(){
2297 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2300 handleButton("cancel");
2305 dlg.on("hide", handleHide);
2307 //dlg.addKeyListener(27, handleEsc);
2309 this.buttons = buttons;
2310 var bt = this.buttonText;
2311 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2312 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2313 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2314 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2316 bodyEl = dlg.body.createChild({
2318 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2319 '<textarea class="roo-mb-textarea"></textarea>' +
2320 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2322 msgEl = bodyEl.dom.firstChild;
2323 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2324 textboxEl.enableDisplayMode();
2325 textboxEl.addKeyListener([10,13], function(){
2326 if(dlg.isVisible() && opt && opt.buttons){
2329 }else if(opt.buttons.yes){
2330 handleButton("yes");
2334 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2335 textareaEl.enableDisplayMode();
2336 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2337 progressEl.enableDisplayMode();
2338 var pf = progressEl.dom.firstChild;
2340 pp = Roo.get(pf.firstChild);
2341 pp.setHeight(pf.offsetHeight);
2349 * Updates the message box body text
2350 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2351 * the XHTML-compliant non-breaking space character '&#160;')
2352 * @return {Roo.MessageBox} This message box
2354 updateText : function(text){
2355 if(!dlg.isVisible() && !opt.width){
2356 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2358 msgEl.innerHTML = text || ' ';
2360 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2361 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2363 Math.min(opt.width || cw , this.maxWidth),
2364 Math.max(opt.minWidth || this.minWidth, bwidth)
2367 activeTextEl.setWidth(w);
2369 if(dlg.isVisible()){
2370 dlg.fixedcenter = false;
2372 // to big, make it scroll. = But as usual stupid IE does not support
2375 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2376 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2377 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2379 bodyEl.dom.style.height = '';
2380 bodyEl.dom.style.overflowY = '';
2383 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2385 bodyEl.dom.style.overflowX = '';
2388 dlg.setContentSize(w, bodyEl.getHeight());
2389 if(dlg.isVisible()){
2390 dlg.fixedcenter = true;
2396 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2397 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2398 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2399 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2400 * @return {Roo.MessageBox} This message box
2402 updateProgress : function(value, text){
2404 this.updateText(text);
2406 if (pp) { // weird bug on my firefox - for some reason this is not defined
2407 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2413 * Returns true if the message box is currently displayed
2414 * @return {Boolean} True if the message box is visible, else false
2416 isVisible : function(){
2417 return dlg && dlg.isVisible();
2421 * Hides the message box if it is displayed
2424 if(this.isVisible()){
2430 * Displays a new message box, or reinitializes an existing message box, based on the config options
2431 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2432 * The following config object properties are supported:
2434 Property Type Description
2435 ---------- --------------- ------------------------------------------------------------------------------------
2436 animEl String/Element An id or Element from which the message box should animate as it opens and
2437 closes (defaults to undefined)
2438 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2439 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2440 closable Boolean False to hide the top-right close button (defaults to true). Note that
2441 progress and wait dialogs will ignore this property and always hide the
2442 close button as they can only be closed programmatically.
2443 cls String A custom CSS class to apply to the message box element
2444 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2445 displayed (defaults to 75)
2446 fn Function A callback function to execute after closing the dialog. The arguments to the
2447 function will be btn (the name of the button that was clicked, if applicable,
2448 e.g. "ok"), and text (the value of the active text field, if applicable).
2449 Progress and wait dialogs will ignore this option since they do not respond to
2450 user actions and can only be closed programmatically, so any required function
2451 should be called by the same code after it closes the dialog.
2452 icon String A CSS class that provides a background image to be used as an icon for
2453 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2454 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2455 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2456 modal Boolean False to allow user interaction with the page while the message box is
2457 displayed (defaults to true)
2458 msg String A string that will replace the existing message box body text (defaults
2459 to the XHTML-compliant non-breaking space character ' ')
2460 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2461 progress Boolean True to display a progress bar (defaults to false)
2462 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2463 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2464 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2465 title String The title text
2466 value String The string value to set into the active textbox element if displayed
2467 wait Boolean True to display a progress bar (defaults to false)
2468 width Number The width of the dialog in pixels
2475 msg: 'Please enter your address:',
2477 buttons: Roo.MessageBox.OKCANCEL,
2480 animEl: 'addAddressBtn'
2483 * @param {Object} config Configuration options
2484 * @return {Roo.MessageBox} This message box
2486 show : function(options)
2489 // this causes nightmares if you show one dialog after another
2490 // especially on callbacks..
2492 if(this.isVisible()){
2495 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2496 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2497 Roo.log("New Dialog Message:" + options.msg )
2498 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2499 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2502 var d = this.getDialog();
2504 d.setTitle(opt.title || " ");
2505 d.close.setDisplayed(opt.closable !== false);
2506 activeTextEl = textboxEl;
2507 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2512 textareaEl.setHeight(typeof opt.multiline == "number" ?
2513 opt.multiline : this.defaultTextHeight);
2514 activeTextEl = textareaEl;
2523 progressEl.setDisplayed(opt.progress === true);
2524 this.updateProgress(0);
2525 activeTextEl.dom.value = opt.value || "";
2527 dlg.setDefaultButton(activeTextEl);
2529 var bs = opt.buttons;
2533 }else if(bs && bs.yes){
2534 db = buttons["yes"];
2536 dlg.setDefaultButton(db);
2538 bwidth = updateButtons(opt.buttons);
2539 this.updateText(opt.msg);
2541 d.el.addClass(opt.cls);
2543 d.proxyDrag = opt.proxyDrag === true;
2544 d.modal = opt.modal !== false;
2545 d.mask = opt.modal !== false ? mask : false;
2547 // force it to the end of the z-index stack so it gets a cursor in FF
2548 document.body.appendChild(dlg.el.dom);
2549 d.animateTarget = null;
2550 d.show(options.animEl);
2556 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2557 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2558 * and closing the message box when the process is complete.
2559 * @param {String} title The title bar text
2560 * @param {String} msg The message box body text
2561 * @return {Roo.MessageBox} This message box
2563 progress : function(title, msg){
2570 minWidth: this.minProgressWidth,
2577 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2578 * If a callback function is passed it will be called after the user clicks the button, and the
2579 * id of the button that was clicked will be passed as the only parameter to the callback
2580 * (could also be the top-right close button).
2581 * @param {String} title The title bar text
2582 * @param {String} msg The message box body text
2583 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2584 * @param {Object} scope (optional) The scope of the callback function
2585 * @return {Roo.MessageBox} This message box
2587 alert : function(title, msg, fn, scope){
2600 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2601 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2602 * You are responsible for closing the message box when the process is complete.
2603 * @param {String} msg The message box body text
2604 * @param {String} title (optional) The title bar text
2605 * @return {Roo.MessageBox} This message box
2607 wait : function(msg, title){
2618 waitTimer = Roo.TaskMgr.start({
2620 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2628 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2629 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2630 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2631 * @param {String} title The title bar text
2632 * @param {String} msg The message box body text
2633 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2634 * @param {Object} scope (optional) The scope of the callback function
2635 * @return {Roo.MessageBox} This message box
2637 confirm : function(title, msg, fn, scope){
2641 buttons: this.YESNO,
2650 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2651 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2652 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2653 * (could also be the top-right close button) and the text that was entered will be passed as the two
2654 * parameters to the callback.
2655 * @param {String} title The title bar text
2656 * @param {String} msg The message box body text
2657 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2658 * @param {Object} scope (optional) The scope of the callback function
2659 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2660 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2661 * @return {Roo.MessageBox} This message box
2663 prompt : function(title, msg, fn, scope, multiline){
2667 buttons: this.OKCANCEL,
2672 multiline: multiline,
2679 * Button config that displays a single OK button
2684 * Button config that displays Yes and No buttons
2687 YESNO : {yes:true, no:true},
2689 * Button config that displays OK and Cancel buttons
2692 OKCANCEL : {ok:true, cancel:true},
2694 * Button config that displays Yes, No and Cancel buttons
2697 YESNOCANCEL : {yes:true, no:true, cancel:true},
2700 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2703 defaultTextHeight : 75,
2705 * The maximum width in pixels of the message box (defaults to 600)
2710 * The minimum width in pixels of the message box (defaults to 100)
2715 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2716 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2719 minProgressWidth : 250,
2721 * An object containing the default button text strings that can be overriden for localized language support.
2722 * Supported properties are: ok, cancel, yes and no.
2723 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2736 * Shorthand for {@link Roo.MessageBox}
2738 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2739 Roo.Msg = Roo.Msg || Roo.MessageBox;
2748 * @class Roo.bootstrap.Navbar
2749 * @extends Roo.bootstrap.Component
2750 * Bootstrap Navbar class
2753 * Create a new Navbar
2754 * @param {Object} config The config object
2758 Roo.bootstrap.Navbar = function(config){
2759 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2763 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2772 getAutoCreate : function(){
2775 throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
2779 initEvents :function ()
2781 //Roo.log(this.el.select('.navbar-toggle',true));
2782 this.el.select('.navbar-toggle',true).on('click', function() {
2783 // Roo.log('click');
2784 this.el.select('.navbar-collapse',true).toggleClass('in');
2792 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2794 var size = this.el.getSize();
2795 this.maskEl.setSize(size.width, size.height);
2796 this.maskEl.enableDisplayMode("block");
2805 getChildContainer : function()
2807 if (this.el.select('.collapse').getCount()) {
2808 return this.el.select('.collapse',true).first();
2840 * @class Roo.bootstrap.NavSimplebar
2841 * @extends Roo.bootstrap.Navbar
2842 * Bootstrap Sidebar class
2844 * @cfg {Boolean} inverse is inverted color
2846 * @cfg {String} type (nav | pills | tabs)
2847 * @cfg {Boolean} arrangement stacked | justified
2848 * @cfg {String} align (left | right) alignment
2850 * @cfg {Boolean} main (true|false) main nav bar? default false
2851 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2853 * @cfg {String} tag (header|footer|nav|div) default is nav
2859 * Create a new Sidebar
2860 * @param {Object} config The config object
2864 Roo.bootstrap.NavSimplebar = function(config){
2865 Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
2868 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar, {
2884 getAutoCreate : function(){
2888 tag : this.tag || 'div',
2901 this.type = this.type || 'nav';
2902 if (['tabs','pills'].indexOf(this.type)!==-1) {
2903 cfg.cn[0].cls += ' nav-' + this.type
2907 if (this.type!=='nav') {
2908 Roo.log('nav type must be nav/tabs/pills')
2910 cfg.cn[0].cls += ' navbar-nav'
2916 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2917 cfg.cn[0].cls += ' nav-' + this.arrangement;
2921 if (this.align === 'right') {
2922 cfg.cn[0].cls += ' navbar-right';
2926 cfg.cls += ' navbar-inverse';
2953 * @class Roo.bootstrap.NavHeaderbar
2954 * @extends Roo.bootstrap.NavSimplebar
2955 * Bootstrap Sidebar class
2957 * @cfg {String} brand what is brand
2958 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2959 * @cfg {String} brand_href href of the brand
2962 * Create a new Sidebar
2963 * @param {Object} config The config object
2967 Roo.bootstrap.NavHeaderbar = function(config){
2968 Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
2971 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar, {
2978 getAutoCreate : function(){
2983 tag: this.nav || 'nav',
2989 cls: 'navbar-header',
2994 cls: 'navbar-toggle',
2995 'data-toggle': 'collapse',
3000 html: 'Toggle navigation'
3020 cls: 'collapse navbar-collapse'
3025 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3027 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3028 cfg.cls += ' navbar-' + this.position;
3030 // tag can override this..
3032 cfg.tag = this.tag || (this.position == 'fixed-bottom' ? 'footer' : 'header');
3035 if (this.brand !== '') {
3038 href: this.brand_href ? this.brand_href : '#',
3039 cls: 'navbar-brand',
3047 cfg.cls += ' main-nav';
3072 * @class Roo.bootstrap.NavSidebar
3073 * @extends Roo.bootstrap.Navbar
3074 * Bootstrap Sidebar class
3077 * Create a new Sidebar
3078 * @param {Object} config The config object
3082 Roo.bootstrap.NavSidebar = function(config){
3083 Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3086 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar, {
3088 sidebar : true, // used by Navbar Item and NavbarGroup at present...
3090 getAutoCreate : function(){
3095 cls: 'sidebar sidebar-nav'
3117 * @class Roo.bootstrap.NavGroup
3118 * @extends Roo.bootstrap.Component
3119 * Bootstrap NavGroup class
3120 * @cfg {String} align left | right
3121 * @cfg {Boolean} inverse false | true
3122 * @cfg {String} type (nav|pills|tab) default nav
3123 * @cfg {String} navId - reference Id for navbar.
3127 * Create a new nav group
3128 * @param {Object} config The config object
3131 Roo.bootstrap.NavGroup = function(config){
3132 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3134 Roo.bootstrap.NavGroup.register(this);
3138 * Fires when the active item changes
3139 * @param {Roo.bootstrap.NavGroup} this
3140 * @param {Roo.bootstrap.Navbar.Item} item The item selected
3141 * @param {Roo.bootstrap.Navbar.Item} item The previously selected item
3148 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
3159 getAutoCreate : function()
3161 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3168 if (['tabs','pills'].indexOf(this.type)!==-1) {
3169 cfg.cls += ' nav-' + this.type
3171 if (this.type!=='nav') {
3172 Roo.log('nav type must be nav/tabs/pills')
3174 cfg.cls += ' navbar-nav'
3177 if (this.parent().sidebar) {
3180 cls: 'dashboard-menu sidebar-menu'
3186 if (this.form === true) {
3192 if (this.align === 'right') {
3193 cfg.cls += ' navbar-right';
3195 cfg.cls += ' navbar-left';
3199 if (this.align === 'right') {
3200 cfg.cls += ' navbar-right';
3204 cfg.cls += ' navbar-inverse';
3212 setActiveItem : function(item)
3215 Roo.each(this.navItems, function(v){
3220 v.setActive(false, true);
3227 item.setActive(true, true);
3228 this.fireEvent('changed', this, item, prev);
3234 register : function(item)
3236 this.navItems.push( item);
3237 item.navId = this.navId;
3240 getNavItem: function(tabId)
3243 Roo.each(this.navItems, function(e) {
3244 if (e.tabId == tabId) {
3256 Roo.apply(Roo.bootstrap.NavGroup, {
3260 register : function(navgrp)
3262 this.groups[navgrp.navId] = navgrp;
3265 get: function(navId) {
3266 return this.groups[navId];
3281 * @class Roo.bootstrap.Navbar.Item
3282 * @extends Roo.bootstrap.Component
3283 * Bootstrap Navbar.Button class
3284 * @cfg {String} href link to
3285 * @cfg {String} html content of button
3286 * @cfg {String} badge text inside badge
3287 * @cfg {String} glyphicon name of glyphicon
3288 * @cfg {String} icon name of font awesome icon
3289 * @cfg {Boolean} active Is item active
3290 * @cfg {Boolean} preventDefault (true | false) default false
3291 * @cfg {String} tabId the tab that this item activates.
3294 * Create a new Navbar Button
3295 * @param {Object} config The config object
3297 Roo.bootstrap.Navbar.Item = function(config){
3298 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3303 * The raw click event for the entire grid.
3304 * @param {Roo.EventObject} e
3309 * Fires when the active item active state changes
3310 * @param {Roo.bootstrap.Navbar.Item} this
3311 * @param {boolean} state the new state
3319 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
3327 preventDefault : false,
3330 getAutoCreate : function(){
3332 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3334 if (this.parent().parent().sidebar === true) {
3347 cfg.cn[0].html = this.html;
3351 this.cls += ' active';
3355 cfg.cn[0].cls += ' dropdown-toggle';
3356 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3360 cfg.cn[0].tag = 'a',
3361 cfg.cn[0].href = this.href;
3364 if (this.glyphicon) {
3365 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3369 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3381 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3391 if (this.glyphicon) {
3392 if(cfg.html){cfg.html = ' ' + this.html};
3396 cls: 'glyphicon glyphicon-' + this.glyphicon
3401 cfg.cn[0].html = this.html || cfg.cn[0].html ;
3406 cfg.cn[0].html += " <span class='caret'></span>";
3407 //}else if (!this.href) {
3408 // cfg.cn[0].tag='p';
3409 // cfg.cn[0].cls='navbar-text';
3412 cfg.cn[0].href=this.href||'#';
3413 cfg.cn[0].html=this.html;
3416 if (this.badge !== '') {
3419 cfg.cn[0].html + ' ',
3430 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3435 initEvents: function() {
3436 // Roo.log('init events?');
3437 // Roo.log(this.el.dom);
3438 this.el.select('a',true).on('click', this.onClick, this);
3439 // at this point parent should be available..
3440 this.parent().register(this);
3443 onClick : function(e)
3445 if(this.preventDefault){
3449 if(this.fireEvent('click', this, e) === false){
3453 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3454 if (typeof(this.parent().setActiveItem) !== 'undefined') {
3455 this.parent().setActiveItem(this);
3463 isActive: function () {
3466 setActive : function(state, fire)
3468 this.active = state;
3470 this.el.removeClass('active');
3471 } else if (!this.el.hasClass('active')) {
3472 this.el.addClass('active');
3475 this.fireEvent('changed', this, state);
3480 // this should not be here...
3493 * @class Roo.bootstrap.NavItem
3494 * @extends Roo.bootstrap.Component
3495 * Bootstrap Navbar.NavItem class
3496 * @cfg {String} href link to
3497 * @cfg {String} html content of button
3498 * @cfg {String} badge text inside badge
3499 * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
3500 * @cfg {String} glyphicon name of glyphicon
3501 * @cfg {String} icon name of font awesome icon
3502 * @cfg {Boolean} active Is item active
3503 * @cfg {Boolean} preventDefault (true | false) default false
3504 * @cfg {String} tabId the tab that this item activates.
3507 * Create a new Navbar Item
3508 * @param {Object} config The config object
3510 Roo.bootstrap.NavItem = function(config){
3511 Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
3516 * The raw click event for the entire grid.
3517 * @param {Roo.EventObject} e
3522 * Fires when the active item active state changes
3523 * @param {Roo.bootstrap.NavItem} this
3524 * @param {boolean} state the new state
3532 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component, {
3540 preventDefault : false,
3543 getAutoCreate : function(){
3551 href : this.href || "#",
3552 html: this.html || ''
3558 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3561 // glyphicon and icon go before content..
3562 if (this.glyphicon || this.icon) {
3564 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html + '</span>'
3566 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span>' + cfg.cn[0].html;
3574 cfg.cn[0].html += " <span class='caret'></span>";
3578 if (this.badge !== '') {
3580 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
3587 initEvents: function() {
3588 // Roo.log('init events?');
3589 // Roo.log(this.el.dom);
3590 if (typeof (this.menu) != 'undefined') {
3591 this.menu.parentType = this.xtype;
3592 this.menu.triggerEl = this.el;
3593 this.addxtype(Roo.apply({}, this.menu));
3597 this.el.select('a',true).on('click', this.onClick, this);
3598 // at this point parent should be available..
3599 this.parent().register(this);
3602 onClick : function(e)
3604 if(this.preventDefault){
3608 if(this.fireEvent('click', this, e) === false){
3612 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3613 if (typeof(this.parent().setActiveItem) !== 'undefined') {
3614 this.parent().setActiveItem(this);
3622 isActive: function () {
3625 setActive : function(state, fire)
3627 this.active = state;
3629 this.el.removeClass('active');
3630 } else if (!this.el.hasClass('active')) {
3631 this.el.addClass('active');
3634 this.fireEvent('changed', this, state);
3639 // this should not be here...
3650 * <span> icon </span>
3651 * <span> text </span>
3652 * <span>badge </span>
3656 * @class Roo.bootstrap.NavSidebarItem
3657 * @extends Roo.bootstrap.Component
3658 * Bootstrap Navbar.NavSidebarItem class
3660 * Create a new Navbar Button
3661 * @param {Object} config The config object
3663 Roo.bootstrap.NavSidebarItem = function(config){
3664 Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
3669 * The raw click event for the entire grid.
3670 * @param {Roo.EventObject} e
3675 * Fires when the active item active state changes
3676 * @param {Roo.bootstrap.Navbar.Item} this
3677 * @param {boolean} state the new state
3685 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem, {
3688 getAutoCreate : function(){
3693 href : this.href || '#',
3705 html : this.html || ''
3710 cfg.cls += ' active';
3714 if (this.glyphicon || this.icon) {
3715 var c = this.glyphicon ? ('glyphicon glyphicon-'+this.glyphicon) : this.icon;
3716 a.cn.push({ tag : 'i', cls : c }) ;
3721 if (this.badge !== '') {
3722 a.cn.push({ tag: 'span', cls : 'badge pull-right ' + (this.badgecls || ''), html: this.badge });
3726 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
3727 a.cls += 'dropdown-toggle treeview' ;
3751 * @class Roo.bootstrap.Row
3752 * @extends Roo.bootstrap.Component
3753 * Bootstrap Row class (contains columns...)
3757 * @param {Object} config The config object
3760 Roo.bootstrap.Row = function(config){
3761 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3764 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3766 getAutoCreate : function(){
3785 * @class Roo.bootstrap.Element
3786 * @extends Roo.bootstrap.Component
3787 * Bootstrap Element class
3788 * @cfg {String} html contents of the element
3789 * @cfg {String} tag tag of the element
3790 * @cfg {String} cls class of the element
3793 * Create a new Element
3794 * @param {Object} config The config object
3797 Roo.bootstrap.Element = function(config){
3798 Roo.bootstrap.Element.superclass.constructor.call(this, config);
3801 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
3808 getAutoCreate : function(){
3833 * @class Roo.bootstrap.Pagination
3834 * @extends Roo.bootstrap.Component
3835 * Bootstrap Pagination class
3836 * @cfg {String} size xs | sm | md | lg
3837 * @cfg {Boolean} inverse false | true
3840 * Create a new Pagination
3841 * @param {Object} config The config object
3844 Roo.bootstrap.Pagination = function(config){
3845 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3848 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
3854 getAutoCreate : function(){
3860 cfg.cls += ' inverse';
3866 cfg.cls += " " + this.cls;
3884 * @class Roo.bootstrap.PaginationItem
3885 * @extends Roo.bootstrap.Component
3886 * Bootstrap PaginationItem class
3887 * @cfg {String} html text
3888 * @cfg {String} href the link
3889 * @cfg {Boolean} preventDefault (true | false) default true
3890 * @cfg {Boolean} active (true | false) default false
3894 * Create a new PaginationItem
3895 * @param {Object} config The config object
3899 Roo.bootstrap.PaginationItem = function(config){
3900 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3905 * The raw click event for the entire grid.
3906 * @param {Roo.EventObject} e
3912 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
3916 preventDefault: true,
3920 getAutoCreate : function(){
3926 href : this.href ? this.href : '#',
3927 html : this.html ? this.html : ''
3937 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3943 initEvents: function() {
3945 this.el.on('click', this.onClick, this);
3948 onClick : function(e)
3950 Roo.log('PaginationItem on click ');
3951 if(this.preventDefault){
3955 this.fireEvent('click', this, e);
3971 * @class Roo.bootstrap.Slider
3972 * @extends Roo.bootstrap.Component
3973 * Bootstrap Slider class
3976 * Create a new Slider
3977 * @param {Object} config The config object
3980 Roo.bootstrap.Slider = function(config){
3981 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3984 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
3986 getAutoCreate : function(){
3990 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3994 cls: 'ui-slider-handle ui-state-default ui-corner-all'
4012 * @class Roo.bootstrap.Table
4013 * @extends Roo.bootstrap.Component
4014 * Bootstrap Table class
4015 * @cfg {String} cls table class
4016 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
4017 * @cfg {String} bgcolor Specifies the background color for a table
4018 * @cfg {Number} border Specifies whether the table cells should have borders or not
4019 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
4020 * @cfg {Number} cellspacing Specifies the space between cells
4021 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
4022 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
4023 * @cfg {String} sortable Specifies that the table should be sortable
4024 * @cfg {String} summary Specifies a summary of the content of a table
4025 * @cfg {Number} width Specifies the width of a table
4027 * @cfg {boolean} striped Should the rows be alternative striped
4028 * @cfg {boolean} bordered Add borders to the table
4029 * @cfg {boolean} hover Add hover highlighting
4030 * @cfg {boolean} condensed Format condensed
4031 * @cfg {boolean} responsive Format condensed
4037 * Create a new Table
4038 * @param {Object} config The config object
4041 Roo.bootstrap.Table = function(config){
4042 Roo.bootstrap.Table.superclass.constructor.call(this, config);
4045 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
4046 this.sm = this.selModel;
4047 this.sm.xmodule = this.xmodule || false;
4049 if (this.cm && typeof(this.cm.config) == 'undefined') {
4050 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
4051 this.cm = this.colModel;
4052 this.cm.xmodule = this.xmodule || false;
4055 this.store= Roo.factory(this.store, Roo.data);
4056 this.ds = this.store;
4057 this.ds.xmodule = this.xmodule || false;
4062 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
4084 getAutoCreate : function(){
4085 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
4094 cfg.cls += ' table-striped';
4097 cfg.cls += ' table-hover';
4099 if (this.bordered) {
4100 cfg.cls += ' table-bordered';
4102 if (this.condensed) {
4103 cfg.cls += ' table-condensed';
4105 if (this.responsive) {
4106 cfg.cls += ' table-responsive';
4113 cfg.cls+= ' ' +this.cls;
4116 // this lot should be simplifed...
4119 cfg.align=this.align;
4122 cfg.bgcolor=this.bgcolor;
4125 cfg.border=this.border;
4127 if (this.cellpadding) {
4128 cfg.cellpadding=this.cellpadding;
4130 if (this.cellspacing) {
4131 cfg.cellspacing=this.cellspacing;
4134 cfg.frame=this.frame;
4137 cfg.rules=this.rules;
4139 if (this.sortable) {
4140 cfg.sortable=this.sortable;
4143 cfg.summary=this.summary;
4146 cfg.width=this.width;
4149 if(this.store || this.cm){
4150 cfg.cn.push(this.renderHeader());
4151 cfg.cn.push(this.renderBody());
4152 cfg.cn.push(this.renderFooter());
4154 cfg.cls+= ' TableGrid';
4160 // initTableGrid : function()
4169 // var cm = this.cm;
4171 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4174 // html: cm.getColumnHeader(i)
4178 // cfg.push(header);
4185 initEvents : function()
4187 if(!this.store || !this.cm){
4191 Roo.log('initEvents with ds!!!!');
4195 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4196 e.on('click', _this.sort, _this);
4198 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
4199 // this.maskEl.enableDisplayMode("block");
4200 // this.maskEl.show();
4202 this.store.on('load', this.onLoad, this);
4203 this.store.on('beforeload', this.onBeforeLoad, this);
4211 sort : function(e,el)
4213 var col = Roo.get(el)
4215 if(!col.hasClass('sortable')){
4219 var sort = col.attr('sort');
4222 if(col.hasClass('glyphicon-arrow-up')){
4226 this.store.sortInfo = {field : sort, direction : dir};
4231 renderHeader : function()
4240 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4242 var config = cm.config[i];
4246 html: cm.getColumnHeader(i)
4249 if(typeof(config.dataIndex) != 'undefined'){
4250 c.sort = config.dataIndex;
4253 if(typeof(config.sortable) != 'undefined' && config.sortable){
4257 if(typeof(config.width) != 'undefined'){
4258 c.style = 'width:' + config.width + 'px';
4267 renderBody : function()
4277 renderFooter : function()
4289 Roo.log('ds onload');
4294 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
4295 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
4297 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
4298 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
4301 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
4302 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
4306 var tbody = this.el.select('tbody', true).first();
4310 if(this.store.getCount() > 0){
4311 this.store.data.each(function(d){
4317 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
4318 var renderer = cm.getRenderer(i);
4319 var config = cm.config[i];
4323 if(typeof(renderer) !== 'undefined'){
4324 value = renderer(d.data[cm.getDataIndex(i)], false, d);
4327 if(typeof(value) === 'object'){
4337 html: (typeof(value) === 'object') ? '' : value
4340 if(typeof(config.width) != 'undefined'){
4341 td.style = 'width:' + config.width + 'px';
4348 tbody.createChild(row);
4356 Roo.each(renders, function(r){
4357 _this.renderColumn(r);
4361 // if(this.loadMask){
4362 // this.maskEl.hide();
4366 onBeforeLoad : function()
4368 Roo.log('ds onBeforeLoad');
4372 // if(this.loadMask){
4373 // this.maskEl.show();
4379 this.el.select('tbody', true).first().dom.innerHTML = '';
4382 getSelectionModel : function(){
4384 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
4386 return this.selModel;
4389 renderColumn : function(r)
4392 r.cfg.render(Roo.get(r.id));
4395 Roo.each(r.cfg.cn, function(c){
4400 _this.renderColumn(child);
4417 * @class Roo.bootstrap.TableCell
4418 * @extends Roo.bootstrap.Component
4419 * Bootstrap TableCell class
4420 * @cfg {String} html cell contain text
4421 * @cfg {String} cls cell class
4422 * @cfg {String} tag cell tag (td|th) default td
4423 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
4424 * @cfg {String} align Aligns the content in a cell
4425 * @cfg {String} axis Categorizes cells
4426 * @cfg {String} bgcolor Specifies the background color of a cell
4427 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4428 * @cfg {Number} colspan Specifies the number of columns a cell should span
4429 * @cfg {String} headers Specifies one or more header cells a cell is related to
4430 * @cfg {Number} height Sets the height of a cell
4431 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
4432 * @cfg {Number} rowspan Sets the number of rows a cell should span
4433 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
4434 * @cfg {String} valign Vertical aligns the content in a cell
4435 * @cfg {Number} width Specifies the width of a cell
4438 * Create a new TableCell
4439 * @param {Object} config The config object
4442 Roo.bootstrap.TableCell = function(config){
4443 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
4446 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
4466 getAutoCreate : function(){
4467 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
4487 cfg.align=this.align
4493 cfg.bgcolor=this.bgcolor
4496 cfg.charoff=this.charoff
4499 cfg.colspan=this.colspan
4502 cfg.headers=this.headers
4505 cfg.height=this.height
4508 cfg.nowrap=this.nowrap
4511 cfg.rowspan=this.rowspan
4514 cfg.scope=this.scope
4517 cfg.valign=this.valign
4520 cfg.width=this.width
4539 * @class Roo.bootstrap.TableRow
4540 * @extends Roo.bootstrap.Component
4541 * Bootstrap TableRow class
4542 * @cfg {String} cls row class
4543 * @cfg {String} align Aligns the content in a table row
4544 * @cfg {String} bgcolor Specifies a background color for a table row
4545 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4546 * @cfg {String} valign Vertical aligns the content in a table row
4549 * Create a new TableRow
4550 * @param {Object} config The config object
4553 Roo.bootstrap.TableRow = function(config){
4554 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4557 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
4565 getAutoCreate : function(){
4566 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4576 cfg.align = this.align;
4579 cfg.bgcolor = this.bgcolor;
4582 cfg.charoff = this.charoff;
4585 cfg.valign = this.valign;
4603 * @class Roo.bootstrap.TableBody
4604 * @extends Roo.bootstrap.Component
4605 * Bootstrap TableBody class
4606 * @cfg {String} cls element class
4607 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4608 * @cfg {String} align Aligns the content inside the element
4609 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4610 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4613 * Create a new TableBody
4614 * @param {Object} config The config object
4617 Roo.bootstrap.TableBody = function(config){
4618 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4621 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
4629 getAutoCreate : function(){
4630 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4644 cfg.align = this.align;
4647 cfg.charoff = this.charoff;
4650 cfg.valign = this.valign;
4657 // initEvents : function()
4664 // this.store = Roo.factory(this.store, Roo.data);
4665 // this.store.on('load', this.onLoad, this);
4667 // this.store.load();
4671 // onLoad: function ()
4673 // this.fireEvent('load', this);
4683 * Ext JS Library 1.1.1
4684 * Copyright(c) 2006-2007, Ext JS, LLC.
4686 * Originally Released Under LGPL - original licence link has changed is not relivant.
4689 * <script type="text/javascript">
4692 // as we use this in bootstrap.
4693 Roo.namespace('Roo.form');
4695 * @class Roo.form.Action
4696 * Internal Class used to handle form actions
4698 * @param {Roo.form.BasicForm} el The form element or its id
4699 * @param {Object} config Configuration options
4704 // define the action interface
4705 Roo.form.Action = function(form, options){
4707 this.options = options || {};
4710 * Client Validation Failed
4713 Roo.form.Action.CLIENT_INVALID = 'client';
4715 * Server Validation Failed
4718 Roo.form.Action.SERVER_INVALID = 'server';
4720 * Connect to Server Failed
4723 Roo.form.Action.CONNECT_FAILURE = 'connect';
4725 * Reading Data from Server Failed
4728 Roo.form.Action.LOAD_FAILURE = 'load';
4730 Roo.form.Action.prototype = {
4732 failureType : undefined,
4733 response : undefined,
4737 run : function(options){
4742 success : function(response){
4747 handleResponse : function(response){
4751 // default connection failure
4752 failure : function(response){
4754 this.response = response;
4755 this.failureType = Roo.form.Action.CONNECT_FAILURE;
4756 this.form.afterAction(this, false);
4759 processResponse : function(response){
4760 this.response = response;
4761 if(!response.responseText){
4764 this.result = this.handleResponse(response);
4768 // utility functions used internally
4769 getUrl : function(appendParams){
4770 var url = this.options.url || this.form.url || this.form.el.dom.action;
4772 var p = this.getParams();
4774 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4780 getMethod : function(){
4781 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4784 getParams : function(){
4785 var bp = this.form.baseParams;
4786 var p = this.options.params;
4788 if(typeof p == "object"){
4789 p = Roo.urlEncode(Roo.applyIf(p, bp));
4790 }else if(typeof p == 'string' && bp){
4791 p += '&' + Roo.urlEncode(bp);
4794 p = Roo.urlEncode(bp);
4799 createCallback : function(){
4801 success: this.success,
4802 failure: this.failure,
4804 timeout: (this.form.timeout*1000),
4805 upload: this.form.fileUpload ? this.success : undefined
4810 Roo.form.Action.Submit = function(form, options){
4811 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4814 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4817 haveProgress : false,
4818 uploadComplete : false,
4820 // uploadProgress indicator.
4821 uploadProgress : function()
4823 if (!this.form.progressUrl) {
4827 if (!this.haveProgress) {
4828 Roo.MessageBox.progress("Uploading", "Uploading");
4830 if (this.uploadComplete) {
4831 Roo.MessageBox.hide();
4835 this.haveProgress = true;
4837 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4839 var c = new Roo.data.Connection();
4841 url : this.form.progressUrl,
4846 success : function(req){
4847 //console.log(data);
4851 rdata = Roo.decode(req.responseText)
4853 Roo.log("Invalid data from server..");
4857 if (!rdata || !rdata.success) {
4859 Roo.MessageBox.alert(Roo.encode(rdata));
4862 var data = rdata.data;
4864 if (this.uploadComplete) {
4865 Roo.MessageBox.hide();
4870 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4871 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4874 this.uploadProgress.defer(2000,this);
4877 failure: function(data) {
4878 Roo.log('progress url failed ');
4889 // run get Values on the form, so it syncs any secondary forms.
4890 this.form.getValues();
4892 var o = this.options;
4893 var method = this.getMethod();
4894 var isPost = method == 'POST';
4895 if(o.clientValidation === false || this.form.isValid()){
4897 if (this.form.progressUrl) {
4898 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4899 (new Date() * 1) + '' + Math.random());
4904 Roo.Ajax.request(Roo.apply(this.createCallback(), {
4905 form:this.form.el.dom,
4906 url:this.getUrl(!isPost),
4908 params:isPost ? this.getParams() : null,
4909 isUpload: this.form.fileUpload
4912 this.uploadProgress();
4914 }else if (o.clientValidation !== false){ // client validation failed
4915 this.failureType = Roo.form.Action.CLIENT_INVALID;
4916 this.form.afterAction(this, false);
4920 success : function(response)
4922 this.uploadComplete= true;
4923 if (this.haveProgress) {
4924 Roo.MessageBox.hide();
4928 var result = this.processResponse(response);
4929 if(result === true || result.success){
4930 this.form.afterAction(this, true);
4934 this.form.markInvalid(result.errors);
4935 this.failureType = Roo.form.Action.SERVER_INVALID;
4937 this.form.afterAction(this, false);
4939 failure : function(response)
4941 this.uploadComplete= true;
4942 if (this.haveProgress) {
4943 Roo.MessageBox.hide();
4946 this.response = response;
4947 this.failureType = Roo.form.Action.CONNECT_FAILURE;
4948 this.form.afterAction(this, false);
4951 handleResponse : function(response){
4952 if(this.form.errorReader){
4953 var rs = this.form.errorReader.read(response);
4956 for(var i = 0, len = rs.records.length; i < len; i++) {
4957 var r = rs.records[i];
4961 if(errors.length < 1){
4965 success : rs.success,
4971 ret = Roo.decode(response.responseText);
4975 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4985 Roo.form.Action.Load = function(form, options){
4986 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4987 this.reader = this.form.reader;
4990 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4995 Roo.Ajax.request(Roo.apply(
4996 this.createCallback(), {
4997 method:this.getMethod(),
4998 url:this.getUrl(false),
4999 params:this.getParams()
5003 success : function(response){
5005 var result = this.processResponse(response);
5006 if(result === true || !result.success || !result.data){
5007 this.failureType = Roo.form.Action.LOAD_FAILURE;
5008 this.form.afterAction(this, false);
5011 this.form.clearInvalid();
5012 this.form.setValues(result.data);
5013 this.form.afterAction(this, true);
5016 handleResponse : function(response){
5017 if(this.form.reader){
5018 var rs = this.form.reader.read(response);
5019 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
5021 success : rs.success,
5025 return Roo.decode(response.responseText);
5029 Roo.form.Action.ACTION_TYPES = {
5030 'load' : Roo.form.Action.Load,
5031 'submit' : Roo.form.Action.Submit
5040 * @class Roo.bootstrap.Form
5041 * @extends Roo.bootstrap.Component
5042 * Bootstrap Form class
5043 * @cfg {String} method GET | POST (default POST)
5044 * @cfg {String} labelAlign top | left (default top)
5045 * @cfg {String} align left | right - for navbars
5050 * @param {Object} config The config object
5054 Roo.bootstrap.Form = function(config){
5055 Roo.bootstrap.Form.superclass.constructor.call(this, config);
5058 * @event clientvalidation
5059 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
5060 * @param {Form} this
5061 * @param {Boolean} valid true if the form has passed client-side validation
5063 clientvalidation: true,
5065 * @event beforeaction
5066 * Fires before any action is performed. Return false to cancel the action.
5067 * @param {Form} this
5068 * @param {Action} action The action to be performed
5072 * @event actionfailed
5073 * Fires when an action fails.
5074 * @param {Form} this
5075 * @param {Action} action The action that failed
5077 actionfailed : true,
5079 * @event actioncomplete
5080 * Fires when an action is completed.
5081 * @param {Form} this
5082 * @param {Action} action The action that completed
5084 actioncomplete : true
5089 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
5092 * @cfg {String} method
5093 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
5098 * The URL to use for form actions if one isn't supplied in the action options.
5101 * @cfg {Boolean} fileUpload
5102 * Set to true if this form is a file upload.
5106 * @cfg {Object} baseParams
5107 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
5111 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
5115 * @cfg {Sting} align (left|right) for navbar forms
5120 activeAction : null,
5123 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5124 * element by passing it or its id or mask the form itself by passing in true.
5127 waitMsgTarget : false,
5132 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
5133 * element by passing it or its id or mask the form itself by passing in true.
5137 getAutoCreate : function(){
5141 method : this.method || 'POST',
5142 id : this.id || Roo.id(),
5145 if (this.parent().xtype.match(/^Nav/)) {
5146 cfg.cls = 'navbar-form navbar-' + this.align;
5150 if (this.labelAlign == 'left' ) {
5151 cfg.cls += ' form-horizontal';
5157 initEvents : function()
5159 this.el.on('submit', this.onSubmit, this);
5164 onSubmit : function(e){
5169 * Returns true if client-side validation on the form is successful.
5172 isValid : function(){
5173 var items = this.getItems();
5175 items.each(function(f){
5184 * Returns true if any fields in this form have changed since their original load.
5187 isDirty : function(){
5189 var items = this.getItems();
5190 items.each(function(f){
5200 * Performs a predefined action (submit or load) or custom actions you define on this form.
5201 * @param {String} actionName The name of the action type
5202 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
5203 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
5204 * accept other config options):
5206 Property Type Description
5207 ---------------- --------------- ----------------------------------------------------------------------------------
5208 url String The url for the action (defaults to the form's url)
5209 method String The form method to use (defaults to the form's method, or POST if not defined)
5210 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
5211 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
5212 validate the form on the client (defaults to false)
5214 * @return {BasicForm} this
5216 doAction : function(action, options){
5217 if(typeof action == 'string'){
5218 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
5220 if(this.fireEvent('beforeaction', this, action) !== false){
5221 this.beforeAction(action);
5222 action.run.defer(100, action);
5228 beforeAction : function(action){
5229 var o = action.options;
5231 // not really supported yet.. ??
5233 //if(this.waitMsgTarget === true){
5234 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
5235 //}else if(this.waitMsgTarget){
5236 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
5237 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
5239 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
5245 afterAction : function(action, success){
5246 this.activeAction = null;
5247 var o = action.options;
5249 //if(this.waitMsgTarget === true){
5251 //}else if(this.waitMsgTarget){
5252 // this.waitMsgTarget.unmask();
5254 // Roo.MessageBox.updateProgress(1);
5255 // Roo.MessageBox.hide();
5262 Roo.callback(o.success, o.scope, [this, action]);
5263 this.fireEvent('actioncomplete', this, action);
5267 // failure condition..
5268 // we have a scenario where updates need confirming.
5269 // eg. if a locking scenario exists..
5270 // we look for { errors : { needs_confirm : true }} in the response.
5272 (typeof(action.result) != 'undefined') &&
5273 (typeof(action.result.errors) != 'undefined') &&
5274 (typeof(action.result.errors.needs_confirm) != 'undefined')
5277 Roo.log("not supported yet");
5280 Roo.MessageBox.confirm(
5281 "Change requires confirmation",
5282 action.result.errorMsg,
5287 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
5297 Roo.callback(o.failure, o.scope, [this, action]);
5298 // show an error message if no failed handler is set..
5299 if (!this.hasListener('actionfailed')) {
5300 Roo.log("need to add dialog support");
5302 Roo.MessageBox.alert("Error",
5303 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
5304 action.result.errorMsg :
5305 "Saving Failed, please check your entries or try again"
5310 this.fireEvent('actionfailed', this, action);
5315 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
5316 * @param {String} id The value to search for
5319 findField : function(id){
5320 var items = this.getItems();
5321 var field = items.get(id);
5323 items.each(function(f){
5324 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
5331 return field || null;
5334 * Mark fields in this form invalid in bulk.
5335 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
5336 * @return {BasicForm} this
5338 markInvalid : function(errors){
5339 if(errors instanceof Array){
5340 for(var i = 0, len = errors.length; i < len; i++){
5341 var fieldError = errors[i];
5342 var f = this.findField(fieldError.id);
5344 f.markInvalid(fieldError.msg);
5350 if(typeof errors[id] != 'function' && (field = this.findField(id))){
5351 field.markInvalid(errors[id]);
5355 //Roo.each(this.childForms || [], function (f) {
5356 // f.markInvalid(errors);
5363 * Set values for fields in this form in bulk.
5364 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
5365 * @return {BasicForm} this
5367 setValues : function(values){
5368 if(values instanceof Array){ // array of objects
5369 for(var i = 0, len = values.length; i < len; i++){
5371 var f = this.findField(v.id);
5373 f.setValue(v.value);
5374 if(this.trackResetOnLoad){
5375 f.originalValue = f.getValue();
5379 }else{ // object hash
5382 if(typeof values[id] != 'function' && (field = this.findField(id))){
5384 if (field.setFromData &&
5386 field.displayField &&
5387 // combos' with local stores can
5388 // be queried via setValue()
5389 // to set their value..
5390 (field.store && !field.store.isLocal)
5394 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
5395 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
5396 field.setFromData(sd);
5399 field.setValue(values[id]);
5403 if(this.trackResetOnLoad){
5404 field.originalValue = field.getValue();
5410 //Roo.each(this.childForms || [], function (f) {
5411 // f.setValues(values);
5418 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
5419 * they are returned as an array.
5420 * @param {Boolean} asString
5423 getValues : function(asString){
5424 //if (this.childForms) {
5425 // copy values from the child forms
5426 // Roo.each(this.childForms, function (f) {
5427 // this.setValues(f.getValues());
5433 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
5434 if(asString === true){
5437 return Roo.urlDecode(fs);
5441 * Returns the fields in this form as an object with key/value pairs.
5442 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
5445 getFieldValues : function(with_hidden)
5447 var items = this.getItems();
5449 items.each(function(f){
5453 var v = f.getValue();
5454 if (f.inputType =='radio') {
5455 if (typeof(ret[f.getName()]) == 'undefined') {
5456 ret[f.getName()] = ''; // empty..
5459 if (!f.el.dom.checked) {
5467 // not sure if this supported any more..
5468 if ((typeof(v) == 'object') && f.getRawValue) {
5469 v = f.getRawValue() ; // dates..
5471 // combo boxes where name != hiddenName...
5472 if (f.name != f.getName()) {
5473 ret[f.name] = f.getRawValue();
5475 ret[f.getName()] = v;
5482 * Clears all invalid messages in this form.
5483 * @return {BasicForm} this
5485 clearInvalid : function(){
5486 var items = this.getItems();
5488 items.each(function(f){
5499 * @return {BasicForm} this
5502 var items = this.getItems();
5503 items.each(function(f){
5507 Roo.each(this.childForms || [], function (f) {
5514 getItems : function()
5516 var r=new Roo.util.MixedCollection(false, function(o){
5517 return o.id || (o.id = Roo.id());
5519 var iter = function(el) {
5526 Roo.each(el.items,function(e) {
5545 * Ext JS Library 1.1.1
5546 * Copyright(c) 2006-2007, Ext JS, LLC.
5548 * Originally Released Under LGPL - original licence link has changed is not relivant.
5551 * <script type="text/javascript">
5554 * @class Roo.form.VTypes
5555 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5558 Roo.form.VTypes = function(){
5559 // closure these in so they are only created once.
5560 var alpha = /^[a-zA-Z_]+$/;
5561 var alphanum = /^[a-zA-Z0-9_]+$/;
5562 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5563 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5565 // All these messages and functions are configurable
5568 * The function used to validate email addresses
5569 * @param {String} value The email address
5571 'email' : function(v){
5572 return email.test(v);
5575 * The error text to display when the email validation function returns false
5578 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5580 * The keystroke filter mask to be applied on email input
5583 'emailMask' : /[a-z0-9_\.\-@]/i,
5586 * The function used to validate URLs
5587 * @param {String} value The URL
5589 'url' : function(v){
5593 * The error text to display when the url validation function returns false
5596 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5599 * The function used to validate alpha values
5600 * @param {String} value The value
5602 'alpha' : function(v){
5603 return alpha.test(v);
5606 * The error text to display when the alpha validation function returns false
5609 'alphaText' : 'This field should only contain letters and _',
5611 * The keystroke filter mask to be applied on alpha input
5614 'alphaMask' : /[a-z_]/i,
5617 * The function used to validate alphanumeric values
5618 * @param {String} value The value
5620 'alphanum' : function(v){
5621 return alphanum.test(v);
5624 * The error text to display when the alphanumeric validation function returns false
5627 'alphanumText' : 'This field should only contain letters, numbers and _',
5629 * The keystroke filter mask to be applied on alphanumeric input
5632 'alphanumMask' : /[a-z0-9_]/i
5642 * @class Roo.bootstrap.Input
5643 * @extends Roo.bootstrap.Component
5644 * Bootstrap Input class
5645 * @cfg {Boolean} disabled is it disabled
5646 * @cfg {String} fieldLabel - the label associated
5647 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5648 * @cfg {String} name name of the input
5649 * @cfg {string} fieldLabel - the label associated
5650 * @cfg {string} inputType - input / file submit ...
5651 * @cfg {string} placeholder - placeholder to put in text.
5652 * @cfg {string} before - input group add on before
5653 * @cfg {string} after - input group add on after
5654 * @cfg {string} size - (lg|sm) or leave empty..
5655 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5656 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5657 * @cfg {Number} md colspan out of 12 for computer-sized screens
5658 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5659 * @cfg {string} value default value of the input
5660 * @cfg {Number} labelWidth set the width of label (0-12)
5661 * @cfg {String} labelAlign (top|left)
5662 * @cfg {Boolean} readOnly Specifies that the field should be read-only
5666 * Create a new Input
5667 * @param {Object} config The config object
5670 Roo.bootstrap.Input = function(config){
5671 Roo.bootstrap.Input.superclass.constructor.call(this, config);
5676 * Fires when this field receives input focus.
5677 * @param {Roo.form.Field} this
5682 * Fires when this field loses input focus.
5683 * @param {Roo.form.Field} this
5688 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
5689 * {@link Roo.EventObject#getKey} to determine which key was pressed.
5690 * @param {Roo.form.Field} this
5691 * @param {Roo.EventObject} e The event object
5696 * Fires just before the field blurs if the field value has changed.
5697 * @param {Roo.form.Field} this
5698 * @param {Mixed} newValue The new value
5699 * @param {Mixed} oldValue The original value
5704 * Fires after the field has been marked as invalid.
5705 * @param {Roo.form.Field} this
5706 * @param {String} msg The validation message
5711 * Fires after the field has been validated with no errors.
5712 * @param {Roo.form.Field} this
5717 * Fires after the key up
5718 * @param {Roo.form.Field} this
5719 * @param {Roo.EventObject} e The event Object
5725 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
5727 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5728 automatic validation (defaults to "keyup").
5730 validationEvent : "keyup",
5732 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5734 validateOnBlur : true,
5736 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5738 validationDelay : 250,
5740 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5742 focusClass : "x-form-focus", // not needed???
5746 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5748 invalidClass : "has-error",
5751 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5753 selectOnFocus : false,
5756 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5760 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5765 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5767 disableKeyFilter : false,
5770 * @cfg {Boolean} disabled True to disable the field (defaults to false).
5774 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5778 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5780 blankText : "This field is required",
5783 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5787 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5789 maxLength : Number.MAX_VALUE,
5791 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5793 minLengthText : "The minimum length for this field is {0}",
5795 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5797 maxLengthText : "The maximum length for this field is {0}",
5801 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5802 * If available, this function will be called only after the basic validators all return true, and will be passed the
5803 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5807 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5808 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5809 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
5813 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5836 parentLabelAlign : function()
5839 while (parent.parent()) {
5840 parent = parent.parent();
5841 if (typeof(parent.labelAlign) !='undefined') {
5842 return parent.labelAlign;
5849 getAutoCreate : function(){
5851 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5857 if(this.inputType != 'hidden'){
5858 cfg.cls = 'form-group' //input-group
5864 type : this.inputType,
5866 cls : 'form-control',
5867 placeholder : this.placeholder || ''
5871 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5872 input.maxLength = this.maxLength;
5875 if (this.disabled) {
5876 input.disabled=true;
5879 if (this.readOnly) {
5880 input.readonly=true;
5884 input.name = this.name;
5887 input.cls += ' input-' + this.size;
5890 ['xs','sm','md','lg'].map(function(size){
5891 if (settings[size]) {
5892 cfg.cls += ' col-' + size + '-' + settings[size];
5896 var inputblock = input;
5898 if (this.before || this.after) {
5901 cls : 'input-group',
5904 if (this.before && typeof(this.before) == 'string') {
5906 inputblock.cn.push({
5908 cls : 'roo-input-before input-group-addon',
5912 if (this.before && typeof(this.before) == 'object') {
5913 this.before = Roo.factory(this.before);
5914 Roo.log(this.before);
5915 inputblock.cn.push({
5917 cls : 'roo-input-before input-group-' +
5918 (this.before.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
5922 inputblock.cn.push(input);
5924 if (this.after && typeof(this.after) == 'string') {
5925 inputblock.cn.push({
5927 cls : 'roo-input-after input-group-addon',
5931 if (this.after && typeof(this.after) == 'object') {
5932 this.after = Roo.factory(this.after);
5933 Roo.log(this.after);
5934 inputblock.cn.push({
5936 cls : 'roo-input-after input-group-' +
5937 (this.after.xtype == 'Button' ? 'btn' : 'addon') //?? what about checkboxes - that looks like a bit of a hack thought?
5942 if (align ==='left' && this.fieldLabel.length) {
5943 Roo.log("left and has label");
5949 cls : 'control-label col-sm-' + this.labelWidth,
5950 html : this.fieldLabel
5954 cls : "col-sm-" + (12 - this.labelWidth),
5961 } else if ( this.fieldLabel.length) {
5967 //cls : 'input-group-addon',
5968 html : this.fieldLabel
5978 Roo.log(" no label && no align");
5987 Roo.log('input-parentType: ' + this.parentType);
5989 if (this.parentType === 'Navbar' && this.parent().bar) {
5990 cfg.cls += ' navbar-form';
5998 * return the real input element.
6000 inputEl: function ()
6002 return this.el.select('input.form-control',true).first();
6004 setDisabled : function(v)
6006 var i = this.inputEl().dom;
6008 i.removeAttribute('disabled');
6012 i.setAttribute('disabled','true');
6014 initEvents : function()
6017 this.inputEl().on("keydown" , this.fireKey, this);
6018 this.inputEl().on("focus", this.onFocus, this);
6019 this.inputEl().on("blur", this.onBlur, this);
6021 this.inputEl().relayEvent('keyup', this);
6023 // reference to original value for reset
6024 this.originalValue = this.getValue();
6025 //Roo.form.TextField.superclass.initEvents.call(this);
6026 if(this.validationEvent == 'keyup'){
6027 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
6028 this.inputEl().on('keyup', this.filterValidation, this);
6030 else if(this.validationEvent !== false){
6031 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
6034 if(this.selectOnFocus){
6035 this.on("focus", this.preFocus, this);
6038 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
6039 this.inputEl().on("keypress", this.filterKeys, this);
6042 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
6043 this.el.on("click", this.autoSize, this);
6046 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
6047 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
6050 if (typeof(this.before) == 'object') {
6051 this.before.render(this.el.select('.roo-input-before',true).first());
6053 if (typeof(this.after) == 'object') {
6054 this.after.render(this.el.select('.roo-input-after',true).first());
6059 filterValidation : function(e){
6060 if(!e.isNavKeyPress()){
6061 this.validationTask.delay(this.validationDelay);
6065 * Validates the field value
6066 * @return {Boolean} True if the value is valid, else false
6068 validate : function(){
6069 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
6070 if(this.disabled || this.validateValue(this.getRawValue())){
6071 this.clearInvalid();
6079 * Validates a value according to the field's validation rules and marks the field as invalid
6080 * if the validation fails
6081 * @param {Mixed} value The value to validate
6082 * @return {Boolean} True if the value is valid, else false
6084 validateValue : function(value){
6085 if(value.length < 1) { // if it's blank
6086 if(this.allowBlank){
6087 this.clearInvalid();
6090 this.markInvalid(this.blankText);
6094 if(value.length < this.minLength){
6095 this.markInvalid(String.format(this.minLengthText, this.minLength));
6098 if(value.length > this.maxLength){
6099 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
6103 var vt = Roo.form.VTypes;
6104 if(!vt[this.vtype](value, this)){
6105 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
6109 if(typeof this.validator == "function"){
6110 var msg = this.validator(value);
6112 this.markInvalid(msg);
6116 if(this.regex && !this.regex.test(value)){
6117 this.markInvalid(this.regexText);
6126 fireKey : function(e){
6127 //Roo.log('field ' + e.getKey());
6128 if(e.isNavKeyPress()){
6129 this.fireEvent("specialkey", this, e);
6132 focus : function (selectText){
6134 this.inputEl().focus();
6135 if(selectText === true){
6136 this.inputEl().dom.select();
6142 onFocus : function(){
6143 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6144 // this.el.addClass(this.focusClass);
6147 this.hasFocus = true;
6148 this.startValue = this.getValue();
6149 this.fireEvent("focus", this);
6153 beforeBlur : Roo.emptyFn,
6157 onBlur : function(){
6159 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
6160 //this.el.removeClass(this.focusClass);
6162 this.hasFocus = false;
6163 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
6166 var v = this.getValue();
6167 if(String(v) !== String(this.startValue)){
6168 this.fireEvent('change', this, v, this.startValue);
6170 this.fireEvent("blur", this);
6174 * Resets the current field value to the originally loaded value and clears any validation messages
6177 this.setValue(this.originalValue);
6178 this.clearInvalid();
6181 * Returns the name of the field
6182 * @return {Mixed} name The name field
6184 getName: function(){
6188 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
6189 * @return {Mixed} value The field value
6191 getValue : function(){
6192 return this.inputEl().getValue();
6195 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
6196 * @return {Mixed} value The field value
6198 getRawValue : function(){
6199 var v = this.inputEl().getValue();
6205 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
6206 * @param {Mixed} value The value to set
6208 setRawValue : function(v){
6209 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6212 selectText : function(start, end){
6213 var v = this.getRawValue();
6215 start = start === undefined ? 0 : start;
6216 end = end === undefined ? v.length : end;
6217 var d = this.inputEl().dom;
6218 if(d.setSelectionRange){
6219 d.setSelectionRange(start, end);
6220 }else if(d.createTextRange){
6221 var range = d.createTextRange();
6222 range.moveStart("character", start);
6223 range.moveEnd("character", v.length-end);
6230 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
6231 * @param {Mixed} value The value to set
6233 setValue : function(v){
6236 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
6242 processValue : function(value){
6243 if(this.stripCharsRe){
6244 var newValue = value.replace(this.stripCharsRe, '');
6245 if(newValue !== value){
6246 this.setRawValue(newValue);
6253 preFocus : function(){
6255 if(this.selectOnFocus){
6256 this.inputEl().dom.select();
6259 filterKeys : function(e){
6261 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
6264 var c = e.getCharCode(), cc = String.fromCharCode(c);
6265 if(Roo.isIE && (e.isSpecialKey() || !cc)){
6268 if(!this.maskRe.test(cc)){
6273 * Clear any invalid styles/messages for this field
6275 clearInvalid : function(){
6277 if(!this.el || this.preventMark){ // not rendered
6280 this.el.removeClass(this.invalidClass);
6282 switch(this.msgTarget){
6284 this.el.dom.qtip = '';
6287 this.el.dom.title = '';
6291 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
6296 this.errorIcon.dom.qtip = '';
6297 this.errorIcon.hide();
6298 this.un('resize', this.alignErrorIcon, this);
6302 var t = Roo.getDom(this.msgTarget);
6304 t.style.display = 'none';
6308 this.fireEvent('valid', this);
6311 * Mark this field as invalid
6312 * @param {String} msg The validation message
6314 markInvalid : function(msg){
6315 if(!this.el || this.preventMark){ // not rendered
6318 this.el.addClass(this.invalidClass);
6320 msg = msg || this.invalidText;
6321 switch(this.msgTarget){
6323 this.el.dom.qtip = msg;
6324 this.el.dom.qclass = 'x-form-invalid-tip';
6325 if(Roo.QuickTips){ // fix for floating editors interacting with DND
6326 Roo.QuickTips.enable();
6330 this.el.dom.title = msg;
6334 var elp = this.el.findParent('.x-form-element', 5, true);
6335 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
6336 this.errorEl.setWidth(elp.getWidth(true)-20);
6338 this.errorEl.update(msg);
6339 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
6342 if(!this.errorIcon){
6343 var elp = this.el.findParent('.x-form-element', 5, true);
6344 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
6346 this.alignErrorIcon();
6347 this.errorIcon.dom.qtip = msg;
6348 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
6349 this.errorIcon.show();
6350 this.on('resize', this.alignErrorIcon, this);
6353 var t = Roo.getDom(this.msgTarget);
6355 t.style.display = this.msgDisplay;
6359 this.fireEvent('invalid', this, msg);
6362 SafariOnKeyDown : function(event)
6364 // this is a workaround for a password hang bug on chrome/ webkit.
6366 var isSelectAll = false;
6368 if(this.inputEl().dom.selectionEnd > 0){
6369 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
6371 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
6372 event.preventDefault();
6377 if(isSelectAll){ // backspace and delete key
6379 event.preventDefault();
6380 // this is very hacky as keydown always get's upper case.
6382 var cc = String.fromCharCode(event.getCharCode());
6383 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
6387 adjustWidth : function(tag, w){
6388 tag = tag.toLowerCase();
6389 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
6390 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
6394 if(tag == 'textarea'){
6397 }else if(Roo.isOpera){
6401 if(tag == 'textarea'){
6420 * @class Roo.bootstrap.TextArea
6421 * @extends Roo.bootstrap.Input
6422 * Bootstrap TextArea class
6423 * @cfg {Number} cols Specifies the visible width of a text area
6424 * @cfg {Number} rows Specifies the visible number of lines in a text area
6425 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
6426 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
6427 * @cfg {string} html text
6430 * Create a new TextArea
6431 * @param {Object} config The config object
6434 Roo.bootstrap.TextArea = function(config){
6435 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
6439 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
6449 getAutoCreate : function(){
6451 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
6462 value : this.value || '',
6463 html: this.html || '',
6464 cls : 'form-control',
6465 placeholder : this.placeholder || ''
6469 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
6470 input.maxLength = this.maxLength;
6474 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
6478 input.cols = this.cols;
6481 if (this.readOnly) {
6482 input.readonly = true;
6486 input.name = this.name;
6490 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
6494 ['xs','sm','md','lg'].map(function(size){
6495 if (settings[size]) {
6496 cfg.cls += ' col-' + size + '-' + settings[size];
6500 var inputblock = input;
6502 if (this.before || this.after) {
6505 cls : 'input-group',
6509 inputblock.cn.push({
6511 cls : 'input-group-addon',
6515 inputblock.cn.push(input);
6517 inputblock.cn.push({
6519 cls : 'input-group-addon',
6526 if (align ==='left' && this.fieldLabel.length) {
6527 Roo.log("left and has label");
6533 cls : 'control-label col-sm-' + this.labelWidth,
6534 html : this.fieldLabel
6538 cls : "col-sm-" + (12 - this.labelWidth),
6545 } else if ( this.fieldLabel.length) {
6551 //cls : 'input-group-addon',
6552 html : this.fieldLabel
6562 Roo.log(" no label && no align");
6572 if (this.disabled) {
6573 input.disabled=true;
6580 * return the real textarea element.
6582 inputEl: function ()
6584 return this.el.select('textarea.form-control',true).first();
6592 * trigger field - base class for combo..
6597 * @class Roo.bootstrap.TriggerField
6598 * @extends Roo.bootstrap.Input
6599 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6600 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6601 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6602 * for which you can provide a custom implementation. For example:
6604 var trigger = new Roo.bootstrap.TriggerField();
6605 trigger.onTriggerClick = myTriggerFn;
6606 trigger.applyTo('my-field');
6609 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6610 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6611 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
6612 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6614 * Create a new TriggerField.
6615 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6616 * to the base TextField)
6618 Roo.bootstrap.TriggerField = function(config){
6619 this.mimicing = false;
6620 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6623 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
6625 * @cfg {String} triggerClass A CSS class to apply to the trigger
6628 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6632 /** @cfg {Boolean} grow @hide */
6633 /** @cfg {Number} growMin @hide */
6634 /** @cfg {Number} growMax @hide */
6640 autoSize: Roo.emptyFn,
6647 actionMode : 'wrap',
6651 getAutoCreate : function(){
6653 var parent = this.parent();
6655 var align = this.labelAlign || this.parentLabelAlign();
6660 cls: 'form-group' //input-group
6667 type : this.inputType,
6668 cls : 'form-control',
6669 autocomplete: 'off',
6670 placeholder : this.placeholder || ''
6674 input.name = this.name;
6677 input.cls += ' input-' + this.size;
6680 if (this.disabled) {
6681 input.disabled=true;
6684 var inputblock = input;
6686 if (this.before || this.after) {
6689 cls : 'input-group',
6693 inputblock.cn.push({
6695 cls : 'input-group-addon',
6699 inputblock.cn.push(input);
6701 inputblock.cn.push({
6703 cls : 'input-group-addon',
6716 cls: 'form-hidden-field'
6724 Roo.log('multiple');
6732 cls: 'form-hidden-field'
6736 cls: 'select2-choices',
6740 cls: 'select2-search-field',
6753 cls: 'select2-container input-group',
6758 cls: 'typeahead typeahead-long dropdown-menu',
6759 style: 'display:none'
6767 cls : 'input-group-addon btn dropdown-toggle',
6775 cls: 'combobox-clear',
6789 combobox.cls += ' select2-container-multi';
6792 if (align ==='left' && this.fieldLabel.length) {
6794 Roo.log("left and has label");
6800 cls : 'control-label col-sm-' + this.labelWidth,
6801 html : this.fieldLabel
6805 cls : "col-sm-" + (12 - this.labelWidth),
6812 } else if ( this.fieldLabel.length) {
6818 //cls : 'input-group-addon',
6819 html : this.fieldLabel
6829 Roo.log(" no label && no align");
6836 ['xs','sm','md','lg'].map(function(size){
6837 if (settings[size]) {
6838 cfg.cls += ' col-' + size + '-' + settings[size];
6849 onResize : function(w, h){
6850 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6851 // if(typeof w == 'number'){
6852 // var x = w - this.trigger.getWidth();
6853 // this.inputEl().setWidth(this.adjustWidth('input', x));
6854 // this.trigger.setStyle('left', x+'px');
6859 adjustSize : Roo.BoxComponent.prototype.adjustSize,
6862 getResizeEl : function(){
6863 return this.inputEl();
6867 getPositionEl : function(){
6868 return this.inputEl();
6872 alignErrorIcon : function(){
6873 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6877 initEvents : function(){
6879 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6880 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6882 this.trigger = this.el.select('span.dropdown-toggle',true).first();
6883 if(this.hideTrigger){
6884 this.trigger.setDisplayed(false);
6886 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6890 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6893 //this.trigger.addClassOnOver('x-form-trigger-over');
6894 //this.trigger.addClassOnClick('x-form-trigger-click');
6897 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6902 initTrigger : function(){
6907 onDestroy : function(){
6909 this.trigger.removeAllListeners();
6910 // this.trigger.remove();
6913 // this.wrap.remove();
6915 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6919 onFocus : function(){
6920 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6923 this.wrap.addClass('x-trigger-wrap-focus');
6924 this.mimicing = true;
6925 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6926 if(this.monitorTab){
6927 this.el.on("keydown", this.checkTab, this);
6934 checkTab : function(e){
6935 if(e.getKey() == e.TAB){
6941 onBlur : function(){
6946 mimicBlur : function(e, t){
6948 if(!this.wrap.contains(t) && this.validateBlur()){
6955 triggerBlur : function(){
6956 this.mimicing = false;
6957 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6958 if(this.monitorTab){
6959 this.el.un("keydown", this.checkTab, this);
6961 //this.wrap.removeClass('x-trigger-wrap-focus');
6962 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6966 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6967 validateBlur : function(e, t){
6972 onDisable : function(){
6973 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6975 // this.wrap.addClass('x-item-disabled');
6980 onEnable : function(){
6981 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6983 // this.el.removeClass('x-item-disabled');
6988 onShow : function(){
6989 var ae = this.getActionEl();
6992 ae.dom.style.display = '';
6993 ae.dom.style.visibility = 'visible';
6999 onHide : function(){
7000 var ae = this.getActionEl();
7001 ae.dom.style.display = 'none';
7005 * The function that should handle the trigger's click event. This method does nothing by default until overridden
7006 * by an implementing function.
7008 * @param {EventObject} e
7010 onTriggerClick : Roo.emptyFn
7014 * Ext JS Library 1.1.1
7015 * Copyright(c) 2006-2007, Ext JS, LLC.
7017 * Originally Released Under LGPL - original licence link has changed is not relivant.
7020 * <script type="text/javascript">
7025 * @class Roo.data.SortTypes
7027 * Defines the default sorting (casting?) comparison functions used when sorting data.
7029 Roo.data.SortTypes = {
7031 * Default sort that does nothing
7032 * @param {Mixed} s The value being converted
7033 * @return {Mixed} The comparison value
7040 * The regular expression used to strip tags
7044 stripTagsRE : /<\/?[^>]+>/gi,
7047 * Strips all HTML tags to sort on text only
7048 * @param {Mixed} s The value being converted
7049 * @return {String} The comparison value
7051 asText : function(s){
7052 return String(s).replace(this.stripTagsRE, "");
7056 * Strips all HTML tags to sort on text only - Case insensitive
7057 * @param {Mixed} s The value being converted
7058 * @return {String} The comparison value
7060 asUCText : function(s){
7061 return String(s).toUpperCase().replace(this.stripTagsRE, "");
7065 * Case insensitive string
7066 * @param {Mixed} s The value being converted
7067 * @return {String} The comparison value
7069 asUCString : function(s) {
7070 return String(s).toUpperCase();
7075 * @param {Mixed} s The value being converted
7076 * @return {Number} The comparison value
7078 asDate : function(s) {
7082 if(s instanceof Date){
7085 return Date.parse(String(s));
7090 * @param {Mixed} s The value being converted
7091 * @return {Float} The comparison value
7093 asFloat : function(s) {
7094 var val = parseFloat(String(s).replace(/,/g, ""));
7095 if(isNaN(val)) val = 0;
7101 * @param {Mixed} s The value being converted
7102 * @return {Number} The comparison value
7104 asInt : function(s) {
7105 var val = parseInt(String(s).replace(/,/g, ""));
7106 if(isNaN(val)) val = 0;
7111 * Ext JS Library 1.1.1
7112 * Copyright(c) 2006-2007, Ext JS, LLC.
7114 * Originally Released Under LGPL - original licence link has changed is not relivant.
7117 * <script type="text/javascript">
7121 * @class Roo.data.Record
7122 * Instances of this class encapsulate both record <em>definition</em> information, and record
7123 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
7124 * to access Records cached in an {@link Roo.data.Store} object.<br>
7126 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
7127 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
7130 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
7132 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
7133 * {@link #create}. The parameters are the same.
7134 * @param {Array} data An associative Array of data values keyed by the field name.
7135 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
7136 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
7137 * not specified an integer id is generated.
7139 Roo.data.Record = function(data, id){
7140 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
7145 * Generate a constructor for a specific record layout.
7146 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
7147 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
7148 * Each field definition object may contain the following properties: <ul>
7149 * <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,
7150 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
7151 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
7152 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
7153 * is being used, then this is a string containing the javascript expression to reference the data relative to
7154 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
7155 * to the data item relative to the record element. If the mapping expression is the same as the field name,
7156 * this may be omitted.</p></li>
7157 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
7158 * <ul><li>auto (Default, implies no conversion)</li>
7163 * <li>date</li></ul></p></li>
7164 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
7165 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
7166 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
7167 * by the Reader into an object that will be stored in the Record. It is passed the
7168 * following parameters:<ul>
7169 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
7171 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
7173 * <br>usage:<br><pre><code>
7174 var TopicRecord = Roo.data.Record.create(
7175 {name: 'title', mapping: 'topic_title'},
7176 {name: 'author', mapping: 'username'},
7177 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
7178 {name: 'lastPost', mapping: 'post_time', type: 'date'},
7179 {name: 'lastPoster', mapping: 'user2'},
7180 {name: 'excerpt', mapping: 'post_text'}
7183 var myNewRecord = new TopicRecord({
7184 title: 'Do my job please',
7187 lastPost: new Date(),
7188 lastPoster: 'Animal',
7189 excerpt: 'No way dude!'
7191 myStore.add(myNewRecord);
7196 Roo.data.Record.create = function(o){
7198 f.superclass.constructor.apply(this, arguments);
7200 Roo.extend(f, Roo.data.Record);
7201 var p = f.prototype;
7202 p.fields = new Roo.util.MixedCollection(false, function(field){
7205 for(var i = 0, len = o.length; i < len; i++){
7206 p.fields.add(new Roo.data.Field(o[i]));
7208 f.getField = function(name){
7209 return p.fields.get(name);
7214 Roo.data.Record.AUTO_ID = 1000;
7215 Roo.data.Record.EDIT = 'edit';
7216 Roo.data.Record.REJECT = 'reject';
7217 Roo.data.Record.COMMIT = 'commit';
7219 Roo.data.Record.prototype = {
7221 * Readonly flag - true if this record has been modified.
7230 join : function(store){
7235 * Set the named field to the specified value.
7236 * @param {String} name The name of the field to set.
7237 * @param {Object} value The value to set the field to.
7239 set : function(name, value){
7240 if(this.data[name] == value){
7247 if(typeof this.modified[name] == 'undefined'){
7248 this.modified[name] = this.data[name];
7250 this.data[name] = value;
7251 if(!this.editing && this.store){
7252 this.store.afterEdit(this);
7257 * Get the value of the named field.
7258 * @param {String} name The name of the field to get the value of.
7259 * @return {Object} The value of the field.
7261 get : function(name){
7262 return this.data[name];
7266 beginEdit : function(){
7267 this.editing = true;
7272 cancelEdit : function(){
7273 this.editing = false;
7274 delete this.modified;
7278 endEdit : function(){
7279 this.editing = false;
7280 if(this.dirty && this.store){
7281 this.store.afterEdit(this);
7286 * Usually called by the {@link Roo.data.Store} which owns the Record.
7287 * Rejects all changes made to the Record since either creation, or the last commit operation.
7288 * Modified fields are reverted to their original values.
7290 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7291 * of reject operations.
7293 reject : function(){
7294 var m = this.modified;
7296 if(typeof m[n] != "function"){
7297 this.data[n] = m[n];
7301 delete this.modified;
7302 this.editing = false;
7304 this.store.afterReject(this);
7309 * Usually called by the {@link Roo.data.Store} which owns the Record.
7310 * Commits all changes made to the Record since either creation, or the last commit operation.
7312 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
7313 * of commit operations.
7315 commit : function(){
7317 delete this.modified;
7318 this.editing = false;
7320 this.store.afterCommit(this);
7325 hasError : function(){
7326 return this.error != null;
7330 clearError : function(){
7335 * Creates a copy of this record.
7336 * @param {String} id (optional) A new record id if you don't want to use this record's id
7339 copy : function(newId) {
7340 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
7344 * Ext JS Library 1.1.1
7345 * Copyright(c) 2006-2007, Ext JS, LLC.
7347 * Originally Released Under LGPL - original licence link has changed is not relivant.
7350 * <script type="text/javascript">
7356 * @class Roo.data.Store
7357 * @extends Roo.util.Observable
7358 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
7359 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
7361 * 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
7362 * has no knowledge of the format of the data returned by the Proxy.<br>
7364 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
7365 * instances from the data object. These records are cached and made available through accessor functions.
7367 * Creates a new Store.
7368 * @param {Object} config A config object containing the objects needed for the Store to access data,
7369 * and read the data into Records.
7371 Roo.data.Store = function(config){
7372 this.data = new Roo.util.MixedCollection(false);
7373 this.data.getKey = function(o){
7376 this.baseParams = {};
7383 "multisort" : "_multisort"
7386 if(config && config.data){
7387 this.inlineData = config.data;
7391 Roo.apply(this, config);
7393 if(this.reader){ // reader passed
7394 this.reader = Roo.factory(this.reader, Roo.data);
7395 this.reader.xmodule = this.xmodule || false;
7396 if(!this.recordType){
7397 this.recordType = this.reader.recordType;
7399 if(this.reader.onMetaChange){
7400 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
7404 if(this.recordType){
7405 this.fields = this.recordType.prototype.fields;
7411 * @event datachanged
7412 * Fires when the data cache has changed, and a widget which is using this Store
7413 * as a Record cache should refresh its view.
7414 * @param {Store} this
7419 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
7420 * @param {Store} this
7421 * @param {Object} meta The JSON metadata
7426 * Fires when Records have been added to the Store
7427 * @param {Store} this
7428 * @param {Roo.data.Record[]} records The array of Records added
7429 * @param {Number} index The index at which the record(s) were added
7434 * Fires when a Record has been removed from the Store
7435 * @param {Store} this
7436 * @param {Roo.data.Record} record The Record that was removed
7437 * @param {Number} index The index at which the record was removed
7442 * Fires when a Record has been updated
7443 * @param {Store} this
7444 * @param {Roo.data.Record} record The Record that was updated
7445 * @param {String} operation The update operation being performed. Value may be one of:
7447 Roo.data.Record.EDIT
7448 Roo.data.Record.REJECT
7449 Roo.data.Record.COMMIT
7455 * Fires when the data cache has been cleared.
7456 * @param {Store} this
7461 * Fires before a request is made for a new data object. If the beforeload handler returns false
7462 * the load action will be canceled.
7463 * @param {Store} this
7464 * @param {Object} options The loading options that were specified (see {@link #load} for details)
7468 * @event beforeloadadd
7469 * Fires after a new set of Records has been loaded.
7470 * @param {Store} this
7471 * @param {Roo.data.Record[]} records The Records that were loaded
7472 * @param {Object} options The loading options that were specified (see {@link #load} for details)
7474 beforeloadadd : true,
7477 * Fires after a new set of Records has been loaded, before they are added to the store.
7478 * @param {Store} this
7479 * @param {Roo.data.Record[]} records The Records that were loaded
7480 * @param {Object} options The loading options that were specified (see {@link #load} for details)
7481 * @params {Object} return from reader
7485 * @event loadexception
7486 * Fires if an exception occurs in the Proxy during loading.
7487 * Called with the signature of the Proxy's "loadexception" event.
7488 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
7491 * @param {Object} return from JsonData.reader() - success, totalRecords, records
7492 * @param {Object} load options
7493 * @param {Object} jsonData from your request (normally this contains the Exception)
7495 loadexception : true
7499 this.proxy = Roo.factory(this.proxy, Roo.data);
7500 this.proxy.xmodule = this.xmodule || false;
7501 this.relayEvents(this.proxy, ["loadexception"]);
7503 this.sortToggle = {};
7504 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
7506 Roo.data.Store.superclass.constructor.call(this);
7508 if(this.inlineData){
7509 this.loadData(this.inlineData);
7510 delete this.inlineData;
7514 Roo.extend(Roo.data.Store, Roo.util.Observable, {
7516 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
7517 * without a remote query - used by combo/forms at present.
7521 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
7524 * @cfg {Array} data Inline data to be loaded when the store is initialized.
7527 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
7528 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
7531 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
7532 * on any HTTP request
7535 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7538 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7542 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7543 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7548 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7549 * loaded or when a record is removed. (defaults to false).
7551 pruneModifiedRecords : false,
7557 * Add Records to the Store and fires the add event.
7558 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7560 add : function(records){
7561 records = [].concat(records);
7562 for(var i = 0, len = records.length; i < len; i++){
7563 records[i].join(this);
7565 var index = this.data.length;
7566 this.data.addAll(records);
7567 this.fireEvent("add", this, records, index);
7571 * Remove a Record from the Store and fires the remove event.
7572 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7574 remove : function(record){
7575 var index = this.data.indexOf(record);
7576 this.data.removeAt(index);
7577 if(this.pruneModifiedRecords){
7578 this.modified.remove(record);
7580 this.fireEvent("remove", this, record, index);
7584 * Remove all Records from the Store and fires the clear event.
7586 removeAll : function(){
7588 if(this.pruneModifiedRecords){
7591 this.fireEvent("clear", this);
7595 * Inserts Records to the Store at the given index and fires the add event.
7596 * @param {Number} index The start index at which to insert the passed Records.
7597 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7599 insert : function(index, records){
7600 records = [].concat(records);
7601 for(var i = 0, len = records.length; i < len; i++){
7602 this.data.insert(index, records[i]);
7603 records[i].join(this);
7605 this.fireEvent("add", this, records, index);
7609 * Get the index within the cache of the passed Record.
7610 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7611 * @return {Number} The index of the passed Record. Returns -1 if not found.
7613 indexOf : function(record){
7614 return this.data.indexOf(record);
7618 * Get the index within the cache of the Record with the passed id.
7619 * @param {String} id The id of the Record to find.
7620 * @return {Number} The index of the Record. Returns -1 if not found.
7622 indexOfId : function(id){
7623 return this.data.indexOfKey(id);
7627 * Get the Record with the specified id.
7628 * @param {String} id The id of the Record to find.
7629 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7631 getById : function(id){
7632 return this.data.key(id);
7636 * Get the Record at the specified index.
7637 * @param {Number} index The index of the Record to find.
7638 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7640 getAt : function(index){
7641 return this.data.itemAt(index);
7645 * Returns a range of Records between specified indices.
7646 * @param {Number} startIndex (optional) The starting index (defaults to 0)
7647 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7648 * @return {Roo.data.Record[]} An array of Records
7650 getRange : function(start, end){
7651 return this.data.getRange(start, end);
7655 storeOptions : function(o){
7656 o = Roo.apply({}, o);
7659 this.lastOptions = o;
7663 * Loads the Record cache from the configured Proxy using the configured Reader.
7665 * If using remote paging, then the first load call must specify the <em>start</em>
7666 * and <em>limit</em> properties in the options.params property to establish the initial
7667 * position within the dataset, and the number of Records to cache on each read from the Proxy.
7669 * <strong>It is important to note that for remote data sources, loading is asynchronous,
7670 * and this call will return before the new data has been loaded. Perform any post-processing
7671 * in a callback function, or in a "load" event handler.</strong>
7673 * @param {Object} options An object containing properties which control loading options:<ul>
7674 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7675 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7676 * passed the following arguments:<ul>
7677 * <li>r : Roo.data.Record[]</li>
7678 * <li>options: Options object from the load call</li>
7679 * <li>success: Boolean success indicator</li></ul></li>
7680 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7681 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7684 load : function(options){
7685 options = options || {};
7686 if(this.fireEvent("beforeload", this, options) !== false){
7687 this.storeOptions(options);
7688 var p = Roo.apply(options.params || {}, this.baseParams);
7689 // if meta was not loaded from remote source.. try requesting it.
7690 if (!this.reader.metaFromRemote) {
7693 if(this.sortInfo && this.remoteSort){
7694 var pn = this.paramNames;
7695 p[pn["sort"]] = this.sortInfo.field;
7696 p[pn["dir"]] = this.sortInfo.direction;
7698 if (this.multiSort) {
7699 var pn = this.paramNames;
7700 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7703 this.proxy.load(p, this.reader, this.loadRecords, this, options);
7708 * Reloads the Record cache from the configured Proxy using the configured Reader and
7709 * the options from the last load operation performed.
7710 * @param {Object} options (optional) An object containing properties which may override the options
7711 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7712 * the most recently used options are reused).
7714 reload : function(options){
7715 this.load(Roo.applyIf(options||{}, this.lastOptions));
7719 // Called as a callback by the Reader during a load operation.
7720 loadRecords : function(o, options, success){
7721 if(!o || success === false){
7722 if(success !== false){
7723 this.fireEvent("load", this, [], options, o);
7725 if(options.callback){
7726 options.callback.call(options.scope || this, [], options, false);
7730 // if data returned failure - throw an exception.
7731 if (o.success === false) {
7732 // show a message if no listener is registered.
7733 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7734 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7736 // loadmask wil be hooked into this..
7737 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7740 var r = o.records, t = o.totalRecords || r.length;
7742 this.fireEvent("beforeloadadd", this, r, options, o);
7744 if(!options || options.add !== true){
7745 if(this.pruneModifiedRecords){
7748 for(var i = 0, len = r.length; i < len; i++){
7752 this.data = this.snapshot;
7753 delete this.snapshot;
7756 this.data.addAll(r);
7757 this.totalLength = t;
7759 this.fireEvent("datachanged", this);
7761 this.totalLength = Math.max(t, this.data.length+r.length);
7764 this.fireEvent("load", this, r, options, o);
7765 if(options.callback){
7766 options.callback.call(options.scope || this, r, options, true);
7772 * Loads data from a passed data block. A Reader which understands the format of the data
7773 * must have been configured in the constructor.
7774 * @param {Object} data The data block from which to read the Records. The format of the data expected
7775 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7776 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7778 loadData : function(o, append){
7779 var r = this.reader.readRecords(o);
7780 this.loadRecords(r, {add: append}, true);
7784 * Gets the number of cached records.
7786 * <em>If using paging, this may not be the total size of the dataset. If the data object
7787 * used by the Reader contains the dataset size, then the getTotalCount() function returns
7788 * the data set size</em>
7790 getCount : function(){
7791 return this.data.length || 0;
7795 * Gets the total number of records in the dataset as returned by the server.
7797 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7798 * the dataset size</em>
7800 getTotalCount : function(){
7801 return this.totalLength || 0;
7805 * Returns the sort state of the Store as an object with two properties:
7807 field {String} The name of the field by which the Records are sorted
7808 direction {String} The sort order, "ASC" or "DESC"
7811 getSortState : function(){
7812 return this.sortInfo;
7816 applySort : function(){
7817 if(this.sortInfo && !this.remoteSort){
7818 var s = this.sortInfo, f = s.field;
7819 var st = this.fields.get(f).sortType;
7820 var fn = function(r1, r2){
7821 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7822 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7824 this.data.sort(s.direction, fn);
7825 if(this.snapshot && this.snapshot != this.data){
7826 this.snapshot.sort(s.direction, fn);
7832 * Sets the default sort column and order to be used by the next load operation.
7833 * @param {String} fieldName The name of the field to sort by.
7834 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7836 setDefaultSort : function(field, dir){
7837 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7842 * If remote sorting is used, the sort is performed on the server, and the cache is
7843 * reloaded. If local sorting is used, the cache is sorted internally.
7844 * @param {String} fieldName The name of the field to sort by.
7845 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7847 sort : function(fieldName, dir){
7848 var f = this.fields.get(fieldName);
7850 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7852 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7853 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7858 this.sortToggle[f.name] = dir;
7859 this.sortInfo = {field: f.name, direction: dir};
7860 if(!this.remoteSort){
7862 this.fireEvent("datachanged", this);
7864 this.load(this.lastOptions);
7869 * Calls the specified function for each of the Records in the cache.
7870 * @param {Function} fn The function to call. The Record is passed as the first parameter.
7871 * Returning <em>false</em> aborts and exits the iteration.
7872 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7874 each : function(fn, scope){
7875 this.data.each(fn, scope);
7879 * Gets all records modified since the last commit. Modified records are persisted across load operations
7880 * (e.g., during paging).
7881 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7883 getModifiedRecords : function(){
7884 return this.modified;
7888 createFilterFn : function(property, value, anyMatch){
7889 if(!value.exec){ // not a regex
7890 value = String(value);
7891 if(value.length == 0){
7894 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7897 return value.test(r.data[property]);
7902 * Sums the value of <i>property</i> for each record between start and end and returns the result.
7903 * @param {String} property A field on your records
7904 * @param {Number} start The record index to start at (defaults to 0)
7905 * @param {Number} end The last record index to include (defaults to length - 1)
7906 * @return {Number} The sum
7908 sum : function(property, start, end){
7909 var rs = this.data.items, v = 0;
7911 end = (end || end === 0) ? end : rs.length-1;
7913 for(var i = start; i <= end; i++){
7914 v += (rs[i].data[property] || 0);
7920 * Filter the records by a specified property.
7921 * @param {String} field A field on your records
7922 * @param {String/RegExp} value Either a string that the field
7923 * should start with or a RegExp to test against the field
7924 * @param {Boolean} anyMatch True to match any part not just the beginning
7926 filter : function(property, value, anyMatch){
7927 var fn = this.createFilterFn(property, value, anyMatch);
7928 return fn ? this.filterBy(fn) : this.clearFilter();
7932 * Filter by a function. The specified function will be called with each
7933 * record in this data source. If the function returns true the record is included,
7934 * otherwise it is filtered.
7935 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7936 * @param {Object} scope (optional) The scope of the function (defaults to this)
7938 filterBy : function(fn, scope){
7939 this.snapshot = this.snapshot || this.data;
7940 this.data = this.queryBy(fn, scope||this);
7941 this.fireEvent("datachanged", this);
7945 * Query the records by a specified property.
7946 * @param {String} field A field on your records
7947 * @param {String/RegExp} value Either a string that the field
7948 * should start with or a RegExp to test against the field
7949 * @param {Boolean} anyMatch True to match any part not just the beginning
7950 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7952 query : function(property, value, anyMatch){
7953 var fn = this.createFilterFn(property, value, anyMatch);
7954 return fn ? this.queryBy(fn) : this.data.clone();
7958 * Query by a function. The specified function will be called with each
7959 * record in this data source. If the function returns true the record is included
7961 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7962 * @param {Object} scope (optional) The scope of the function (defaults to this)
7963 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7965 queryBy : function(fn, scope){
7966 var data = this.snapshot || this.data;
7967 return data.filterBy(fn, scope||this);
7971 * Collects unique values for a particular dataIndex from this store.
7972 * @param {String} dataIndex The property to collect
7973 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7974 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7975 * @return {Array} An array of the unique values
7977 collect : function(dataIndex, allowNull, bypassFilter){
7978 var d = (bypassFilter === true && this.snapshot) ?
7979 this.snapshot.items : this.data.items;
7980 var v, sv, r = [], l = {};
7981 for(var i = 0, len = d.length; i < len; i++){
7982 v = d[i].data[dataIndex];
7984 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7993 * Revert to a view of the Record cache with no filtering applied.
7994 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7996 clearFilter : function(suppressEvent){
7997 if(this.snapshot && this.snapshot != this.data){
7998 this.data = this.snapshot;
7999 delete this.snapshot;
8000 if(suppressEvent !== true){
8001 this.fireEvent("datachanged", this);
8007 afterEdit : function(record){
8008 if(this.modified.indexOf(record) == -1){
8009 this.modified.push(record);
8011 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
8015 afterReject : function(record){
8016 this.modified.remove(record);
8017 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
8021 afterCommit : function(record){
8022 this.modified.remove(record);
8023 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
8027 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
8028 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
8030 commitChanges : function(){
8031 var m = this.modified.slice(0);
8033 for(var i = 0, len = m.length; i < len; i++){
8039 * Cancel outstanding changes on all changed records.
8041 rejectChanges : function(){
8042 var m = this.modified.slice(0);
8044 for(var i = 0, len = m.length; i < len; i++){
8049 onMetaChange : function(meta, rtype, o){
8050 this.recordType = rtype;
8051 this.fields = rtype.prototype.fields;
8052 delete this.snapshot;
8053 this.sortInfo = meta.sortInfo || this.sortInfo;
8055 this.fireEvent('metachange', this, this.reader.meta);
8058 moveIndex : function(data, type)
8060 var index = this.indexOf(data);
8062 var newIndex = index + type;
8066 this.insert(newIndex, data);
8071 * Ext JS Library 1.1.1
8072 * Copyright(c) 2006-2007, Ext JS, LLC.
8074 * Originally Released Under LGPL - original licence link has changed is not relivant.
8077 * <script type="text/javascript">
8081 * @class Roo.data.SimpleStore
8082 * @extends Roo.data.Store
8083 * Small helper class to make creating Stores from Array data easier.
8084 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
8085 * @cfg {Array} fields An array of field definition objects, or field name strings.
8086 * @cfg {Array} data The multi-dimensional array of data
8088 * @param {Object} config
8090 Roo.data.SimpleStore = function(config){
8091 Roo.data.SimpleStore.superclass.constructor.call(this, {
8093 reader: new Roo.data.ArrayReader({
8096 Roo.data.Record.create(config.fields)
8098 proxy : new Roo.data.MemoryProxy(config.data)
8102 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
8104 * Ext JS Library 1.1.1
8105 * Copyright(c) 2006-2007, Ext JS, LLC.
8107 * Originally Released Under LGPL - original licence link has changed is not relivant.
8110 * <script type="text/javascript">
8115 * @extends Roo.data.Store
8116 * @class Roo.data.JsonStore
8117 * Small helper class to make creating Stores for JSON data easier. <br/>
8119 var store = new Roo.data.JsonStore({
8120 url: 'get-images.php',
8122 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
8125 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
8126 * JsonReader and HttpProxy (unless inline data is provided).</b>
8127 * @cfg {Array} fields An array of field definition objects, or field name strings.
8129 * @param {Object} config
8131 Roo.data.JsonStore = function(c){
8132 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
8133 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
8134 reader: new Roo.data.JsonReader(c, c.fields)
8137 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
8139 * Ext JS Library 1.1.1
8140 * Copyright(c) 2006-2007, Ext JS, LLC.
8142 * Originally Released Under LGPL - original licence link has changed is not relivant.
8145 * <script type="text/javascript">
8149 Roo.data.Field = function(config){
8150 if(typeof config == "string"){
8151 config = {name: config};
8153 Roo.apply(this, config);
8159 var st = Roo.data.SortTypes;
8160 // named sortTypes are supported, here we look them up
8161 if(typeof this.sortType == "string"){
8162 this.sortType = st[this.sortType];
8165 // set default sortType for strings and dates
8169 this.sortType = st.asUCString;
8172 this.sortType = st.asDate;
8175 this.sortType = st.none;
8180 var stripRe = /[\$,%]/g;
8182 // prebuilt conversion function for this field, instead of
8183 // switching every time we're reading a value
8185 var cv, dateFormat = this.dateFormat;
8190 cv = function(v){ return v; };
8193 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
8197 return v !== undefined && v !== null && v !== '' ?
8198 parseInt(String(v).replace(stripRe, ""), 10) : '';
8203 return v !== undefined && v !== null && v !== '' ?
8204 parseFloat(String(v).replace(stripRe, ""), 10) : '';
8209 cv = function(v){ return v === true || v === "true" || v == 1; };
8216 if(v instanceof Date){
8220 if(dateFormat == "timestamp"){
8221 return new Date(v*1000);
8223 return Date.parseDate(v, dateFormat);
8225 var parsed = Date.parse(v);
8226 return parsed ? new Date(parsed) : null;
8235 Roo.data.Field.prototype = {
8243 * Ext JS Library 1.1.1
8244 * Copyright(c) 2006-2007, Ext JS, LLC.
8246 * Originally Released Under LGPL - original licence link has changed is not relivant.
8249 * <script type="text/javascript">
8252 // Base class for reading structured data from a data source. This class is intended to be
8253 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
8256 * @class Roo.data.DataReader
8257 * Base class for reading structured data from a data source. This class is intended to be
8258 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
8261 Roo.data.DataReader = function(meta, recordType){
8265 this.recordType = recordType instanceof Array ?
8266 Roo.data.Record.create(recordType) : recordType;
8269 Roo.data.DataReader.prototype = {
8271 * Create an empty record
8272 * @param {Object} data (optional) - overlay some values
8273 * @return {Roo.data.Record} record created.
8275 newRow : function(d) {
8277 this.recordType.prototype.fields.each(function(c) {
8279 case 'int' : da[c.name] = 0; break;
8280 case 'date' : da[c.name] = new Date(); break;
8281 case 'float' : da[c.name] = 0.0; break;
8282 case 'boolean' : da[c.name] = false; break;
8283 default : da[c.name] = ""; break;
8287 return new this.recordType(Roo.apply(da, d));
8292 * Ext JS Library 1.1.1
8293 * Copyright(c) 2006-2007, Ext JS, LLC.
8295 * Originally Released Under LGPL - original licence link has changed is not relivant.
8298 * <script type="text/javascript">
8302 * @class Roo.data.DataProxy
8303 * @extends Roo.data.Observable
8304 * This class is an abstract base class for implementations which provide retrieval of
8305 * unformatted data objects.<br>
8307 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
8308 * (of the appropriate type which knows how to parse the data object) to provide a block of
8309 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
8311 * Custom implementations must implement the load method as described in
8312 * {@link Roo.data.HttpProxy#load}.
8314 Roo.data.DataProxy = function(){
8318 * Fires before a network request is made to retrieve a data object.
8319 * @param {Object} This DataProxy object.
8320 * @param {Object} params The params parameter to the load function.
8325 * Fires before the load method's callback is called.
8326 * @param {Object} This DataProxy object.
8327 * @param {Object} o The data object.
8328 * @param {Object} arg The callback argument object passed to the load function.
8332 * @event loadexception
8333 * Fires if an Exception occurs during data retrieval.
8334 * @param {Object} This DataProxy object.
8335 * @param {Object} o The data object.
8336 * @param {Object} arg The callback argument object passed to the load function.
8337 * @param {Object} e The Exception.
8339 loadexception : true
8341 Roo.data.DataProxy.superclass.constructor.call(this);
8344 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
8347 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
8351 * Ext JS Library 1.1.1
8352 * Copyright(c) 2006-2007, Ext JS, LLC.
8354 * Originally Released Under LGPL - original licence link has changed is not relivant.
8357 * <script type="text/javascript">
8360 * @class Roo.data.MemoryProxy
8361 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
8362 * to the Reader when its load method is called.
8364 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
8366 Roo.data.MemoryProxy = function(data){
8370 Roo.data.MemoryProxy.superclass.constructor.call(this);
8374 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
8376 * Load data from the requested source (in this case an in-memory
8377 * data object passed to the constructor), read the data object into
8378 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8379 * process that block using the passed callback.
8380 * @param {Object} params This parameter is not used by the MemoryProxy class.
8381 * @param {Roo.data.DataReader} reader The Reader object which converts the data
8382 * object into a block of Roo.data.Records.
8383 * @param {Function} callback The function into which to pass the block of Roo.data.records.
8384 * The function must be passed <ul>
8385 * <li>The Record block object</li>
8386 * <li>The "arg" argument from the load function</li>
8387 * <li>A boolean success indicator</li>
8389 * @param {Object} scope The scope in which to call the callback
8390 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8392 load : function(params, reader, callback, scope, arg){
8393 params = params || {};
8396 result = reader.readRecords(this.data);
8398 this.fireEvent("loadexception", this, arg, null, e);
8399 callback.call(scope, null, arg, false);
8402 callback.call(scope, result, arg, true);
8406 update : function(params, records){
8411 * Ext JS Library 1.1.1
8412 * Copyright(c) 2006-2007, Ext JS, LLC.
8414 * Originally Released Under LGPL - original licence link has changed is not relivant.
8417 * <script type="text/javascript">
8420 * @class Roo.data.HttpProxy
8421 * @extends Roo.data.DataProxy
8422 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
8423 * configured to reference a certain URL.<br><br>
8425 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
8426 * from which the running page was served.<br><br>
8428 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
8430 * Be aware that to enable the browser to parse an XML document, the server must set
8431 * the Content-Type header in the HTTP response to "text/xml".
8433 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
8434 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
8435 * will be used to make the request.
8437 Roo.data.HttpProxy = function(conn){
8438 Roo.data.HttpProxy.superclass.constructor.call(this);
8439 // is conn a conn config or a real conn?
8441 this.useAjax = !conn || !conn.events;
8445 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
8446 // thse are take from connection...
8449 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
8452 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
8453 * extra parameters to each request made by this object. (defaults to undefined)
8456 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
8457 * to each request made by this object. (defaults to undefined)
8460 * @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)
8463 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
8466 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
8472 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
8476 * Return the {@link Roo.data.Connection} object being used by this Proxy.
8477 * @return {Connection} The Connection object. This object may be used to subscribe to events on
8478 * a finer-grained basis than the DataProxy events.
8480 getConnection : function(){
8481 return this.useAjax ? Roo.Ajax : this.conn;
8485 * Load data from the configured {@link Roo.data.Connection}, read the data object into
8486 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
8487 * process that block using the passed callback.
8488 * @param {Object} params An object containing properties which are to be used as HTTP parameters
8489 * for the request to the remote server.
8490 * @param {Roo.data.DataReader} reader The Reader object which converts the data
8491 * object into a block of Roo.data.Records.
8492 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8493 * The function must be passed <ul>
8494 * <li>The Record block object</li>
8495 * <li>The "arg" argument from the load function</li>
8496 * <li>A boolean success indicator</li>
8498 * @param {Object} scope The scope in which to call the callback
8499 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8501 load : function(params, reader, callback, scope, arg){
8502 if(this.fireEvent("beforeload", this, params) !== false){
8504 params : params || {},
8506 callback : callback,
8511 callback : this.loadResponse,
8515 Roo.applyIf(o, this.conn);
8516 if(this.activeRequest){
8517 Roo.Ajax.abort(this.activeRequest);
8519 this.activeRequest = Roo.Ajax.request(o);
8521 this.conn.request(o);
8524 callback.call(scope||this, null, arg, false);
8529 loadResponse : function(o, success, response){
8530 delete this.activeRequest;
8532 this.fireEvent("loadexception", this, o, response);
8533 o.request.callback.call(o.request.scope, null, o.request.arg, false);
8538 result = o.reader.read(response);
8540 this.fireEvent("loadexception", this, o, response, e);
8541 o.request.callback.call(o.request.scope, null, o.request.arg, false);
8545 this.fireEvent("load", this, o, o.request.arg);
8546 o.request.callback.call(o.request.scope, result, o.request.arg, true);
8550 update : function(dataSet){
8555 updateResponse : function(dataSet){
8560 * Ext JS Library 1.1.1
8561 * Copyright(c) 2006-2007, Ext JS, LLC.
8563 * Originally Released Under LGPL - original licence link has changed is not relivant.
8566 * <script type="text/javascript">
8570 * @class Roo.data.ScriptTagProxy
8571 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8572 * other than the originating domain of the running page.<br><br>
8574 * <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
8575 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8577 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8578 * source code that is used as the source inside a <script> tag.<br><br>
8580 * In order for the browser to process the returned data, the server must wrap the data object
8581 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8582 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8583 * depending on whether the callback name was passed:
8586 boolean scriptTag = false;
8587 String cb = request.getParameter("callback");
8590 response.setContentType("text/javascript");
8592 response.setContentType("application/x-json");
8594 Writer out = response.getWriter();
8596 out.write(cb + "(");
8598 out.print(dataBlock.toJsonString());
8605 * @param {Object} config A configuration object.
8607 Roo.data.ScriptTagProxy = function(config){
8608 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8609 Roo.apply(this, config);
8610 this.head = document.getElementsByTagName("head")[0];
8613 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8615 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8617 * @cfg {String} url The URL from which to request the data object.
8620 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8624 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8625 * the server the name of the callback function set up by the load call to process the returned data object.
8626 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8627 * javascript output which calls this named function passing the data object as its only parameter.
8629 callbackParam : "callback",
8631 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8632 * name to the request.
8637 * Load data from the configured URL, read the data object into
8638 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8639 * process that block using the passed callback.
8640 * @param {Object} params An object containing properties which are to be used as HTTP parameters
8641 * for the request to the remote server.
8642 * @param {Roo.data.DataReader} reader The Reader object which converts the data
8643 * object into a block of Roo.data.Records.
8644 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8645 * The function must be passed <ul>
8646 * <li>The Record block object</li>
8647 * <li>The "arg" argument from the load function</li>
8648 * <li>A boolean success indicator</li>
8650 * @param {Object} scope The scope in which to call the callback
8651 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8653 load : function(params, reader, callback, scope, arg){
8654 if(this.fireEvent("beforeload", this, params) !== false){
8656 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8659 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8661 url += "&_dc=" + (new Date().getTime());
8663 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8666 cb : "stcCallback"+transId,
8667 scriptId : "stcScript"+transId,
8671 callback : callback,
8677 window[trans.cb] = function(o){
8678 conn.handleResponse(o, trans);
8681 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8683 if(this.autoAbort !== false){
8687 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8689 var script = document.createElement("script");
8690 script.setAttribute("src", url);
8691 script.setAttribute("type", "text/javascript");
8692 script.setAttribute("id", trans.scriptId);
8693 this.head.appendChild(script);
8697 callback.call(scope||this, null, arg, false);
8702 isLoading : function(){
8703 return this.trans ? true : false;
8707 * Abort the current server request.
8710 if(this.isLoading()){
8711 this.destroyTrans(this.trans);
8716 destroyTrans : function(trans, isLoaded){
8717 this.head.removeChild(document.getElementById(trans.scriptId));
8718 clearTimeout(trans.timeoutId);
8720 window[trans.cb] = undefined;
8722 delete window[trans.cb];
8725 // if hasn't been loaded, wait for load to remove it to prevent script error
8726 window[trans.cb] = function(){
8727 window[trans.cb] = undefined;
8729 delete window[trans.cb];
8736 handleResponse : function(o, trans){
8738 this.destroyTrans(trans, true);
8741 result = trans.reader.readRecords(o);
8743 this.fireEvent("loadexception", this, o, trans.arg, e);
8744 trans.callback.call(trans.scope||window, null, trans.arg, false);
8747 this.fireEvent("load", this, o, trans.arg);
8748 trans.callback.call(trans.scope||window, result, trans.arg, true);
8752 handleFailure : function(trans){
8754 this.destroyTrans(trans, false);
8755 this.fireEvent("loadexception", this, null, trans.arg);
8756 trans.callback.call(trans.scope||window, null, trans.arg, false);
8760 * Ext JS Library 1.1.1
8761 * Copyright(c) 2006-2007, Ext JS, LLC.
8763 * Originally Released Under LGPL - original licence link has changed is not relivant.
8766 * <script type="text/javascript">
8770 * @class Roo.data.JsonReader
8771 * @extends Roo.data.DataReader
8772 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8773 * based on mappings in a provided Roo.data.Record constructor.
8775 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8776 * in the reply previously.
8781 var RecordDef = Roo.data.Record.create([
8782 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
8783 {name: 'occupation'} // This field will use "occupation" as the mapping.
8785 var myReader = new Roo.data.JsonReader({
8786 totalProperty: "results", // The property which contains the total dataset size (optional)
8787 root: "rows", // The property which contains an Array of row objects
8788 id: "id" // The property within each row object that provides an ID for the record (optional)
8792 * This would consume a JSON file like this:
8794 { 'results': 2, 'rows': [
8795 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8796 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8799 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8800 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8801 * paged from the remote server.
8802 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8803 * @cfg {String} root name of the property which contains the Array of row objects.
8804 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8806 * Create a new JsonReader
8807 * @param {Object} meta Metadata configuration options
8808 * @param {Object} recordType Either an Array of field definition objects,
8809 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8811 Roo.data.JsonReader = function(meta, recordType){
8814 // set some defaults:
8816 totalProperty: 'total',
8817 successProperty : 'success',
8822 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8824 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8827 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
8828 * Used by Store query builder to append _requestMeta to params.
8831 metaFromRemote : false,
8833 * This method is only used by a DataProxy which has retrieved data from a remote server.
8834 * @param {Object} response The XHR object which contains the JSON data in its responseText.
8835 * @return {Object} data A data block which is used by an Roo.data.Store object as
8836 * a cache of Roo.data.Records.
8838 read : function(response){
8839 var json = response.responseText;
8841 var o = /* eval:var:o */ eval("("+json+")");
8843 throw {message: "JsonReader.read: Json object not found"};
8849 this.metaFromRemote = true;
8850 this.meta = o.metaData;
8851 this.recordType = Roo.data.Record.create(o.metaData.fields);
8852 this.onMetaChange(this.meta, this.recordType, o);
8854 return this.readRecords(o);
8857 // private function a store will implement
8858 onMetaChange : function(meta, recordType, o){
8865 simpleAccess: function(obj, subsc) {
8872 getJsonAccessor: function(){
8874 return function(expr) {
8876 return(re.test(expr))
8877 ? new Function("obj", "return obj." + expr)
8887 * Create a data block containing Roo.data.Records from an XML document.
8888 * @param {Object} o An object which contains an Array of row objects in the property specified
8889 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8890 * which contains the total size of the dataset.
8891 * @return {Object} data A data block which is used by an Roo.data.Store object as
8892 * a cache of Roo.data.Records.
8894 readRecords : function(o){
8896 * After any data loads, the raw JSON data is available for further custom processing.
8900 var s = this.meta, Record = this.recordType,
8901 f = Record.prototype.fields, fi = f.items, fl = f.length;
8903 // Generate extraction functions for the totalProperty, the root, the id, and for each field
8905 if(s.totalProperty) {
8906 this.getTotal = this.getJsonAccessor(s.totalProperty);
8908 if(s.successProperty) {
8909 this.getSuccess = this.getJsonAccessor(s.successProperty);
8911 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8913 var g = this.getJsonAccessor(s.id);
8914 this.getId = function(rec) {
8916 return (r === undefined || r === "") ? null : r;
8919 this.getId = function(){return null;};
8922 for(var jj = 0; jj < fl; jj++){
8924 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8925 this.ef[jj] = this.getJsonAccessor(map);
8929 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8930 if(s.totalProperty){
8931 var vt = parseInt(this.getTotal(o), 10);
8936 if(s.successProperty){
8937 var vs = this.getSuccess(o);
8938 if(vs === false || vs === 'false'){
8943 for(var i = 0; i < c; i++){
8946 var id = this.getId(n);
8947 for(var j = 0; j < fl; j++){
8949 var v = this.ef[j](n);
8951 Roo.log('missing convert for ' + f.name);
8955 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8957 var record = new Record(values, id);
8959 records[i] = record;
8965 totalRecords : totalRecords
8970 * Ext JS Library 1.1.1
8971 * Copyright(c) 2006-2007, Ext JS, LLC.
8973 * Originally Released Under LGPL - original licence link has changed is not relivant.
8976 * <script type="text/javascript">
8980 * @class Roo.data.ArrayReader
8981 * @extends Roo.data.DataReader
8982 * Data reader class to create an Array of Roo.data.Record objects from an Array.
8983 * Each element of that Array represents a row of data fields. The
8984 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8985 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8989 var RecordDef = Roo.data.Record.create([
8990 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
8991 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
8993 var myReader = new Roo.data.ArrayReader({
8994 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
8998 * This would consume an Array like this:
9000 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
9002 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
9004 * Create a new JsonReader
9005 * @param {Object} meta Metadata configuration options.
9006 * @param {Object} recordType Either an Array of field definition objects
9007 * as specified to {@link Roo.data.Record#create},
9008 * or an {@link Roo.data.Record} object
9009 * created using {@link Roo.data.Record#create}.
9011 Roo.data.ArrayReader = function(meta, recordType){
9012 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
9015 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
9017 * Create a data block containing Roo.data.Records from an XML document.
9018 * @param {Object} o An Array of row objects which represents the dataset.
9019 * @return {Object} data A data block which is used by an Roo.data.Store object as
9020 * a cache of Roo.data.Records.
9022 readRecords : function(o){
9023 var sid = this.meta ? this.meta.id : null;
9024 var recordType = this.recordType, fields = recordType.prototype.fields;
9027 for(var i = 0; i < root.length; i++){
9030 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
9031 for(var j = 0, jlen = fields.length; j < jlen; j++){
9032 var f = fields.items[j];
9033 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
9034 var v = n[k] !== undefined ? n[k] : f.defaultValue;
9038 var record = new recordType(values, id);
9040 records[records.length] = record;
9044 totalRecords : records.length
9053 * @class Roo.bootstrap.ComboBox
9054 * @extends Roo.bootstrap.TriggerField
9055 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
9056 * @cfg {Boolean} append (true|false) default false
9058 * Create a new ComboBox.
9059 * @param {Object} config Configuration options
9061 Roo.bootstrap.ComboBox = function(config){
9062 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
9066 * Fires when the dropdown list is expanded
9067 * @param {Roo.bootstrap.ComboBox} combo This combo box
9072 * Fires when the dropdown list is collapsed
9073 * @param {Roo.bootstrap.ComboBox} combo This combo box
9077 * @event beforeselect
9078 * Fires before a list item is selected. Return false to cancel the selection.
9079 * @param {Roo.bootstrap.ComboBox} combo This combo box
9080 * @param {Roo.data.Record} record The data record returned from the underlying store
9081 * @param {Number} index The index of the selected item in the dropdown list
9083 'beforeselect' : true,
9086 * Fires when a list item is selected
9087 * @param {Roo.bootstrap.ComboBox} combo This combo box
9088 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
9089 * @param {Number} index The index of the selected item in the dropdown list
9093 * @event beforequery
9094 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
9095 * The event object passed has these properties:
9096 * @param {Roo.bootstrap.ComboBox} combo This combo box
9097 * @param {String} query The query
9098 * @param {Boolean} forceAll true to force "all" query
9099 * @param {Boolean} cancel true to cancel the query
9100 * @param {Object} e The query event object
9102 'beforequery': true,
9105 * Fires when the 'add' icon is pressed (add a listener to enable add button)
9106 * @param {Roo.bootstrap.ComboBox} combo This combo box
9111 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
9112 * @param {Roo.bootstrap.ComboBox} combo This combo box
9113 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
9118 * Fires when the remove value from the combobox array
9119 * @param {Roo.bootstrap.ComboBox} combo This combo box
9126 this.selectedIndex = -1;
9127 if(this.mode == 'local'){
9128 if(config.queryDelay === undefined){
9129 this.queryDelay = 10;
9131 if(config.minChars === undefined){
9137 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
9140 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
9141 * rendering into an Roo.Editor, defaults to false)
9144 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
9145 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
9148 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
9151 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
9152 * the dropdown list (defaults to undefined, with no header element)
9156 * @cfg {String/Roo.Template} tpl The template to use to render the output
9160 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
9162 listWidth: undefined,
9164 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
9165 * mode = 'remote' or 'text' if mode = 'local')
9167 displayField: undefined,
9169 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
9170 * mode = 'remote' or 'value' if mode = 'local').
9171 * Note: use of a valueField requires the user make a selection
9172 * in order for a value to be mapped.
9174 valueField: undefined,
9178 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
9179 * field's data value (defaults to the underlying DOM element's name)
9181 hiddenName: undefined,
9183 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
9187 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
9189 selectedClass: 'active',
9192 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
9196 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
9197 * anchor positions (defaults to 'tl-bl')
9199 listAlign: 'tl-bl?',
9201 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
9205 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
9206 * query specified by the allQuery config option (defaults to 'query')
9208 triggerAction: 'query',
9210 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
9211 * (defaults to 4, does not apply if editable = false)
9215 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
9216 * delay (typeAheadDelay) if it matches a known value (defaults to false)
9220 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
9221 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
9225 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
9226 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
9230 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
9231 * when editable = true (defaults to false)
9233 selectOnFocus:false,
9235 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
9237 queryParam: 'query',
9239 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
9240 * when mode = 'remote' (defaults to 'Loading...')
9242 loadingText: 'Loading...',
9244 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
9248 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
9252 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
9253 * traditional select (defaults to true)
9257 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
9261 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
9265 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
9266 * listWidth has a higher value)
9270 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
9271 * allow the user to set arbitrary text into the field (defaults to false)
9273 forceSelection:false,
9275 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
9276 * if typeAhead = true (defaults to 250)
9278 typeAheadDelay : 250,
9280 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
9281 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
9283 valueNotFoundText : undefined,
9285 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
9290 * @cfg {Boolean} disableClear Disable showing of clear button.
9292 disableClear : false,
9294 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
9296 alwaysQuery : false,
9299 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
9313 // element that contains real text value.. (when hidden is used..)
9316 initEvents: function(){
9319 throw "can not find store for combo";
9321 this.store = Roo.factory(this.store, Roo.data);
9325 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
9328 if(this.hiddenName){
9330 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
9332 this.hiddenField.dom.value =
9333 this.hiddenValue !== undefined ? this.hiddenValue :
9334 this.value !== undefined ? this.value : '';
9336 // prevent input submission
9337 this.el.dom.removeAttribute('name');
9338 this.hiddenField.dom.setAttribute('name', this.hiddenName);
9343 // this.el.dom.setAttribute('autocomplete', 'off');
9346 var cls = 'x-combo-list';
9347 this.list = this.el.select('ul.dropdown-menu',true).first();
9349 //this.list = new Roo.Layer({
9350 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
9353 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
9354 this.list.setWidth(lw);
9356 this.list.on('mouseover', this.onViewOver, this);
9357 this.list.on('mousemove', this.onViewMove, this);
9359 this.list.on('scroll', this.onViewScroll, this);
9362 this.list.swallowEvent('mousewheel');
9363 this.assetHeight = 0;
9366 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
9367 this.assetHeight += this.header.getHeight();
9370 this.innerList = this.list.createChild({cls:cls+'-inner'});
9371 this.innerList.on('mouseover', this.onViewOver, this);
9372 this.innerList.on('mousemove', this.onViewMove, this);
9373 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9375 if(this.allowBlank && !this.pageSize && !this.disableClear){
9376 this.footer = this.list.createChild({cls:cls+'-ft'});
9377 this.pageTb = new Roo.Toolbar(this.footer);
9381 this.footer = this.list.createChild({cls:cls+'-ft'});
9382 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
9383 {pageSize: this.pageSize});
9387 if (this.pageTb && this.allowBlank && !this.disableClear) {
9389 this.pageTb.add(new Roo.Toolbar.Fill(), {
9390 cls: 'x-btn-icon x-btn-clear',
9396 _this.onSelect(false, -1);
9401 this.assetHeight += this.footer.getHeight();
9406 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
9409 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
9410 singleSelect:true, store: this.store, selectedClass: this.selectedClass
9412 //this.view.wrapEl.setDisplayed(false);
9413 this.view.on('click', this.onViewClick, this);
9417 this.store.on('beforeload', this.onBeforeLoad, this);
9418 this.store.on('load', this.onLoad, this);
9419 this.store.on('loadexception', this.onLoadException, this);
9422 this.resizer = new Roo.Resizable(this.list, {
9423 pinned:true, handles:'se'
9425 this.resizer.on('resize', function(r, w, h){
9426 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
9428 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
9429 this.restrictHeight();
9431 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
9435 this.editable = true;
9436 this.setEditable(false);
9441 if (typeof(this.events.add.listeners) != 'undefined') {
9443 this.addicon = this.wrap.createChild(
9444 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
9446 this.addicon.on('click', function(e) {
9447 this.fireEvent('add', this);
9450 if (typeof(this.events.edit.listeners) != 'undefined') {
9452 this.editicon = this.wrap.createChild(
9453 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
9455 this.editicon.setStyle('margin-left', '40px');
9457 this.editicon.on('click', function(e) {
9459 // we fire even if inothing is selected..
9460 this.fireEvent('edit', this, this.lastData );
9466 this.keyNav = new Roo.KeyNav(this.inputEl(), {
9468 this.inKeyMode = true;
9472 "down" : function(e){
9473 if(!this.isExpanded()){
9474 this.onTriggerClick();
9476 this.inKeyMode = true;
9481 "enter" : function(e){
9486 "esc" : function(e){
9490 "tab" : function(e){
9493 if(this.fireEvent("specialkey", this, e)){
9494 this.onViewClick(false);
9502 doRelay : function(foo, bar, hname){
9503 if(hname == 'down' || this.scope.isExpanded()){
9504 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
9513 this.queryDelay = Math.max(this.queryDelay || 10,
9514 this.mode == 'local' ? 10 : 250);
9517 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
9520 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
9522 if(this.editable !== false){
9523 this.inputEl().on("keyup", this.onKeyUp, this);
9525 if(this.forceSelection){
9526 this.inputEl().on('blur', this.doForce, this);
9530 this.choices = this.el.select('ul.select2-choices', true).first();
9531 this.searchField = this.el.select('ul li.select2-search-field', true).first();
9535 onDestroy : function(){
9537 this.view.setStore(null);
9538 this.view.el.removeAllListeners();
9539 this.view.el.remove();
9540 this.view.purgeListeners();
9543 this.list.dom.innerHTML = '';
9546 this.store.un('beforeload', this.onBeforeLoad, this);
9547 this.store.un('load', this.onLoad, this);
9548 this.store.un('loadexception', this.onLoadException, this);
9550 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9554 fireKey : function(e){
9555 if(e.isNavKeyPress() && !this.list.isVisible()){
9556 this.fireEvent("specialkey", this, e);
9561 onResize: function(w, h){
9562 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9564 // if(typeof w != 'number'){
9565 // // we do not handle it!?!?
9568 // var tw = this.trigger.getWidth();
9569 // // tw += this.addicon ? this.addicon.getWidth() : 0;
9570 // // tw += this.editicon ? this.editicon.getWidth() : 0;
9572 // this.inputEl().setWidth( this.adjustWidth('input', x));
9574 // //this.trigger.setStyle('left', x+'px');
9576 // if(this.list && this.listWidth === undefined){
9577 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9578 // this.list.setWidth(lw);
9579 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9587 * Allow or prevent the user from directly editing the field text. If false is passed,
9588 * the user will only be able to select from the items defined in the dropdown list. This method
9589 * is the runtime equivalent of setting the 'editable' config option at config time.
9590 * @param {Boolean} value True to allow the user to directly edit the field text
9592 setEditable : function(value){
9593 if(value == this.editable){
9596 this.editable = value;
9598 this.inputEl().dom.setAttribute('readOnly', true);
9599 this.inputEl().on('mousedown', this.onTriggerClick, this);
9600 this.inputEl().addClass('x-combo-noedit');
9602 this.inputEl().dom.setAttribute('readOnly', false);
9603 this.inputEl().un('mousedown', this.onTriggerClick, this);
9604 this.inputEl().removeClass('x-combo-noedit');
9610 onBeforeLoad : function(combo,opts){
9615 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9617 this.restrictHeight();
9618 this.selectedIndex = -1;
9622 onLoad : function(){
9624 this.hasQuery = false;
9630 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9631 this.loading.hide();
9634 if(this.store.getCount() > 0){
9636 this.restrictHeight();
9637 if(this.lastQuery == this.allQuery){
9639 this.inputEl().dom.select();
9641 if(!this.selectByValue(this.value, true)){
9642 this.select(0, true);
9646 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9647 this.taTask.delay(this.typeAheadDelay);
9651 this.onEmptyResults();
9657 onLoadException : function()
9659 this.hasQuery = false;
9661 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9662 this.loading.hide();
9666 Roo.log(this.store.reader.jsonData);
9667 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9669 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9675 onTypeAhead : function(){
9676 if(this.store.getCount() > 0){
9677 var r = this.store.getAt(0);
9678 var newValue = r.data[this.displayField];
9679 var len = newValue.length;
9680 var selStart = this.getRawValue().length;
9682 if(selStart != len){
9683 this.setRawValue(newValue);
9684 this.selectText(selStart, newValue.length);
9690 onSelect : function(record, index){
9692 if(this.fireEvent('beforeselect', this, record, index) !== false){
9694 this.setFromData(index > -1 ? record.data : false);
9697 this.fireEvent('select', this, record, index);
9702 * Returns the currently selected field value or empty string if no value is set.
9703 * @return {String} value The selected value
9705 getValue : function(){
9708 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9711 if(this.valueField){
9712 return typeof this.value != 'undefined' ? this.value : '';
9714 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9719 * Clears any text/value currently set in the field
9721 clearValue : function(){
9722 if(this.hiddenField){
9723 this.hiddenField.dom.value = '';
9726 this.setRawValue('');
9727 this.lastSelectionText = '';
9732 * Sets the specified value into the field. If the value finds a match, the corresponding record text
9733 * will be displayed in the field. If the value does not match the data value of an existing item,
9734 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9735 * Otherwise the field will be blank (although the value will still be set).
9736 * @param {String} value The value to match
9738 setValue : function(v){
9745 if(this.valueField){
9746 var r = this.findRecord(this.valueField, v);
9748 text = r.data[this.displayField];
9749 }else if(this.valueNotFoundText !== undefined){
9750 text = this.valueNotFoundText;
9753 this.lastSelectionText = text;
9754 if(this.hiddenField){
9755 this.hiddenField.dom.value = v;
9757 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9761 * @property {Object} the last set data for the element
9766 * Sets the value of the field based on a object which is related to the record format for the store.
9767 * @param {Object} value the value to set as. or false on reset?
9769 setFromData : function(o){
9776 var dv = ''; // display value
9777 var vv = ''; // value value..
9779 if (this.displayField) {
9780 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9782 // this is an error condition!!!
9783 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
9786 if(this.valueField){
9787 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9790 if(this.hiddenField){
9791 this.hiddenField.dom.value = vv;
9793 this.lastSelectionText = dv;
9794 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9798 // no hidden field.. - we store the value in 'value', but still display
9799 // display field!!!!
9800 this.lastSelectionText = dv;
9801 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9808 // overridden so that last data is reset..
9809 this.setValue(this.originalValue);
9810 this.clearInvalid();
9811 this.lastData = false;
9813 this.view.clearSelections();
9817 findRecord : function(prop, value){
9819 if(this.store.getCount() > 0){
9820 this.store.each(function(r){
9821 if(r.data[prop] == value){
9833 // returns hidden if it's set..
9834 if (!this.rendered) {return ''};
9835 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
9839 onViewMove : function(e, t){
9840 this.inKeyMode = false;
9844 onViewOver : function(e, t){
9845 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9848 var item = this.view.findItemFromChild(t);
9850 var index = this.view.indexOf(item);
9851 this.select(index, false);
9856 onViewClick : function(doFocus)
9858 var index = this.view.getSelectedIndexes()[0];
9859 var r = this.store.getAt(index);
9861 this.onSelect(r, index);
9863 if(doFocus !== false && !this.blockFocus){
9864 this.inputEl().focus();
9869 restrictHeight : function(){
9870 //this.innerList.dom.style.height = '';
9871 //var inner = this.innerList.dom;
9872 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9873 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9874 //this.list.beginUpdate();
9875 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9876 this.list.alignTo(this.inputEl(), this.listAlign);
9877 //this.list.endUpdate();
9881 onEmptyResults : function(){
9886 * Returns true if the dropdown list is expanded, else false.
9888 isExpanded : function(){
9889 return this.list.isVisible();
9893 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9894 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9895 * @param {String} value The data value of the item to select
9896 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9897 * selected item if it is not currently in view (defaults to true)
9898 * @return {Boolean} True if the value matched an item in the list, else false
9900 selectByValue : function(v, scrollIntoView){
9901 if(v !== undefined && v !== null){
9902 var r = this.findRecord(this.valueField || this.displayField, v);
9904 this.select(this.store.indexOf(r), scrollIntoView);
9912 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9913 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9914 * @param {Number} index The zero-based index of the list item to select
9915 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9916 * selected item if it is not currently in view (defaults to true)
9918 select : function(index, scrollIntoView){
9919 this.selectedIndex = index;
9920 this.view.select(index);
9921 if(scrollIntoView !== false){
9922 var el = this.view.getNode(index);
9924 //this.innerList.scrollChildIntoView(el, false);
9931 selectNext : function(){
9932 var ct = this.store.getCount();
9934 if(this.selectedIndex == -1){
9936 }else if(this.selectedIndex < ct-1){
9937 this.select(this.selectedIndex+1);
9943 selectPrev : function(){
9944 var ct = this.store.getCount();
9946 if(this.selectedIndex == -1){
9948 }else if(this.selectedIndex != 0){
9949 this.select(this.selectedIndex-1);
9955 onKeyUp : function(e){
9956 if(this.editable !== false && !e.isSpecialKey()){
9957 this.lastKey = e.getKey();
9958 this.dqTask.delay(this.queryDelay);
9963 validateBlur : function(){
9964 return !this.list || !this.list.isVisible();
9968 initQuery : function(){
9969 this.doQuery(this.getRawValue());
9973 doForce : function(){
9974 if(this.inputEl().dom.value.length > 0){
9975 this.inputEl().dom.value =
9976 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9982 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
9983 * query allowing the query action to be canceled if needed.
9984 * @param {String} query The SQL query to execute
9985 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9986 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
9987 * saved in the current store (defaults to false)
9989 doQuery : function(q, forceAll){
9991 if(q === undefined || q === null){
10000 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
10005 forceAll = qe.forceAll;
10006 if(forceAll === true || (q.length >= this.minChars)){
10008 this.hasQuery = true;
10010 if(this.lastQuery != q || this.alwaysQuery){
10011 this.lastQuery = q;
10012 if(this.mode == 'local'){
10013 this.selectedIndex = -1;
10015 this.store.clearFilter();
10017 this.store.filter(this.displayField, q);
10021 this.store.baseParams[this.queryParam] = q;
10023 var options = {params : this.getParams(q)};
10026 options.add = true;
10027 options.params.start = this.page * this.pageSize;
10030 this.store.load(options);
10034 this.selectedIndex = -1;
10039 this.loadNext = false;
10043 getParams : function(q){
10045 //p[this.queryParam] = q;
10049 p.limit = this.pageSize;
10055 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
10057 collapse : function(){
10058 if(!this.isExpanded()){
10063 Roo.get(document).un('mousedown', this.collapseIf, this);
10064 Roo.get(document).un('mousewheel', this.collapseIf, this);
10065 if (!this.editable) {
10066 Roo.get(document).un('keydown', this.listKeyPress, this);
10068 this.fireEvent('collapse', this);
10072 collapseIf : function(e){
10073 var in_combo = e.within(this.el);
10074 var in_list = e.within(this.list);
10076 if (in_combo || in_list) {
10077 //e.stopPropagation();
10086 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
10088 expand : function(){
10090 if(this.isExpanded() || !this.hasFocus){
10094 this.list.alignTo(this.inputEl(), this.listAlign);
10096 Roo.get(document).on('mousedown', this.collapseIf, this);
10097 Roo.get(document).on('mousewheel', this.collapseIf, this);
10098 if (!this.editable) {
10099 Roo.get(document).on('keydown', this.listKeyPress, this);
10102 this.fireEvent('expand', this);
10106 // Implements the default empty TriggerField.onTriggerClick function
10107 onTriggerClick : function()
10109 Roo.log('trigger click');
10116 this.loadNext = false;
10118 if(this.isExpanded()){
10120 if (!this.blockFocus) {
10121 this.inputEl().focus();
10125 this.hasFocus = true;
10126 if(this.triggerAction == 'all') {
10127 this.doQuery(this.allQuery, true);
10129 this.doQuery(this.getRawValue());
10131 if (!this.blockFocus) {
10132 this.inputEl().focus();
10136 listKeyPress : function(e)
10138 //Roo.log('listkeypress');
10139 // scroll to first matching element based on key pres..
10140 if (e.isSpecialKey()) {
10143 var k = String.fromCharCode(e.getKey()).toUpperCase();
10146 var csel = this.view.getSelectedNodes();
10147 var cselitem = false;
10149 var ix = this.view.indexOf(csel[0]);
10150 cselitem = this.store.getAt(ix);
10151 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
10157 this.store.each(function(v) {
10159 // start at existing selection.
10160 if (cselitem.id == v.id) {
10166 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
10167 match = this.store.indexOf(v);
10173 if (match === false) {
10174 return true; // no more action?
10177 this.view.select(match);
10178 var sn = Roo.get(this.view.getSelectedNodes()[0])
10179 //sn.scrollIntoView(sn.dom.parentNode, false);
10182 onViewScroll : function(e, t){
10184 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
10188 this.hasQuery = true;
10190 this.loading = this.list.select('.loading', true).first();
10192 if(this.loading === null){
10193 this.list.createChild({
10195 cls: 'loading select2-more-results select2-active',
10196 html: 'Loading more results...'
10199 this.loading = this.list.select('.loading', true).first();
10201 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
10203 this.loading.hide();
10206 this.loading.show();
10211 this.loadNext = true;
10213 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
10218 addItem : function(o)
10220 var dv = ''; // display value
10222 if (this.displayField) {
10223 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
10225 // this is an error condition!!!
10226 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
10233 var choice = this.choices.createChild({
10235 cls: 'select2-search-choice',
10244 cls: 'select2-search-choice-close',
10249 }, this.searchField);
10251 var close = choice.select('a.select2-search-choice-close', true).first()
10253 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
10260 this.inputEl().dom.value = '';
10264 onRemoveItem : function(e, _self, o)
10266 Roo.log('remove item');
10267 var index = this.item.indexOf(o.data) * 1;
10270 Roo.log('not this item?!');
10274 this.item.splice(index, 1);
10279 this.fireEvent('remove', this, e);
10283 syncValue : function()
10285 if(!this.item.length){
10292 Roo.each(this.item, function(i){
10293 if(_this.valueField){
10294 value.push(i[_this.valueField]);
10301 this.value = value.join(',');
10303 if(this.hiddenField){
10304 this.hiddenField.dom.value = this.value;
10308 clearItem : function()
10310 if(!this.multiple){
10316 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
10326 * @cfg {Boolean} grow
10330 * @cfg {Number} growMin
10334 * @cfg {Number} growMax
10344 * Ext JS Library 1.1.1
10345 * Copyright(c) 2006-2007, Ext JS, LLC.
10347 * Originally Released Under LGPL - original licence link has changed is not relivant.
10350 * <script type="text/javascript">
10355 * @extends Roo.util.Observable
10356 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
10357 * This class also supports single and multi selection modes. <br>
10358 * Create a data model bound view:
10360 var store = new Roo.data.Store(...);
10362 var view = new Roo.View({
10364 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
10366 singleSelect: true,
10367 selectedClass: "ydataview-selected",
10371 // listen for node click?
10372 view.on("click", function(vw, index, node, e){
10373 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
10377 dataModel.load("foobar.xml");
10379 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
10381 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
10382 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
10384 * Note: old style constructor is still suported (container, template, config)
10387 * Create a new View
10388 * @param {Object} config The config object
10391 Roo.View = function(config, depreciated_tpl, depreciated_config){
10393 if (typeof(depreciated_tpl) == 'undefined') {
10394 // new way.. - universal constructor.
10395 Roo.apply(this, config);
10396 this.el = Roo.get(this.el);
10399 this.el = Roo.get(config);
10400 this.tpl = depreciated_tpl;
10401 Roo.apply(this, depreciated_config);
10403 this.wrapEl = this.el.wrap().wrap();
10404 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
10407 if(typeof(this.tpl) == "string"){
10408 this.tpl = new Roo.Template(this.tpl);
10410 // support xtype ctors..
10411 this.tpl = new Roo.factory(this.tpl, Roo);
10415 this.tpl.compile();
10423 * @event beforeclick
10424 * Fires before a click is processed. Returns false to cancel the default action.
10425 * @param {Roo.View} this
10426 * @param {Number} index The index of the target node
10427 * @param {HTMLElement} node The target node
10428 * @param {Roo.EventObject} e The raw event object
10430 "beforeclick" : true,
10433 * Fires when a template node is clicked.
10434 * @param {Roo.View} this
10435 * @param {Number} index The index of the target node
10436 * @param {HTMLElement} node The target node
10437 * @param {Roo.EventObject} e The raw event object
10442 * Fires when a template node is double clicked.
10443 * @param {Roo.View} this
10444 * @param {Number} index The index of the target node
10445 * @param {HTMLElement} node The target node
10446 * @param {Roo.EventObject} e The raw event object
10450 * @event contextmenu
10451 * Fires when a template node is right clicked.
10452 * @param {Roo.View} this
10453 * @param {Number} index The index of the target node
10454 * @param {HTMLElement} node The target node
10455 * @param {Roo.EventObject} e The raw event object
10457 "contextmenu" : true,
10459 * @event selectionchange
10460 * Fires when the selected nodes change.
10461 * @param {Roo.View} this
10462 * @param {Array} selections Array of the selected nodes
10464 "selectionchange" : true,
10467 * @event beforeselect
10468 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
10469 * @param {Roo.View} this
10470 * @param {HTMLElement} node The node to be selected
10471 * @param {Array} selections Array of currently selected nodes
10473 "beforeselect" : true,
10475 * @event preparedata
10476 * Fires on every row to render, to allow you to change the data.
10477 * @param {Roo.View} this
10478 * @param {Object} data to be rendered (change this)
10480 "preparedata" : true
10488 "click": this.onClick,
10489 "dblclick": this.onDblClick,
10490 "contextmenu": this.onContextMenu,
10494 this.selections = [];
10496 this.cmp = new Roo.CompositeElementLite([]);
10498 this.store = Roo.factory(this.store, Roo.data);
10499 this.setStore(this.store, true);
10502 if ( this.footer && this.footer.xtype) {
10504 var fctr = this.wrapEl.appendChild(document.createElement("div"));
10506 this.footer.dataSource = this.store
10507 this.footer.container = fctr;
10508 this.footer = Roo.factory(this.footer, Roo);
10509 fctr.insertFirst(this.el);
10511 // this is a bit insane - as the paging toolbar seems to detach the el..
10512 // dom.parentNode.parentNode.parentNode
10513 // they get detached?
10517 Roo.View.superclass.constructor.call(this);
10522 Roo.extend(Roo.View, Roo.util.Observable, {
10525 * @cfg {Roo.data.Store} store Data store to load data from.
10530 * @cfg {String|Roo.Element} el The container element.
10535 * @cfg {String|Roo.Template} tpl The template used by this View
10539 * @cfg {String} dataName the named area of the template to use as the data area
10540 * Works with domtemplates roo-name="name"
10544 * @cfg {String} selectedClass The css class to add to selected nodes
10546 selectedClass : "x-view-selected",
10548 * @cfg {String} emptyText The empty text to show when nothing is loaded.
10553 * @cfg {String} text to display on mask (default Loading)
10557 * @cfg {Boolean} multiSelect Allow multiple selection
10559 multiSelect : false,
10561 * @cfg {Boolean} singleSelect Allow single selection
10563 singleSelect: false,
10566 * @cfg {Boolean} toggleSelect - selecting
10568 toggleSelect : false,
10571 * Returns the element this view is bound to.
10572 * @return {Roo.Element}
10574 getEl : function(){
10575 return this.wrapEl;
10581 * Refreshes the view. - called by datachanged on the store. - do not call directly.
10583 refresh : function(){
10584 Roo.log('refresh');
10587 // if we are using something like 'domtemplate', then
10588 // the what gets used is:
10589 // t.applySubtemplate(NAME, data, wrapping data..)
10590 // the outer template then get' applied with
10591 // the store 'extra data'
10592 // and the body get's added to the
10593 // roo-name="data" node?
10594 // <span class='roo-tpl-{name}'></span> ?????
10598 this.clearSelections();
10599 this.el.update("");
10601 var records = this.store.getRange();
10602 if(records.length < 1) {
10604 // is this valid?? = should it render a template??
10606 this.el.update(this.emptyText);
10610 if (this.dataName) {
10611 this.el.update(t.apply(this.store.meta)); //????
10612 el = this.el.child('.roo-tpl-' + this.dataName);
10615 for(var i = 0, len = records.length; i < len; i++){
10616 var data = this.prepareData(records[i].data, i, records[i]);
10617 this.fireEvent("preparedata", this, data, i, records[i]);
10618 html[html.length] = Roo.util.Format.trim(
10620 t.applySubtemplate(this.dataName, data, this.store.meta) :
10627 el.update(html.join(""));
10628 this.nodes = el.dom.childNodes;
10629 this.updateIndexes(0);
10634 * Function to override to reformat the data that is sent to
10635 * the template for each node.
10636 * DEPRICATED - use the preparedata event handler.
10637 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10638 * a JSON object for an UpdateManager bound view).
10640 prepareData : function(data, index, record)
10642 this.fireEvent("preparedata", this, data, index, record);
10646 onUpdate : function(ds, record){
10647 Roo.log('on update');
10648 this.clearSelections();
10649 var index = this.store.indexOf(record);
10650 var n = this.nodes[index];
10651 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10652 n.parentNode.removeChild(n);
10653 this.updateIndexes(index, index);
10659 onAdd : function(ds, records, index)
10661 Roo.log(['on Add', ds, records, index] );
10662 this.clearSelections();
10663 if(this.nodes.length == 0){
10667 var n = this.nodes[index];
10668 for(var i = 0, len = records.length; i < len; i++){
10669 var d = this.prepareData(records[i].data, i, records[i]);
10671 this.tpl.insertBefore(n, d);
10674 this.tpl.append(this.el, d);
10677 this.updateIndexes(index);
10680 onRemove : function(ds, record, index){
10681 Roo.log('onRemove');
10682 this.clearSelections();
10683 var el = this.dataName ?
10684 this.el.child('.roo-tpl-' + this.dataName) :
10687 el.dom.removeChild(this.nodes[index]);
10688 this.updateIndexes(index);
10692 * Refresh an individual node.
10693 * @param {Number} index
10695 refreshNode : function(index){
10696 this.onUpdate(this.store, this.store.getAt(index));
10699 updateIndexes : function(startIndex, endIndex){
10700 var ns = this.nodes;
10701 startIndex = startIndex || 0;
10702 endIndex = endIndex || ns.length - 1;
10703 for(var i = startIndex; i <= endIndex; i++){
10704 ns[i].nodeIndex = i;
10709 * Changes the data store this view uses and refresh the view.
10710 * @param {Store} store
10712 setStore : function(store, initial){
10713 if(!initial && this.store){
10714 this.store.un("datachanged", this.refresh);
10715 this.store.un("add", this.onAdd);
10716 this.store.un("remove", this.onRemove);
10717 this.store.un("update", this.onUpdate);
10718 this.store.un("clear", this.refresh);
10719 this.store.un("beforeload", this.onBeforeLoad);
10720 this.store.un("load", this.onLoad);
10721 this.store.un("loadexception", this.onLoad);
10725 store.on("datachanged", this.refresh, this);
10726 store.on("add", this.onAdd, this);
10727 store.on("remove", this.onRemove, this);
10728 store.on("update", this.onUpdate, this);
10729 store.on("clear", this.refresh, this);
10730 store.on("beforeload", this.onBeforeLoad, this);
10731 store.on("load", this.onLoad, this);
10732 store.on("loadexception", this.onLoad, this);
10740 * onbeforeLoad - masks the loading area.
10743 onBeforeLoad : function(store,opts)
10745 Roo.log('onBeforeLoad');
10747 this.el.update("");
10749 this.el.mask(this.mask ? this.mask : "Loading" );
10751 onLoad : function ()
10758 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10759 * @param {HTMLElement} node
10760 * @return {HTMLElement} The template node
10762 findItemFromChild : function(node){
10763 var el = this.dataName ?
10764 this.el.child('.roo-tpl-' + this.dataName,true) :
10767 if(!node || node.parentNode == el){
10770 var p = node.parentNode;
10771 while(p && p != el){
10772 if(p.parentNode == el){
10781 onClick : function(e){
10782 var item = this.findItemFromChild(e.getTarget());
10784 var index = this.indexOf(item);
10785 if(this.onItemClick(item, index, e) !== false){
10786 this.fireEvent("click", this, index, item, e);
10789 this.clearSelections();
10794 onContextMenu : function(e){
10795 var item = this.findItemFromChild(e.getTarget());
10797 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10802 onDblClick : function(e){
10803 var item = this.findItemFromChild(e.getTarget());
10805 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10809 onItemClick : function(item, index, e)
10811 if(this.fireEvent("beforeclick", this, index, item, e) === false){
10814 if (this.toggleSelect) {
10815 var m = this.isSelected(item) ? 'unselect' : 'select';
10818 _t[m](item, true, false);
10821 if(this.multiSelect || this.singleSelect){
10822 if(this.multiSelect && e.shiftKey && this.lastSelection){
10823 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10825 this.select(item, this.multiSelect && e.ctrlKey);
10826 this.lastSelection = item;
10828 e.preventDefault();
10834 * Get the number of selected nodes.
10837 getSelectionCount : function(){
10838 return this.selections.length;
10842 * Get the currently selected nodes.
10843 * @return {Array} An array of HTMLElements
10845 getSelectedNodes : function(){
10846 return this.selections;
10850 * Get the indexes of the selected nodes.
10853 getSelectedIndexes : function(){
10854 var indexes = [], s = this.selections;
10855 for(var i = 0, len = s.length; i < len; i++){
10856 indexes.push(s[i].nodeIndex);
10862 * Clear all selections
10863 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10865 clearSelections : function(suppressEvent){
10866 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10867 this.cmp.elements = this.selections;
10868 this.cmp.removeClass(this.selectedClass);
10869 this.selections = [];
10870 if(!suppressEvent){
10871 this.fireEvent("selectionchange", this, this.selections);
10877 * Returns true if the passed node is selected
10878 * @param {HTMLElement/Number} node The node or node index
10879 * @return {Boolean}
10881 isSelected : function(node){
10882 var s = this.selections;
10886 node = this.getNode(node);
10887 return s.indexOf(node) !== -1;
10892 * @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
10893 * @param {Boolean} keepExisting (optional) true to keep existing selections
10894 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10896 select : function(nodeInfo, keepExisting, suppressEvent){
10897 if(nodeInfo instanceof Array){
10899 this.clearSelections(true);
10901 for(var i = 0, len = nodeInfo.length; i < len; i++){
10902 this.select(nodeInfo[i], true, true);
10906 var node = this.getNode(nodeInfo);
10907 if(!node || this.isSelected(node)){
10908 return; // already selected.
10911 this.clearSelections(true);
10913 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10914 Roo.fly(node).addClass(this.selectedClass);
10915 this.selections.push(node);
10916 if(!suppressEvent){
10917 this.fireEvent("selectionchange", this, this.selections);
10925 * @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
10926 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10927 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10929 unselect : function(nodeInfo, keepExisting, suppressEvent)
10931 if(nodeInfo instanceof Array){
10932 Roo.each(this.selections, function(s) {
10933 this.unselect(s, nodeInfo);
10937 var node = this.getNode(nodeInfo);
10938 if(!node || !this.isSelected(node)){
10939 Roo.log("not selected");
10940 return; // not selected.
10944 Roo.each(this.selections, function(s) {
10946 Roo.fly(node).removeClass(this.selectedClass);
10953 this.selections= ns;
10954 this.fireEvent("selectionchange", this, this.selections);
10958 * Gets a template node.
10959 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10960 * @return {HTMLElement} The node or null if it wasn't found
10962 getNode : function(nodeInfo){
10963 if(typeof nodeInfo == "string"){
10964 return document.getElementById(nodeInfo);
10965 }else if(typeof nodeInfo == "number"){
10966 return this.nodes[nodeInfo];
10972 * Gets a range template nodes.
10973 * @param {Number} startIndex
10974 * @param {Number} endIndex
10975 * @return {Array} An array of nodes
10977 getNodes : function(start, end){
10978 var ns = this.nodes;
10979 start = start || 0;
10980 end = typeof end == "undefined" ? ns.length - 1 : end;
10983 for(var i = start; i <= end; i++){
10987 for(var i = start; i >= end; i--){
10995 * Finds the index of the passed node
10996 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10997 * @return {Number} The index of the node or -1
10999 indexOf : function(node){
11000 node = this.getNode(node);
11001 if(typeof node.nodeIndex == "number"){
11002 return node.nodeIndex;
11004 var ns = this.nodes;
11005 for(var i = 0, len = ns.length; i < len; i++){
11016 * based on jquery fullcalendar
11020 Roo.bootstrap = Roo.bootstrap || {};
11022 * @class Roo.bootstrap.Calendar
11023 * @extends Roo.bootstrap.Component
11024 * Bootstrap Calendar class
11025 * @cfg {Boolean} loadMask (true|false) default false
11026 * @cfg {Object} header generate the user specific header of the calendar, default false
11029 * Create a new Container
11030 * @param {Object} config The config object
11035 Roo.bootstrap.Calendar = function(config){
11036 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
11040 * Fires when a date is selected
11041 * @param {DatePicker} this
11042 * @param {Date} date The selected date
11046 * @event monthchange
11047 * Fires when the displayed month changes
11048 * @param {DatePicker} this
11049 * @param {Date} date The selected month
11051 'monthchange': true,
11053 * @event evententer
11054 * Fires when mouse over an event
11055 * @param {Calendar} this
11056 * @param {event} Event
11058 'evententer': true,
11060 * @event eventleave
11061 * Fires when the mouse leaves an
11062 * @param {Calendar} this
11065 'eventleave': true,
11067 * @event eventclick
11068 * Fires when the mouse click an
11069 * @param {Calendar} this
11078 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
11081 * @cfg {Number} startDay
11082 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
11090 getAutoCreate : function(){
11093 var fc_button = function(name, corner, style, content ) {
11094 return Roo.apply({},{
11096 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
11098 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
11101 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
11112 style : 'width:100%',
11119 cls : 'fc-header-left',
11121 fc_button('prev', 'left', 'arrow', '‹' ),
11122 fc_button('next', 'right', 'arrow', '›' ),
11123 { tag: 'span', cls: 'fc-header-space' },
11124 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
11132 cls : 'fc-header-center',
11136 cls: 'fc-header-title',
11139 html : 'month / year'
11147 cls : 'fc-header-right',
11149 /* fc_button('month', 'left', '', 'month' ),
11150 fc_button('week', '', '', 'week' ),
11151 fc_button('day', 'right', '', 'day' )
11163 header = this.header;
11166 var cal_heads = function() {
11168 // fixme - handle this.
11170 for (var i =0; i < Date.dayNames.length; i++) {
11171 var d = Date.dayNames[i];
11174 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
11175 html : d.substring(0,3)
11179 ret[0].cls += ' fc-first';
11180 ret[6].cls += ' fc-last';
11183 var cal_cell = function(n) {
11186 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
11191 cls: 'fc-day-number',
11195 cls: 'fc-day-content',
11199 style: 'position: relative;' // height: 17px;
11211 var cal_rows = function() {
11214 for (var r = 0; r < 6; r++) {
11221 for (var i =0; i < Date.dayNames.length; i++) {
11222 var d = Date.dayNames[i];
11223 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
11226 row.cn[0].cls+=' fc-first';
11227 row.cn[0].cn[0].style = 'min-height:90px';
11228 row.cn[6].cls+=' fc-last';
11232 ret[0].cls += ' fc-first';
11233 ret[4].cls += ' fc-prev-last';
11234 ret[5].cls += ' fc-last';
11241 cls: 'fc-border-separate',
11242 style : 'width:100%',
11250 cls : 'fc-first fc-last',
11268 cls : 'fc-content',
11269 style : "position: relative;",
11272 cls : 'fc-view fc-view-month fc-grid',
11273 style : 'position: relative',
11274 unselectable : 'on',
11277 cls : 'fc-event-container',
11278 style : 'position:absolute;z-index:8;top:0;left:0;'
11296 initEvents : function()
11299 throw "can not find store for calendar";
11305 style: "text-align:center",
11309 style: "background-color:white;width:50%;margin:250 auto",
11313 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
11324 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
11326 var size = this.el.select('.fc-content', true).first().getSize();
11327 this.maskEl.setSize(size.width, size.height);
11328 this.maskEl.enableDisplayMode("block");
11329 if(!this.loadMask){
11330 this.maskEl.hide();
11333 this.store = Roo.factory(this.store, Roo.data);
11334 this.store.on('load', this.onLoad, this);
11335 this.store.on('beforeload', this.onBeforeLoad, this);
11339 this.cells = this.el.select('.fc-day',true);
11340 //Roo.log(this.cells);
11341 this.textNodes = this.el.query('.fc-day-number');
11342 this.cells.addClassOnOver('fc-state-hover');
11344 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
11345 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
11346 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
11347 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
11349 this.on('monthchange', this.onMonthChange, this);
11351 this.update(new Date().clearTime());
11354 resize : function() {
11355 var sz = this.el.getSize();
11357 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
11358 this.el.select('.fc-day-content div',true).setHeight(34);
11363 showPrevMonth : function(e){
11364 this.update(this.activeDate.add("mo", -1));
11366 showToday : function(e){
11367 this.update(new Date().clearTime());
11370 showNextMonth : function(e){
11371 this.update(this.activeDate.add("mo", 1));
11375 showPrevYear : function(){
11376 this.update(this.activeDate.add("y", -1));
11380 showNextYear : function(){
11381 this.update(this.activeDate.add("y", 1));
11386 update : function(date)
11388 var vd = this.activeDate;
11389 this.activeDate = date;
11390 // if(vd && this.el){
11391 // var t = date.getTime();
11392 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
11393 // Roo.log('using add remove');
11395 // this.fireEvent('monthchange', this, date);
11397 // this.cells.removeClass("fc-state-highlight");
11398 // this.cells.each(function(c){
11399 // if(c.dateValue == t){
11400 // c.addClass("fc-state-highlight");
11401 // setTimeout(function(){
11402 // try{c.dom.firstChild.focus();}catch(e){}
11412 var days = date.getDaysInMonth();
11414 var firstOfMonth = date.getFirstDateOfMonth();
11415 var startingPos = firstOfMonth.getDay()-this.startDay;
11417 if(startingPos < this.startDay){
11421 var pm = date.add(Date.MONTH, -1);
11422 var prevStart = pm.getDaysInMonth()-startingPos;
11424 this.cells = this.el.select('.fc-day',true);
11425 this.textNodes = this.el.query('.fc-day-number');
11426 this.cells.addClassOnOver('fc-state-hover');
11428 var cells = this.cells.elements;
11429 var textEls = this.textNodes;
11431 Roo.each(cells, function(cell){
11432 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
11435 days += startingPos;
11437 // convert everything to numbers so it's fast
11438 var day = 86400000;
11439 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
11442 //Roo.log(prevStart);
11444 var today = new Date().clearTime().getTime();
11445 var sel = date.clearTime().getTime();
11446 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
11447 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
11448 var ddMatch = this.disabledDatesRE;
11449 var ddText = this.disabledDatesText;
11450 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
11451 var ddaysText = this.disabledDaysText;
11452 var format = this.format;
11454 var setCellClass = function(cal, cell){
11456 //Roo.log('set Cell Class');
11458 var t = d.getTime();
11462 cell.dateValue = t;
11464 cell.className += " fc-today";
11465 cell.className += " fc-state-highlight";
11466 cell.title = cal.todayText;
11469 // disable highlight in other month..
11470 //cell.className += " fc-state-highlight";
11475 cell.className = " fc-state-disabled";
11476 cell.title = cal.minText;
11480 cell.className = " fc-state-disabled";
11481 cell.title = cal.maxText;
11485 if(ddays.indexOf(d.getDay()) != -1){
11486 cell.title = ddaysText;
11487 cell.className = " fc-state-disabled";
11490 if(ddMatch && format){
11491 var fvalue = d.dateFormat(format);
11492 if(ddMatch.test(fvalue)){
11493 cell.title = ddText.replace("%0", fvalue);
11494 cell.className = " fc-state-disabled";
11498 if (!cell.initialClassName) {
11499 cell.initialClassName = cell.dom.className;
11502 cell.dom.className = cell.initialClassName + ' ' + cell.className;
11507 for(; i < startingPos; i++) {
11508 textEls[i].innerHTML = (++prevStart);
11509 d.setDate(d.getDate()+1);
11511 cells[i].className = "fc-past fc-other-month";
11512 setCellClass(this, cells[i]);
11517 for(; i < days; i++){
11518 intDay = i - startingPos + 1;
11519 textEls[i].innerHTML = (intDay);
11520 d.setDate(d.getDate()+1);
11522 cells[i].className = ''; // "x-date-active";
11523 setCellClass(this, cells[i]);
11527 for(; i < 42; i++) {
11528 textEls[i].innerHTML = (++extraDays);
11529 d.setDate(d.getDate()+1);
11531 cells[i].className = "fc-future fc-other-month";
11532 setCellClass(this, cells[i]);
11535 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11537 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11539 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11540 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11542 if(totalRows != 6){
11543 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11544 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11547 this.fireEvent('monthchange', this, date);
11551 if(!this.internalRender){
11552 var main = this.el.dom.firstChild;
11553 var w = main.offsetWidth;
11554 this.el.setWidth(w + this.el.getBorderWidth("lr"));
11555 Roo.fly(main).setWidth(w);
11556 this.internalRender = true;
11557 // opera does not respect the auto grow header center column
11558 // then, after it gets a width opera refuses to recalculate
11559 // without a second pass
11560 if(Roo.isOpera && !this.secondPass){
11561 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11562 this.secondPass = true;
11563 this.update.defer(10, this, [date]);
11570 findCell : function(dt) {
11571 dt = dt.clearTime().getTime();
11573 this.cells.each(function(c){
11574 //Roo.log("check " +c.dateValue + '?=' + dt);
11575 if(c.dateValue == dt){
11585 findCells : function(ev) {
11586 var s = ev.start.clone().clearTime().getTime();
11588 var e= ev.end.clone().clearTime().getTime();
11591 this.cells.each(function(c){
11592 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11594 if(c.dateValue > e){
11597 if(c.dateValue < s){
11606 // findBestRow: function(cells)
11610 // for (var i =0 ; i < cells.length;i++) {
11611 // ret = Math.max(cells[i].rows || 0,ret);
11618 addItem : function(ev)
11620 // look for vertical location slot in
11621 var cells = this.findCells(ev);
11623 // ev.row = this.findBestRow(cells);
11625 // work out the location.
11629 for(var i =0; i < cells.length; i++) {
11637 if (crow.start.getY() == cells[i].getY()) {
11639 crow.end = cells[i];
11655 ev.rendered = false;
11656 // for (var i = 0; i < cells.length;i++) {
11657 // cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11661 this.calevents.push(ev);
11664 clearEvents: function() {
11666 if(!this.calevents){
11670 Roo.each(this.cells.elements, function(c){
11675 Roo.each(this.calevents, function(e) {
11676 Roo.each(e.els, function(el) {
11677 el.un('mouseenter' ,this.onEventEnter, this);
11678 el.un('mouseleave' ,this.onEventLeave, this);
11683 Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
11689 renderEvents: function()
11691 // first make sure there is enough space..
11692 this.cells.each(function(c) {
11697 for (var e = 0; e < this.calevents.length; e++) {
11699 var ev = this.calevents[e];
11700 var cells = ev.cells;
11701 var rows = ev.rows;
11703 for(var i = 0; i < cells.length; i++){
11705 var cbox = this.cells.item(this.cells.indexOf(cells[i]));
11707 if(cells.length < 2 && cbox.rows.length > 3){
11708 cbox.more.push(ev);
11712 cbox.rows.push(ev);
11718 this.cells.each(function(c) {
11719 if(c.more.length && c.more.length == 1){
11720 c.rows.push(c.more.pop());
11723 var r = (c.more.length) ? c.rows.length + 1 : c.rows.length;
11724 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, r * 20));
11727 for (var e = 0; e < c.rows.length; e++){
11728 var ev = c.rows[e];
11734 var cells = ev.cells;
11735 var rows = ev.rows;
11737 for(var i = 0; i < rows.length; i++) {
11739 // how many rows should it span..
11742 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11743 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11745 unselectable : "on",
11748 cls: 'fc-event-inner',
11752 // cls: 'fc-event-time',
11753 // html : cells.length > 1 ? '' : ev.time
11757 cls: 'fc-event-title',
11758 html : String.format('{0}', ev.title)
11765 cls: 'ui-resizable-handle ui-resizable-e',
11766 html : '  '
11773 cfg.cls += ' fc-event-start';
11775 if ((i+1) == rows.length) {
11776 cfg.cls += ' fc-event-end';
11779 var ctr = _this.el.select('.fc-event-container',true).first();
11780 var cg = ctr.createChild(cfg);
11782 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11783 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11785 cg.setXY([sbox.x +2, sbox.y +(e * 20)]);
11786 cg.setWidth(ebox.right - sbox.x -2);
11788 cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
11789 cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
11790 cg.on('click', _this.onEventClick, _this, ev);
11794 ev.rendered = true;
11802 cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
11803 style : 'position: absolute',
11804 unselectable : "on",
11807 cls: 'fc-event-inner',
11811 cls: 'fc-event-title',
11819 cls: 'ui-resizable-handle ui-resizable-e',
11820 html : '  '
11826 var ctr = _this.el.select('.fc-event-container',true).first();
11827 var cg = ctr.createChild(cfg);
11829 var sbox = c.select('.fc-day-content',true).first().getBox();
11830 var ebox = c.select('.fc-day-content',true).first().getBox();
11832 cg.setXY([sbox.x +2, sbox.y +(c.rows.length * 20)]);
11833 cg.setWidth(ebox.right - sbox.x -2);
11835 cg.on('click', _this.onMoreEventClick, _this, c.more);
11845 onEventEnter: function (e, el,event,d) {
11846 this.fireEvent('evententer', this, el, event);
11849 onEventLeave: function (e, el,event,d) {
11850 this.fireEvent('eventleave', this, el, event);
11853 onEventClick: function (e, el,event,d) {
11854 this.fireEvent('eventclick', this, el, event);
11857 onMonthChange: function () {
11861 onMoreEventClick: function(e, el, more)
11865 this.calpopover.placement = 'right';
11866 this.calpopover.setTitle('More');
11868 this.calpopover.setContent('');
11870 var ctr = this.calpopover.el.select('.popover-content', true).first();
11872 Roo.each(more, function(m){
11874 cls : 'fc-event-hori fc-event-draggable',
11877 var cg = ctr.createChild(cfg);
11879 cg.on('click', _this.onEventClick, _this, m);
11882 this.calpopover.show(el);
11887 onLoad: function ()
11889 this.calevents = [];
11892 if(this.store.getCount() > 0){
11893 this.store.data.each(function(d){
11896 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11897 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11898 time : d.data.start_time,
11899 title : d.data.title,
11900 description : d.data.description,
11901 venue : d.data.venue
11906 this.renderEvents();
11908 if(this.calevents.length && this.loadMask){
11909 this.maskEl.hide();
11913 onBeforeLoad: function()
11915 this.clearEvents();
11917 this.maskEl.show();
11931 * @class Roo.bootstrap.Popover
11932 * @extends Roo.bootstrap.Component
11933 * Bootstrap Popover class
11934 * @cfg {String} html contents of the popover (or false to use children..)
11935 * @cfg {String} title of popover (or false to hide)
11936 * @cfg {String} placement how it is placed
11937 * @cfg {String} trigger click || hover (or false to trigger manually)
11938 * @cfg {String} over what (parent or false to trigger manually.)
11941 * Create a new Popover
11942 * @param {Object} config The config object
11945 Roo.bootstrap.Popover = function(config){
11946 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11949 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
11951 title: 'Fill in a title',
11954 placement : 'right',
11955 trigger : 'hover', // hover
11959 can_build_overlaid : false,
11961 getChildContainer : function()
11963 return this.el.select('.popover-content',true).first();
11966 getAutoCreate : function(){
11967 Roo.log('make popover?');
11969 cls : 'popover roo-dynamic',
11970 style: 'display:block',
11976 cls : 'popover-inner',
11980 cls: 'popover-title',
11984 cls : 'popover-content',
11995 setTitle: function(str)
11997 this.el.select('.popover-title',true).first().dom.innerHTML = str;
11999 setContent: function(str)
12001 this.el.select('.popover-content',true).first().dom.innerHTML = str;
12003 // as it get's added to the bottom of the page.
12004 onRender : function(ct, position)
12006 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
12008 var cfg = Roo.apply({}, this.getAutoCreate());
12012 cfg.cls += ' ' + this.cls;
12015 cfg.style = this.style;
12017 Roo.log("adding to ")
12018 this.el = Roo.get(document.body).createChild(cfg, position);
12024 initEvents : function()
12026 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
12027 this.el.enableDisplayMode('block');
12029 if (this.over === false) {
12032 if (this.triggers === false) {
12035 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12036 var triggers = this.trigger ? this.trigger.split(' ') : [];
12037 Roo.each(triggers, function(trigger) {
12039 if (trigger == 'click') {
12040 on_el.on('click', this.toggle, this);
12041 } else if (trigger != 'manual') {
12042 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
12043 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
12045 on_el.on(eventIn ,this.enter, this);
12046 on_el.on(eventOut, this.leave, this);
12057 toggle : function () {
12058 this.hoverState == 'in' ? this.leave() : this.enter();
12061 enter : function () {
12064 clearTimeout(this.timeout);
12066 this.hoverState = 'in'
12068 if (!this.delay || !this.delay.show) {
12073 this.timeout = setTimeout(function () {
12074 if (_t.hoverState == 'in') {
12077 }, this.delay.show)
12079 leave : function() {
12080 clearTimeout(this.timeout);
12082 this.hoverState = 'out'
12084 if (!this.delay || !this.delay.hide) {
12089 this.timeout = setTimeout(function () {
12090 if (_t.hoverState == 'out') {
12093 }, this.delay.hide)
12096 show : function (on_el)
12099 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
12102 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
12103 if (this.html !== false) {
12104 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
12106 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
12107 if (!this.title.length) {
12108 this.el.select('.popover-title',true).hide();
12111 var placement = typeof this.placement == 'function' ?
12112 this.placement.call(this, this.el, on_el) :
12115 var autoToken = /\s?auto?\s?/i;
12116 var autoPlace = autoToken.test(placement);
12118 placement = placement.replace(autoToken, '') || 'top';
12122 //this.el.setXY([0,0]);
12124 this.el.dom.style.display='block';
12125 this.el.addClass(placement);
12127 //this.el.appendTo(on_el);
12129 var p = this.getPosition();
12130 var box = this.el.getBox();
12135 var align = Roo.bootstrap.Popover.alignment[placement]
12136 this.el.alignTo(on_el, align[0],align[1]);
12137 //var arrow = this.el.select('.arrow',true).first();
12138 //arrow.set(align[2],
12140 this.el.addClass('in');
12141 this.hoverState = null;
12143 if (this.el.hasClass('fade')) {
12150 this.el.setXY([0,0]);
12151 this.el.removeClass('in');
12158 Roo.bootstrap.Popover.alignment = {
12159 'left' : ['r-l', [-10,0], 'right'],
12160 'right' : ['l-r', [10,0], 'left'],
12161 'bottom' : ['t-b', [0,10], 'top'],
12162 'top' : [ 'b-t', [0,-10], 'bottom']
12173 * @class Roo.bootstrap.Progress
12174 * @extends Roo.bootstrap.Component
12175 * Bootstrap Progress class
12176 * @cfg {Boolean} striped striped of the progress bar
12177 * @cfg {Boolean} active animated of the progress bar
12181 * Create a new Progress
12182 * @param {Object} config The config object
12185 Roo.bootstrap.Progress = function(config){
12186 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
12189 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
12194 getAutoCreate : function(){
12202 cfg.cls += ' progress-striped';
12206 cfg.cls += ' active';
12225 * @class Roo.bootstrap.ProgressBar
12226 * @extends Roo.bootstrap.Component
12227 * Bootstrap ProgressBar class
12228 * @cfg {Number} aria_valuenow aria-value now
12229 * @cfg {Number} aria_valuemin aria-value min
12230 * @cfg {Number} aria_valuemax aria-value max
12231 * @cfg {String} label label for the progress bar
12232 * @cfg {String} panel (success | info | warning | danger )
12233 * @cfg {String} role role of the progress bar
12234 * @cfg {String} sr_only text
12238 * Create a new ProgressBar
12239 * @param {Object} config The config object
12242 Roo.bootstrap.ProgressBar = function(config){
12243 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
12246 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
12250 aria_valuemax : 100,
12256 getAutoCreate : function()
12261 cls: 'progress-bar',
12262 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
12274 cfg.role = this.role;
12277 if(this.aria_valuenow){
12278 cfg['aria-valuenow'] = this.aria_valuenow;
12281 if(this.aria_valuemin){
12282 cfg['aria-valuemin'] = this.aria_valuemin;
12285 if(this.aria_valuemax){
12286 cfg['aria-valuemax'] = this.aria_valuemax;
12289 if(this.label && !this.sr_only){
12290 cfg.html = this.label;
12294 cfg.cls += ' progress-bar-' + this.panel;
12300 update : function(aria_valuenow)
12302 this.aria_valuenow = aria_valuenow;
12304 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
12319 * @class Roo.bootstrap.TabPanel
12320 * @extends Roo.bootstrap.Component
12321 * Bootstrap TabPanel class
12322 * @cfg {Boolean} active panel active
12323 * @cfg {String} html panel content
12324 * @cfg {String} tabId tab relate id
12325 * @cfg {String} navId The navbar which triggers show hide
12329 * Create a new TabPanel
12330 * @param {Object} config The config object
12333 Roo.bootstrap.TabPanel = function(config){
12334 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
12338 * Fires when the active status changes
12339 * @param {Roo.bootstrap.TabPanel} this
12340 * @param {Boolean} state the new state
12347 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
12354 getAutoCreate : function(){
12358 html: this.html || ''
12362 cfg.cls += ' active';
12366 cfg.tabId = this.tabId;
12371 onRender : function(ct, position)
12373 // Roo.log("Call onRender: " + this.xtype);
12375 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
12377 if (this.navId && this.tabId) {
12378 var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
12380 Roo.log("could not find navID:" + this.navId + ", tabId: " + this.tabId);
12382 item.on('changed', function(item, state) {
12383 this.setActive(state);
12389 setActive: function(state)
12391 Roo.log("panel - set active " + this.tabId + "=" + state);
12393 this.active = state;
12395 this.el.removeClass('active');
12397 } else if (!this.el.hasClass('active')) {
12398 this.el.addClass('active');
12400 this.fireEvent('changed', this, state);
12417 * @class Roo.bootstrap.DateField
12418 * @extends Roo.bootstrap.Input
12419 * Bootstrap DateField class
12420 * @cfg {Number} weekStart default 0
12421 * @cfg {Number} weekStart default 0
12422 * @cfg {Number} viewMode default empty, (months|years)
12423 * @cfg {Number} minViewMode default empty, (months|years)
12424 * @cfg {Number} startDate default -Infinity
12425 * @cfg {Number} endDate default Infinity
12426 * @cfg {Boolean} todayHighlight default false
12427 * @cfg {Boolean} todayBtn default false
12428 * @cfg {Boolean} calendarWeeks default false
12429 * @cfg {Object} daysOfWeekDisabled default empty
12431 * @cfg {Boolean} keyboardNavigation default true
12432 * @cfg {String} language default en
12435 * Create a new DateField
12436 * @param {Object} config The config object
12439 Roo.bootstrap.DateField = function(config){
12440 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
12444 * Fires when this field show.
12445 * @param {Roo.bootstrap.DateField} this
12446 * @param {Mixed} date The date value
12451 * Fires when this field hide.
12452 * @param {Roo.bootstrap.DateField} this
12453 * @param {Mixed} date The date value
12458 * Fires when select a date.
12459 * @param {Roo.bootstrap.DateField} this
12460 * @param {Mixed} date The date value
12466 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
12469 * @cfg {String} format
12470 * The default date format string which can be overriden for localization support. The format must be
12471 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
12475 * @cfg {String} altFormats
12476 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
12477 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
12479 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
12487 todayHighlight : false,
12493 keyboardNavigation: true,
12495 calendarWeeks: false,
12497 startDate: -Infinity,
12501 daysOfWeekDisabled: [],
12505 UTCDate: function()
12507 return new Date(Date.UTC.apply(Date, arguments));
12510 UTCToday: function()
12512 var today = new Date();
12513 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
12516 getDate: function() {
12517 var d = this.getUTCDate();
12518 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
12521 getUTCDate: function() {
12525 setDate: function(d) {
12526 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
12529 setUTCDate: function(d) {
12531 this.setValue(this.formatDate(this.date));
12534 onRender: function(ct, position)
12537 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
12539 this.language = this.language || 'en';
12540 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
12541 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
12543 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
12544 this.format = this.format || 'm/d/y';
12545 this.isInline = false;
12546 this.isInput = true;
12547 this.component = this.el.select('.add-on', true).first() || false;
12548 this.component = (this.component && this.component.length === 0) ? false : this.component;
12549 this.hasInput = this.component && this.inputEL().length;
12551 if (typeof(this.minViewMode === 'string')) {
12552 switch (this.minViewMode) {
12554 this.minViewMode = 1;
12557 this.minViewMode = 2;
12560 this.minViewMode = 0;
12565 if (typeof(this.viewMode === 'string')) {
12566 switch (this.viewMode) {
12579 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
12581 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12583 this.picker().on('mousedown', this.onMousedown, this);
12584 this.picker().on('click', this.onClick, this);
12586 this.picker().addClass('datepicker-dropdown');
12588 this.startViewMode = this.viewMode;
12591 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
12592 if(!this.calendarWeeks){
12597 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
12598 v.attr('colspan', function(i, val){
12599 return parseInt(val) + 1;
12604 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
12606 this.setStartDate(this.startDate);
12607 this.setEndDate(this.endDate);
12609 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
12616 if(this.isInline) {
12621 picker : function()
12623 return this.el.select('.datepicker', true).first();
12626 fillDow: function()
12628 var dowCnt = this.weekStart;
12637 if(this.calendarWeeks){
12645 while (dowCnt < this.weekStart + 7) {
12649 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12653 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12656 fillMonths: function()
12659 var months = this.picker().select('>.datepicker-months td', true).first();
12661 months.dom.innerHTML = '';
12667 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12670 months.createChild(month);
12675 update: function(){
12677 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12679 if (this.date < this.startDate) {
12680 this.viewDate = new Date(this.startDate);
12681 } else if (this.date > this.endDate) {
12682 this.viewDate = new Date(this.endDate);
12684 this.viewDate = new Date(this.date);
12691 var d = new Date(this.viewDate),
12692 year = d.getUTCFullYear(),
12693 month = d.getUTCMonth(),
12694 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12695 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12696 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12697 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12698 currentDate = this.date && this.date.valueOf(),
12699 today = this.UTCToday();
12701 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12703 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12705 // this.picker.select('>tfoot th.today').
12706 // .text(dates[this.language].today)
12707 // .toggle(this.todayBtn !== false);
12709 this.updateNavArrows();
12712 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12714 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12716 prevMonth.setUTCDate(day);
12718 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12720 var nextMonth = new Date(prevMonth);
12722 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12724 nextMonth = nextMonth.valueOf();
12726 var fillMonths = false;
12728 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12730 while(prevMonth.valueOf() < nextMonth) {
12733 if (prevMonth.getUTCDay() === this.weekStart) {
12735 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12743 if(this.calendarWeeks){
12744 // ISO 8601: First week contains first thursday.
12745 // ISO also states week starts on Monday, but we can be more abstract here.
12747 // Start of current week: based on weekstart/current date
12748 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12749 // Thursday of this week
12750 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12751 // First Thursday of year, year from thursday
12752 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12753 // Calendar week: ms between thursdays, div ms per day, div 7 days
12754 calWeek = (th - yth) / 864e5 / 7 + 1;
12756 fillMonths.cn.push({
12764 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12766 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12769 if (this.todayHighlight &&
12770 prevMonth.getUTCFullYear() == today.getFullYear() &&
12771 prevMonth.getUTCMonth() == today.getMonth() &&
12772 prevMonth.getUTCDate() == today.getDate()) {
12773 clsName += ' today';
12776 if (currentDate && prevMonth.valueOf() === currentDate) {
12777 clsName += ' active';
12780 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12781 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12782 clsName += ' disabled';
12785 fillMonths.cn.push({
12787 cls: 'day ' + clsName,
12788 html: prevMonth.getDate()
12791 prevMonth.setDate(prevMonth.getDate()+1);
12794 var currentYear = this.date && this.date.getUTCFullYear();
12795 var currentMonth = this.date && this.date.getUTCMonth();
12797 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12799 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12800 v.removeClass('active');
12802 if(currentYear === year && k === currentMonth){
12803 v.addClass('active');
12806 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12807 v.addClass('disabled');
12813 year = parseInt(year/10, 10) * 10;
12815 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12817 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12820 for (var i = -1; i < 11; i++) {
12821 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12823 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12831 showMode: function(dir) {
12833 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12835 Roo.each(this.picker().select('>div',true).elements, function(v){
12836 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12839 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12844 if(this.isInline) return;
12846 this.picker().removeClass(['bottom', 'top']);
12848 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12850 * place to the top of element!
12854 this.picker().addClass('top');
12855 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12860 this.picker().addClass('bottom');
12862 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12865 parseDate : function(value){
12866 if(!value || value instanceof Date){
12869 var v = Date.parseDate(value, this.format);
12870 if (!v && this.useIso) {
12871 v = Date.parseDate(value, 'Y-m-d');
12873 if(!v && this.altFormats){
12874 if(!this.altFormatsArray){
12875 this.altFormatsArray = this.altFormats.split("|");
12877 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12878 v = Date.parseDate(value, this.altFormatsArray[i]);
12884 formatDate : function(date, fmt){
12885 return (!date || !(date instanceof Date)) ?
12886 date : date.dateFormat(fmt || this.format);
12889 onFocus : function()
12891 Roo.bootstrap.DateField.superclass.onFocus.call(this);
12895 onBlur : function()
12897 Roo.bootstrap.DateField.superclass.onBlur.call(this);
12903 this.picker().show();
12907 this.fireEvent('show', this, this.date);
12912 if(this.isInline) return;
12913 this.picker().hide();
12914 this.viewMode = this.startViewMode;
12917 this.fireEvent('hide', this, this.date);
12921 onMousedown: function(e){
12922 e.stopPropagation();
12923 e.preventDefault();
12926 keyup: function(e){
12927 Roo.bootstrap.DateField.superclass.keyup.call(this);
12932 setValue: function(v){
12933 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12935 this.fireEvent('select', this, this.date);
12939 fireKey: function(e){
12940 if (!this.picker().isVisible()){
12941 if (e.keyCode == 27) // allow escape to hide and re-show picker
12945 var dateChanged = false,
12947 newDate, newViewDate;
12951 e.preventDefault();
12955 if (!this.keyboardNavigation) break;
12956 dir = e.keyCode == 37 ? -1 : 1;
12959 newDate = this.moveYear(this.date, dir);
12960 newViewDate = this.moveYear(this.viewDate, dir);
12961 } else if (e.shiftKey){
12962 newDate = this.moveMonth(this.date, dir);
12963 newViewDate = this.moveMonth(this.viewDate, dir);
12965 newDate = new Date(this.date);
12966 newDate.setUTCDate(this.date.getUTCDate() + dir);
12967 newViewDate = new Date(this.viewDate);
12968 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12970 if (this.dateWithinRange(newDate)){
12971 this.date = newDate;
12972 this.viewDate = newViewDate;
12973 this.setValue(this.formatDate(this.date));
12975 e.preventDefault();
12976 dateChanged = true;
12981 if (!this.keyboardNavigation) break;
12982 dir = e.keyCode == 38 ? -1 : 1;
12984 newDate = this.moveYear(this.date, dir);
12985 newViewDate = this.moveYear(this.viewDate, dir);
12986 } else if (e.shiftKey){
12987 newDate = this.moveMonth(this.date, dir);
12988 newViewDate = this.moveMonth(this.viewDate, dir);
12990 newDate = new Date(this.date);
12991 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12992 newViewDate = new Date(this.viewDate);
12993 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12995 if (this.dateWithinRange(newDate)){
12996 this.date = newDate;
12997 this.viewDate = newViewDate;
12998 this.setValue(this.formatDate(this.date));
13000 e.preventDefault();
13001 dateChanged = true;
13005 this.setValue(this.formatDate(this.date));
13007 e.preventDefault();
13010 this.setValue(this.formatDate(this.date));
13017 onClick: function(e) {
13018 e.stopPropagation();
13019 e.preventDefault();
13021 var target = e.getTarget();
13023 if(target.nodeName.toLowerCase() === 'i'){
13024 target = Roo.get(target).dom.parentNode;
13027 var nodeName = target.nodeName;
13028 var className = target.className;
13029 var html = target.innerHTML;
13031 switch(nodeName.toLowerCase()) {
13033 switch(className) {
13039 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
13040 switch(this.viewMode){
13042 this.viewDate = this.moveMonth(this.viewDate, dir);
13046 this.viewDate = this.moveYear(this.viewDate, dir);
13052 var date = new Date();
13053 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
13055 this.setValue(this.formatDate(this.date));
13061 if (className.indexOf('disabled') === -1) {
13062 this.viewDate.setUTCDate(1);
13063 if (className.indexOf('month') !== -1) {
13064 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
13066 var year = parseInt(html, 10) || 0;
13067 this.viewDate.setUTCFullYear(year);
13076 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
13077 var day = parseInt(html, 10) || 1;
13078 var year = this.viewDate.getUTCFullYear(),
13079 month = this.viewDate.getUTCMonth();
13081 if (className.indexOf('old') !== -1) {
13088 } else if (className.indexOf('new') !== -1) {
13096 this.date = this.UTCDate(year, month, day,0,0,0,0);
13097 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
13099 this.setValue(this.formatDate(this.date));
13106 setStartDate: function(startDate){
13107 this.startDate = startDate || -Infinity;
13108 if (this.startDate !== -Infinity) {
13109 this.startDate = this.parseDate(this.startDate);
13112 this.updateNavArrows();
13115 setEndDate: function(endDate){
13116 this.endDate = endDate || Infinity;
13117 if (this.endDate !== Infinity) {
13118 this.endDate = this.parseDate(this.endDate);
13121 this.updateNavArrows();
13124 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
13125 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
13126 if (typeof(this.daysOfWeekDisabled) !== 'object') {
13127 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
13129 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
13130 return parseInt(d, 10);
13133 this.updateNavArrows();
13136 updateNavArrows: function() {
13137 var d = new Date(this.viewDate),
13138 year = d.getUTCFullYear(),
13139 month = d.getUTCMonth();
13141 Roo.each(this.picker().select('.prev', true).elements, function(v){
13143 switch (this.viewMode) {
13146 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
13152 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
13159 Roo.each(this.picker().select('.next', true).elements, function(v){
13161 switch (this.viewMode) {
13164 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
13170 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
13178 moveMonth: function(date, dir){
13179 if (!dir) return date;
13180 var new_date = new Date(date.valueOf()),
13181 day = new_date.getUTCDate(),
13182 month = new_date.getUTCMonth(),
13183 mag = Math.abs(dir),
13185 dir = dir > 0 ? 1 : -1;
13188 // If going back one month, make sure month is not current month
13189 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
13191 return new_date.getUTCMonth() == month;
13193 // If going forward one month, make sure month is as expected
13194 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
13196 return new_date.getUTCMonth() != new_month;
13198 new_month = month + dir;
13199 new_date.setUTCMonth(new_month);
13200 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
13201 if (new_month < 0 || new_month > 11)
13202 new_month = (new_month + 12) % 12;
13204 // For magnitudes >1, move one month at a time...
13205 for (var i=0; i<mag; i++)
13206 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
13207 new_date = this.moveMonth(new_date, dir);
13208 // ...then reset the day, keeping it in the new month
13209 new_month = new_date.getUTCMonth();
13210 new_date.setUTCDate(day);
13212 return new_month != new_date.getUTCMonth();
13215 // Common date-resetting loop -- if date is beyond end of month, make it
13218 new_date.setUTCDate(--day);
13219 new_date.setUTCMonth(new_month);
13224 moveYear: function(date, dir){
13225 return this.moveMonth(date, dir*12);
13228 dateWithinRange: function(date){
13229 return date >= this.startDate && date <= this.endDate;
13233 remove: function() {
13234 this.picker().remove();
13239 Roo.apply(Roo.bootstrap.DateField, {
13250 html: '<i class="icon-arrow-left"/>'
13260 html: '<i class="icon-arrow-right"/>'
13302 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
13303 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
13304 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
13305 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
13306 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
13319 navFnc: 'FullYear',
13324 navFnc: 'FullYear',
13329 Roo.apply(Roo.bootstrap.DateField, {
13333 cls: 'datepicker dropdown-menu',
13337 cls: 'datepicker-days',
13341 cls: 'table-condensed',
13343 Roo.bootstrap.DateField.head,
13347 Roo.bootstrap.DateField.footer
13354 cls: 'datepicker-months',
13358 cls: 'table-condensed',
13360 Roo.bootstrap.DateField.head,
13361 Roo.bootstrap.DateField.content,
13362 Roo.bootstrap.DateField.footer
13369 cls: 'datepicker-years',
13373 cls: 'table-condensed',
13375 Roo.bootstrap.DateField.head,
13376 Roo.bootstrap.DateField.content,
13377 Roo.bootstrap.DateField.footer
13396 * @class Roo.bootstrap.TimeField
13397 * @extends Roo.bootstrap.Input
13398 * Bootstrap DateField class
13402 * Create a new TimeField
13403 * @param {Object} config The config object
13406 Roo.bootstrap.TimeField = function(config){
13407 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
13411 * Fires when this field show.
13412 * @param {Roo.bootstrap.DateField} this
13413 * @param {Mixed} date The date value
13418 * Fires when this field hide.
13419 * @param {Roo.bootstrap.DateField} this
13420 * @param {Mixed} date The date value
13425 * Fires when select a date.
13426 * @param {Roo.bootstrap.DateField} this
13427 * @param {Mixed} date The date value
13433 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
13436 * @cfg {String} format
13437 * The default time format string which can be overriden for localization support. The format must be
13438 * valid according to {@link Date#parseDate} (defaults to 'H:i').
13442 onRender: function(ct, position)
13445 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
13447 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
13449 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
13451 this.pop = this.picker().select('>.datepicker-time',true).first();
13452 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
13454 this.picker().on('mousedown', this.onMousedown, this);
13455 this.picker().on('click', this.onClick, this);
13457 this.picker().addClass('datepicker-dropdown');
13462 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
13463 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
13464 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
13465 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
13466 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
13467 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
13471 fireKey: function(e){
13472 if (!this.picker().isVisible()){
13473 if (e.keyCode == 27) // allow escape to hide and re-show picker
13478 e.preventDefault();
13486 this.onTogglePeriod();
13489 this.onIncrementMinutes();
13492 this.onDecrementMinutes();
13501 onClick: function(e) {
13502 e.stopPropagation();
13503 e.preventDefault();
13506 picker : function()
13508 return this.el.select('.datepicker', true).first();
13511 fillTime: function()
13513 var time = this.pop.select('tbody', true).first();
13515 time.dom.innerHTML = '';
13530 cls: 'hours-up glyphicon glyphicon-chevron-up'
13550 cls: 'minutes-up glyphicon glyphicon-chevron-up'
13571 cls: 'timepicker-hour',
13586 cls: 'timepicker-minute',
13601 cls: 'btn btn-primary period',
13623 cls: 'hours-down glyphicon glyphicon-chevron-down'
13643 cls: 'minutes-down glyphicon glyphicon-chevron-down'
13661 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13668 var hours = this.time.getHours();
13669 var minutes = this.time.getMinutes();
13682 hours = hours - 12;
13686 hours = '0' + hours;
13690 minutes = '0' + minutes;
13693 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13694 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13695 this.pop.select('button', true).first().dom.innerHTML = period;
13701 this.picker().removeClass(['bottom', 'top']);
13703 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13705 * place to the top of element!
13709 this.picker().addClass('top');
13710 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13715 this.picker().addClass('bottom');
13717 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13720 onFocus : function()
13722 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13726 onBlur : function()
13728 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13734 this.picker().show();
13739 this.fireEvent('show', this, this.date);
13744 this.picker().hide();
13747 this.fireEvent('hide', this, this.date);
13750 setTime : function()
13753 this.setValue(this.time.format(this.format));
13755 this.fireEvent('select', this, this.date);
13760 onMousedown: function(e){
13761 e.stopPropagation();
13762 e.preventDefault();
13765 onIncrementHours: function()
13767 Roo.log('onIncrementHours');
13768 this.time = this.time.add(Date.HOUR, 1);
13773 onDecrementHours: function()
13775 Roo.log('onDecrementHours');
13776 this.time = this.time.add(Date.HOUR, -1);
13780 onIncrementMinutes: function()
13782 Roo.log('onIncrementMinutes');
13783 this.time = this.time.add(Date.MINUTE, 1);
13787 onDecrementMinutes: function()
13789 Roo.log('onDecrementMinutes');
13790 this.time = this.time.add(Date.MINUTE, -1);
13794 onTogglePeriod: function()
13796 Roo.log('onTogglePeriod');
13797 this.time = this.time.add(Date.HOUR, 12);
13804 Roo.apply(Roo.bootstrap.TimeField, {
13834 cls: 'btn btn-info ok',
13846 Roo.apply(Roo.bootstrap.TimeField, {
13850 cls: 'datepicker dropdown-menu',
13854 cls: 'datepicker-time',
13858 cls: 'table-condensed',
13860 Roo.bootstrap.TimeField.content,
13861 Roo.bootstrap.TimeField.footer
13880 * @class Roo.bootstrap.CheckBox
13881 * @extends Roo.bootstrap.Input
13882 * Bootstrap CheckBox class
13884 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13885 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13886 * @cfg {String} boxLabel The text that appears beside the checkbox
13887 * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
13888 * @cfg {Boolean} checked initnal the element
13892 * Create a new CheckBox
13893 * @param {Object} config The config object
13896 Roo.bootstrap.CheckBox = function(config){
13897 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13902 * Fires when the element is checked or unchecked.
13903 * @param {Roo.bootstrap.CheckBox} this This input
13904 * @param {Boolean} checked The new checked value
13910 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
13912 inputType: 'checkbox',
13919 getAutoCreate : function()
13921 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13927 cfg.cls = 'form-group checkbox' //input-group
13935 type : this.inputType,
13936 value : (!this.checked) ? this.valueOff : this.inputValue,
13937 cls : 'roo-checkbox', //'form-box',
13938 placeholder : this.placeholder || ''
13942 if (this.weight) { // Validity check?
13943 cfg.cls += "checkbox-" + this.weight;
13946 if (this.disabled) {
13947 input.disabled=true;
13951 input.checked = this.checked;
13955 input.name = this.name;
13959 input.cls += ' input-' + this.size;
13963 ['xs','sm','md','lg'].map(function(size){
13964 if (settings[size]) {
13965 cfg.cls += ' col-' + size + '-' + settings[size];
13971 var inputblock = input;
13976 if (this.before || this.after) {
13979 cls : 'input-group',
13983 inputblock.cn.push({
13985 cls : 'input-group-addon',
13989 inputblock.cn.push(input);
13991 inputblock.cn.push({
13993 cls : 'input-group-addon',
14000 if (align ==='left' && this.fieldLabel.length) {
14001 Roo.log("left and has label");
14007 cls : 'control-label col-md-' + this.labelWidth,
14008 html : this.fieldLabel
14012 cls : "col-md-" + (12 - this.labelWidth),
14019 } else if ( this.fieldLabel.length) {
14024 tag: this.boxLabel ? 'span' : 'label',
14026 cls: 'control-label box-input-label',
14027 //cls : 'input-group-addon',
14028 html : this.fieldLabel
14038 Roo.log(" no label && no align");
14039 cfg.cn = [ inputblock ] ;
14048 html: this.boxLabel
14060 * return the real input element.
14062 inputEl: function ()
14064 return this.el.select('input.roo-checkbox',true).first();
14069 return this.el.select('label.control-label',true).first();
14072 initEvents : function()
14074 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
14076 this.inputEl().on('click', this.onClick, this);
14080 onClick : function()
14082 this.setChecked(!this.checked);
14085 setChecked : function(state,suppressEvent)
14087 this.checked = state;
14089 this.inputEl().dom.checked = state;
14091 if(suppressEvent !== true){
14092 this.fireEvent('check', this, state);
14095 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14099 setValue : function(v,suppressEvent)
14101 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
14115 * @class Roo.bootstrap.Radio
14116 * @extends Roo.bootstrap.CheckBox
14117 * Bootstrap Radio class
14120 * Create a new Radio
14121 * @param {Object} config The config object
14124 Roo.bootstrap.Radio = function(config){
14125 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
14129 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
14131 inputType: 'radio',
14135 getAutoCreate : function()
14137 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
14143 cfg.cls = 'form-group' //input-group
14148 type : this.inputType,
14149 value : (!this.checked) ? this.valueOff : this.inputValue,
14151 placeholder : this.placeholder || ''
14155 if (this.disabled) {
14156 input.disabled=true;
14160 input.checked = this.checked;
14164 input.name = this.name;
14168 input.cls += ' input-' + this.size;
14172 ['xs','sm','md','lg'].map(function(size){
14173 if (settings[size]) {
14174 cfg.cls += ' col-' + size + '-' + settings[size];
14178 var inputblock = input;
14180 if (this.before || this.after) {
14183 cls : 'input-group',
14187 inputblock.cn.push({
14189 cls : 'input-group-addon',
14193 inputblock.cn.push(input);
14195 inputblock.cn.push({
14197 cls : 'input-group-addon',
14204 if (align ==='left' && this.fieldLabel.length) {
14205 Roo.log("left and has label");
14211 cls : 'control-label col-md-' + this.labelWidth,
14212 html : this.fieldLabel
14216 cls : "col-md-" + (12 - this.labelWidth),
14223 } else if ( this.fieldLabel.length) {
14230 cls: 'control-label box-input-label',
14231 //cls : 'input-group-addon',
14232 html : this.fieldLabel
14242 Roo.log(" no label && no align");
14257 html: this.boxLabel
14265 onClick : function()
14267 this.setChecked(true);
14270 setChecked : function(state,suppressEvent)
14273 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14274 v.dom.checked = false;
14278 this.checked = state;
14279 this.inputEl().dom.checked = state;
14281 if(suppressEvent !== true){
14282 this.fireEvent('check', this, state);
14285 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
14289 getGroupValue : function()
14292 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
14293 if(v.dom.checked == true){
14294 value = v.dom.value;
14302 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
14303 * @return {Mixed} value The field value
14305 getValue : function(){
14306 return this.getGroupValue();
14312 //<script type="text/javascript">
14315 * Based Ext JS Library 1.1.1
14316 * Copyright(c) 2006-2007, Ext JS, LLC.
14322 * @class Roo.HtmlEditorCore
14323 * @extends Roo.Component
14324 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
14326 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
14329 Roo.HtmlEditorCore = function(config){
14332 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
14335 * @event initialize
14336 * Fires when the editor is fully initialized (including the iframe)
14337 * @param {Roo.HtmlEditorCore} this
14342 * Fires when the editor is first receives the focus. Any insertion must wait
14343 * until after this event.
14344 * @param {Roo.HtmlEditorCore} this
14348 * @event beforesync
14349 * Fires before the textarea is updated with content from the editor iframe. Return false
14350 * to cancel the sync.
14351 * @param {Roo.HtmlEditorCore} this
14352 * @param {String} html
14356 * @event beforepush
14357 * Fires before the iframe editor is updated with content from the textarea. Return false
14358 * to cancel the push.
14359 * @param {Roo.HtmlEditorCore} this
14360 * @param {String} html
14365 * Fires when the textarea is updated with content from the editor iframe.
14366 * @param {Roo.HtmlEditorCore} this
14367 * @param {String} html
14372 * Fires when the iframe editor is updated with content from the textarea.
14373 * @param {Roo.HtmlEditorCore} this
14374 * @param {String} html
14379 * @event editorevent
14380 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14381 * @param {Roo.HtmlEditorCore} this
14389 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
14393 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
14399 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14404 * @cfg {Number} height (in pixels)
14408 * @cfg {Number} width (in pixels)
14413 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14416 stylesheets: false,
14421 // private properties
14422 validationEvent : false,
14424 initialized : false,
14426 sourceEditMode : false,
14427 onFocus : Roo.emptyFn,
14429 hideMode:'offsets',
14437 * Protected method that will not generally be called directly. It
14438 * is called when the editor initializes the iframe with HTML contents. Override this method if you
14439 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
14441 getDocMarkup : function(){
14444 Roo.log(this.stylesheets);
14446 // inherit styels from page...??
14447 if (this.stylesheets === false) {
14449 Roo.get(document.head).select('style').each(function(node) {
14450 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14453 Roo.get(document.head).select('link').each(function(node) {
14454 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
14457 } else if (!this.stylesheets.length) {
14459 st = '<style type="text/css">' +
14460 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14463 Roo.each(this.stylesheets, function(s) {
14464 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
14469 st += '<style type="text/css">' +
14470 'IMG { cursor: pointer } ' +
14474 return '<html><head>' + st +
14475 //<style type="text/css">' +
14476 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
14478 ' </head><body class="roo-htmleditor-body"></body></html>';
14482 onRender : function(ct, position)
14485 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
14486 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
14489 this.el.dom.style.border = '0 none';
14490 this.el.dom.setAttribute('tabIndex', -1);
14491 this.el.addClass('x-hidden hide');
14495 if(Roo.isIE){ // fix IE 1px bogus margin
14496 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
14500 this.frameId = Roo.id();
14504 var iframe = this.owner.wrap.createChild({
14506 cls: 'form-control', // bootstrap..
14508 name: this.frameId,
14509 frameBorder : 'no',
14510 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
14515 this.iframe = iframe.dom;
14517 this.assignDocWin();
14519 this.doc.designMode = 'on';
14522 this.doc.write(this.getDocMarkup());
14526 var task = { // must defer to wait for browser to be ready
14528 //console.log("run task?" + this.doc.readyState);
14529 this.assignDocWin();
14530 if(this.doc.body || this.doc.readyState == 'complete'){
14532 this.doc.designMode="on";
14536 Roo.TaskMgr.stop(task);
14537 this.initEditor.defer(10, this);
14544 Roo.TaskMgr.start(task);
14551 onResize : function(w, h)
14553 Roo.log('resize: ' +w + ',' + h );
14554 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
14558 if(typeof w == 'number'){
14560 this.iframe.style.width = w + 'px';
14562 if(typeof h == 'number'){
14564 this.iframe.style.height = h + 'px';
14566 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
14573 * Toggles the editor between standard and source edit mode.
14574 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14576 toggleSourceEdit : function(sourceEditMode){
14578 this.sourceEditMode = sourceEditMode === true;
14580 if(this.sourceEditMode){
14582 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
14585 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
14586 //this.iframe.className = '';
14589 //this.setSize(this.owner.wrap.getSize());
14590 //this.fireEvent('editmodechange', this, this.sourceEditMode);
14597 * Protected method that will not generally be called directly. If you need/want
14598 * custom HTML cleanup, this is the method you should override.
14599 * @param {String} html The HTML to be cleaned
14600 * return {String} The cleaned HTML
14602 cleanHtml : function(html){
14603 html = String(html);
14604 if(html.length > 5){
14605 if(Roo.isSafari){ // strip safari nonsense
14606 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
14609 if(html == ' '){
14616 * HTML Editor -> Textarea
14617 * Protected method that will not generally be called directly. Syncs the contents
14618 * of the editor iframe with the textarea.
14620 syncValue : function(){
14621 if(this.initialized){
14622 var bd = (this.doc.body || this.doc.documentElement);
14623 //this.cleanUpPaste(); -- this is done else where and causes havoc..
14624 var html = bd.innerHTML;
14626 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
14627 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
14629 html = '<div style="'+m[0]+'">' + html + '</div>';
14632 html = this.cleanHtml(html);
14633 // fix up the special chars.. normaly like back quotes in word...
14634 // however we do not want to do this with chinese..
14635 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
14636 var cc = b.charCodeAt();
14638 (cc >= 0x4E00 && cc < 0xA000 ) ||
14639 (cc >= 0x3400 && cc < 0x4E00 ) ||
14640 (cc >= 0xf900 && cc < 0xfb00 )
14646 if(this.owner.fireEvent('beforesync', this, html) !== false){
14647 this.el.dom.value = html;
14648 this.owner.fireEvent('sync', this, html);
14654 * Protected method that will not generally be called directly. Pushes the value of the textarea
14655 * into the iframe editor.
14657 pushValue : function(){
14658 if(this.initialized){
14659 var v = this.el.dom.value.trim();
14661 // if(v.length < 1){
14665 if(this.owner.fireEvent('beforepush', this, v) !== false){
14666 var d = (this.doc.body || this.doc.documentElement);
14668 this.cleanUpPaste();
14669 this.el.dom.value = d.innerHTML;
14670 this.owner.fireEvent('push', this, v);
14676 deferFocus : function(){
14677 this.focus.defer(10, this);
14681 focus : function(){
14682 if(this.win && !this.sourceEditMode){
14689 assignDocWin: function()
14691 var iframe = this.iframe;
14694 this.doc = iframe.contentWindow.document;
14695 this.win = iframe.contentWindow;
14697 if (!Roo.get(this.frameId)) {
14700 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14701 this.win = Roo.get(this.frameId).dom.contentWindow;
14706 initEditor : function(){
14707 //console.log("INIT EDITOR");
14708 this.assignDocWin();
14712 this.doc.designMode="on";
14714 this.doc.write(this.getDocMarkup());
14717 var dbody = (this.doc.body || this.doc.documentElement);
14718 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14719 // this copies styles from the containing element into thsi one..
14720 // not sure why we need all of this..
14721 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14722 ss['background-attachment'] = 'fixed'; // w3c
14723 dbody.bgProperties = 'fixed'; // ie
14724 Roo.DomHelper.applyStyles(dbody, ss);
14725 Roo.EventManager.on(this.doc, {
14726 //'mousedown': this.onEditorEvent,
14727 'mouseup': this.onEditorEvent,
14728 'dblclick': this.onEditorEvent,
14729 'click': this.onEditorEvent,
14730 'keyup': this.onEditorEvent,
14735 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14737 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14738 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14740 this.initialized = true;
14742 this.owner.fireEvent('initialize', this);
14747 onDestroy : function(){
14753 //for (var i =0; i < this.toolbars.length;i++) {
14754 // // fixme - ask toolbars for heights?
14755 // this.toolbars[i].onDestroy();
14758 //this.wrap.dom.innerHTML = '';
14759 //this.wrap.remove();
14764 onFirstFocus : function(){
14766 this.assignDocWin();
14769 this.activated = true;
14772 if(Roo.isGecko){ // prevent silly gecko errors
14774 var s = this.win.getSelection();
14775 if(!s.focusNode || s.focusNode.nodeType != 3){
14776 var r = s.getRangeAt(0);
14777 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14782 this.execCmd('useCSS', true);
14783 this.execCmd('styleWithCSS', false);
14786 this.owner.fireEvent('activate', this);
14790 adjustFont: function(btn){
14791 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14792 //if(Roo.isSafari){ // safari
14795 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14796 if(Roo.isSafari){ // safari
14797 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14798 v = (v < 10) ? 10 : v;
14799 v = (v > 48) ? 48 : v;
14800 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14805 v = Math.max(1, v+adjust);
14807 this.execCmd('FontSize', v );
14810 onEditorEvent : function(e){
14811 this.owner.fireEvent('editorevent', this, e);
14812 // this.updateToolbar();
14813 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14816 insertTag : function(tg)
14818 // could be a bit smarter... -> wrap the current selected tRoo..
14819 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14821 range = this.createRange(this.getSelection());
14822 var wrappingNode = this.doc.createElement(tg.toLowerCase());
14823 wrappingNode.appendChild(range.extractContents());
14824 range.insertNode(wrappingNode);
14831 this.execCmd("formatblock", tg);
14835 insertText : function(txt)
14839 var range = this.createRange();
14840 range.deleteContents();
14841 //alert(Sender.getAttribute('label'));
14843 range.insertNode(this.doc.createTextNode(txt));
14849 * Executes a Midas editor command on the editor document and performs necessary focus and
14850 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14851 * @param {String} cmd The Midas command
14852 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14854 relayCmd : function(cmd, value){
14856 this.execCmd(cmd, value);
14857 this.owner.fireEvent('editorevent', this);
14858 //this.updateToolbar();
14859 this.owner.deferFocus();
14863 * Executes a Midas editor command directly on the editor document.
14864 * For visual commands, you should use {@link #relayCmd} instead.
14865 * <b>This should only be called after the editor is initialized.</b>
14866 * @param {String} cmd The Midas command
14867 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14869 execCmd : function(cmd, value){
14870 this.doc.execCommand(cmd, false, value === undefined ? null : value);
14877 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14879 * @param {String} text | dom node..
14881 insertAtCursor : function(text)
14886 if(!this.activated){
14892 var r = this.doc.selection.createRange();
14903 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14907 // from jquery ui (MIT licenced)
14909 var win = this.win;
14911 if (win.getSelection && win.getSelection().getRangeAt) {
14912 range = win.getSelection().getRangeAt(0);
14913 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14914 range.insertNode(node);
14915 } else if (win.document.selection && win.document.selection.createRange) {
14916 // no firefox support
14917 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14918 win.document.selection.createRange().pasteHTML(txt);
14920 // no firefox support
14921 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14922 this.execCmd('InsertHTML', txt);
14931 mozKeyPress : function(e){
14933 var c = e.getCharCode(), cmd;
14936 c = String.fromCharCode(c).toLowerCase();
14950 this.cleanUpPaste.defer(100, this);
14958 e.preventDefault();
14966 fixKeys : function(){ // load time branching for fastest keydown performance
14968 return function(e){
14969 var k = e.getKey(), r;
14972 r = this.doc.selection.createRange();
14975 r.pasteHTML('    ');
14982 r = this.doc.selection.createRange();
14984 var target = r.parentElement();
14985 if(!target || target.tagName.toLowerCase() != 'li'){
14987 r.pasteHTML('<br />');
14993 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14994 this.cleanUpPaste.defer(100, this);
15000 }else if(Roo.isOpera){
15001 return function(e){
15002 var k = e.getKey();
15006 this.execCmd('InsertHTML','    ');
15009 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15010 this.cleanUpPaste.defer(100, this);
15015 }else if(Roo.isSafari){
15016 return function(e){
15017 var k = e.getKey();
15021 this.execCmd('InsertText','\t');
15025 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
15026 this.cleanUpPaste.defer(100, this);
15034 getAllAncestors: function()
15036 var p = this.getSelectedNode();
15039 a.push(p); // push blank onto stack..
15040 p = this.getParentElement();
15044 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
15048 a.push(this.doc.body);
15052 lastSelNode : false,
15055 getSelection : function()
15057 this.assignDocWin();
15058 return Roo.isIE ? this.doc.selection : this.win.getSelection();
15061 getSelectedNode: function()
15063 // this may only work on Gecko!!!
15065 // should we cache this!!!!
15070 var range = this.createRange(this.getSelection()).cloneRange();
15073 var parent = range.parentElement();
15075 var testRange = range.duplicate();
15076 testRange.moveToElementText(parent);
15077 if (testRange.inRange(range)) {
15080 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
15083 parent = parent.parentElement;
15088 // is ancestor a text element.
15089 var ac = range.commonAncestorContainer;
15090 if (ac.nodeType == 3) {
15091 ac = ac.parentNode;
15094 var ar = ac.childNodes;
15097 var other_nodes = [];
15098 var has_other_nodes = false;
15099 for (var i=0;i<ar.length;i++) {
15100 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
15103 // fullly contained node.
15105 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
15110 // probably selected..
15111 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
15112 other_nodes.push(ar[i]);
15116 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
15121 has_other_nodes = true;
15123 if (!nodes.length && other_nodes.length) {
15124 nodes= other_nodes;
15126 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
15132 createRange: function(sel)
15134 // this has strange effects when using with
15135 // top toolbar - not sure if it's a great idea.
15136 //this.editor.contentWindow.focus();
15137 if (typeof sel != "undefined") {
15139 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
15141 return this.doc.createRange();
15144 return this.doc.createRange();
15147 getParentElement: function()
15150 this.assignDocWin();
15151 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
15153 var range = this.createRange(sel);
15156 var p = range.commonAncestorContainer;
15157 while (p.nodeType == 3) { // text node
15168 * Range intersection.. the hard stuff...
15172 * [ -- selected range --- ]
15176 * if end is before start or hits it. fail.
15177 * if start is after end or hits it fail.
15179 * if either hits (but other is outside. - then it's not
15185 // @see http://www.thismuchiknow.co.uk/?p=64.
15186 rangeIntersectsNode : function(range, node)
15188 var nodeRange = node.ownerDocument.createRange();
15190 nodeRange.selectNode(node);
15192 nodeRange.selectNodeContents(node);
15195 var rangeStartRange = range.cloneRange();
15196 rangeStartRange.collapse(true);
15198 var rangeEndRange = range.cloneRange();
15199 rangeEndRange.collapse(false);
15201 var nodeStartRange = nodeRange.cloneRange();
15202 nodeStartRange.collapse(true);
15204 var nodeEndRange = nodeRange.cloneRange();
15205 nodeEndRange.collapse(false);
15207 return rangeStartRange.compareBoundaryPoints(
15208 Range.START_TO_START, nodeEndRange) == -1 &&
15209 rangeEndRange.compareBoundaryPoints(
15210 Range.START_TO_START, nodeStartRange) == 1;
15214 rangeCompareNode : function(range, node)
15216 var nodeRange = node.ownerDocument.createRange();
15218 nodeRange.selectNode(node);
15220 nodeRange.selectNodeContents(node);
15224 range.collapse(true);
15226 nodeRange.collapse(true);
15228 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
15229 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
15231 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
15233 var nodeIsBefore = ss == 1;
15234 var nodeIsAfter = ee == -1;
15236 if (nodeIsBefore && nodeIsAfter)
15238 if (!nodeIsBefore && nodeIsAfter)
15239 return 1; //right trailed.
15241 if (nodeIsBefore && !nodeIsAfter)
15242 return 2; // left trailed.
15247 // private? - in a new class?
15248 cleanUpPaste : function()
15250 // cleans up the whole document..
15251 Roo.log('cleanuppaste');
15253 this.cleanUpChildren(this.doc.body);
15254 var clean = this.cleanWordChars(this.doc.body.innerHTML);
15255 if (clean != this.doc.body.innerHTML) {
15256 this.doc.body.innerHTML = clean;
15261 cleanWordChars : function(input) {// change the chars to hex code
15262 var he = Roo.HtmlEditorCore;
15264 var output = input;
15265 Roo.each(he.swapCodes, function(sw) {
15266 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
15268 output = output.replace(swapper, sw[1]);
15275 cleanUpChildren : function (n)
15277 if (!n.childNodes.length) {
15280 for (var i = n.childNodes.length-1; i > -1 ; i--) {
15281 this.cleanUpChild(n.childNodes[i]);
15288 cleanUpChild : function (node)
15291 //console.log(node);
15292 if (node.nodeName == "#text") {
15293 // clean up silly Windows -- stuff?
15296 if (node.nodeName == "#comment") {
15297 node.parentNode.removeChild(node);
15298 // clean up silly Windows -- stuff?
15302 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
15304 node.parentNode.removeChild(node);
15309 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
15311 // remove <a name=....> as rendering on yahoo mailer is borked with this.
15312 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
15314 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
15315 // remove_keep_children = true;
15318 if (remove_keep_children) {
15319 this.cleanUpChildren(node);
15320 // inserts everything just before this node...
15321 while (node.childNodes.length) {
15322 var cn = node.childNodes[0];
15323 node.removeChild(cn);
15324 node.parentNode.insertBefore(cn, node);
15326 node.parentNode.removeChild(node);
15330 if (!node.attributes || !node.attributes.length) {
15331 this.cleanUpChildren(node);
15335 function cleanAttr(n,v)
15338 if (v.match(/^\./) || v.match(/^\//)) {
15341 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
15344 if (v.match(/^#/)) {
15347 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
15348 node.removeAttribute(n);
15352 function cleanStyle(n,v)
15354 if (v.match(/expression/)) { //XSS?? should we even bother..
15355 node.removeAttribute(n);
15358 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
15359 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
15362 var parts = v.split(/;/);
15365 Roo.each(parts, function(p) {
15366 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
15370 var l = p.split(':').shift().replace(/\s+/g,'');
15371 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
15373 if ( cblack.indexOf(l) > -1) {
15374 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15375 //node.removeAttribute(n);
15379 // only allow 'c whitelisted system attributes'
15380 if ( cwhite.length && cwhite.indexOf(l) < 0) {
15381 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
15382 //node.removeAttribute(n);
15392 if (clean.length) {
15393 node.setAttribute(n, clean.join(';'));
15395 node.removeAttribute(n);
15401 for (var i = node.attributes.length-1; i > -1 ; i--) {
15402 var a = node.attributes[i];
15405 if (a.name.toLowerCase().substr(0,2)=='on') {
15406 node.removeAttribute(a.name);
15409 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
15410 node.removeAttribute(a.name);
15413 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
15414 cleanAttr(a.name,a.value); // fixme..
15417 if (a.name == 'style') {
15418 cleanStyle(a.name,a.value);
15421 /// clean up MS crap..
15422 // tecnically this should be a list of valid class'es..
15425 if (a.name == 'class') {
15426 if (a.value.match(/^Mso/)) {
15427 node.className = '';
15430 if (a.value.match(/body/)) {
15431 node.className = '';
15442 this.cleanUpChildren(node);
15447 * Clean up MS wordisms...
15449 cleanWord : function(node)
15452 var cleanWordChildren = function()
15454 if (!node.childNodes.length) {
15457 for (var i = node.childNodes.length-1; i > -1 ; i--) {
15458 _t.cleanWord(node.childNodes[i]);
15464 this.cleanWord(this.doc.body);
15467 if (node.nodeName == "#text") {
15468 // clean up silly Windows -- stuff?
15471 if (node.nodeName == "#comment") {
15472 node.parentNode.removeChild(node);
15473 // clean up silly Windows -- stuff?
15477 if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
15478 node.parentNode.removeChild(node);
15482 // remove - but keep children..
15483 if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
15484 while (node.childNodes.length) {
15485 var cn = node.childNodes[0];
15486 node.removeChild(cn);
15487 node.parentNode.insertBefore(cn, node);
15489 node.parentNode.removeChild(node);
15490 cleanWordChildren();
15494 if (node.className.length) {
15496 var cn = node.className.split(/\W+/);
15498 Roo.each(cn, function(cls) {
15499 if (cls.match(/Mso[a-zA-Z]+/)) {
15504 node.className = cna.length ? cna.join(' ') : '';
15506 node.removeAttribute("class");
15510 if (node.hasAttribute("lang")) {
15511 node.removeAttribute("lang");
15514 if (node.hasAttribute("style")) {
15516 var styles = node.getAttribute("style").split(";");
15518 Roo.each(styles, function(s) {
15519 if (!s.match(/:/)) {
15522 var kv = s.split(":");
15523 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
15526 // what ever is left... we allow.
15529 node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
15530 if (!nstyle.length) {
15531 node.removeAttribute('style');
15535 cleanWordChildren();
15539 domToHTML : function(currentElement, depth, nopadtext) {
15541 depth = depth || 0;
15542 nopadtext = nopadtext || false;
15544 if (!currentElement) {
15545 return this.domToHTML(this.doc.body);
15548 //Roo.log(currentElement);
15550 var allText = false;
15551 var nodeName = currentElement.nodeName;
15552 var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
15554 if (nodeName == '#text') {
15555 return currentElement.nodeValue;
15560 if (nodeName != 'BODY') {
15563 // Prints the node tagName, such as <A>, <IMG>, etc
15566 for(i = 0; i < currentElement.attributes.length;i++) {
15568 var aname = currentElement.attributes.item(i).name;
15569 if (!currentElement.attributes.item(i).value.length) {
15572 attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
15575 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
15584 if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
15587 if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
15592 // Traverse the tree
15594 var currentElementChild = currentElement.childNodes.item(i);
15595 var allText = true;
15596 var innerHTML = '';
15598 while (currentElementChild) {
15599 // Formatting code (indent the tree so it looks nice on the screen)
15600 var nopad = nopadtext;
15601 if (lastnode == 'SPAN') {
15605 if (currentElementChild.nodeName == '#text') {
15606 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
15607 if (!nopad && toadd.length > 80) {
15608 innerHTML += "\n" + (new Array( depth + 1 )).join( " " );
15610 innerHTML += toadd;
15613 currentElementChild = currentElement.childNodes.item(i);
15619 innerHTML += nopad ? '' : "\n" + (new Array( depth + 1 )).join( " " );
15621 // Recursively traverse the tree structure of the child node
15622 innerHTML += this.domToHTML(currentElementChild, depth+1, nopadtext);
15623 lastnode = currentElementChild.nodeName;
15625 currentElementChild=currentElement.childNodes.item(i);
15631 // The remaining code is mostly for formatting the tree
15632 ret+= nopadtext ? '' : "\n" + (new Array( depth )).join( " " );
15637 ret+= "</"+tagName+">";
15643 // hide stuff that is not compatible
15657 * @event specialkey
15661 * @cfg {String} fieldClass @hide
15664 * @cfg {String} focusClass @hide
15667 * @cfg {String} autoCreate @hide
15670 * @cfg {String} inputType @hide
15673 * @cfg {String} invalidClass @hide
15676 * @cfg {String} invalidText @hide
15679 * @cfg {String} msgFx @hide
15682 * @cfg {String} validateOnBlur @hide
15686 Roo.HtmlEditorCore.white = [
15687 'area', 'br', 'img', 'input', 'hr', 'wbr',
15689 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
15690 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
15691 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
15692 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
15693 'table', 'ul', 'xmp',
15695 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
15698 'dir', 'menu', 'ol', 'ul', 'dl',
15704 Roo.HtmlEditorCore.black = [
15705 // 'embed', 'object', // enable - backend responsiblity to clean thiese
15707 'base', 'basefont', 'bgsound', 'blink', 'body',
15708 'frame', 'frameset', 'head', 'html', 'ilayer',
15709 'iframe', 'layer', 'link', 'meta', 'object',
15710 'script', 'style' ,'title', 'xml' // clean later..
15712 Roo.HtmlEditorCore.clean = [
15713 'script', 'style', 'title', 'xml'
15715 Roo.HtmlEditorCore.remove = [
15720 Roo.HtmlEditorCore.ablack = [
15724 Roo.HtmlEditorCore.aclean = [
15725 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
15729 Roo.HtmlEditorCore.pwhite= [
15730 'http', 'https', 'mailto'
15733 // white listed style attributes.
15734 Roo.HtmlEditorCore.cwhite= [
15735 // 'text-align', /// default is to allow most things..
15741 // black listed style attributes.
15742 Roo.HtmlEditorCore.cblack= [
15743 // 'font-size' -- this can be set by the project
15747 Roo.HtmlEditorCore.swapCodes =[
15766 * @class Roo.bootstrap.HtmlEditor
15767 * @extends Roo.bootstrap.TextArea
15768 * Bootstrap HtmlEditor class
15771 * Create a new HtmlEditor
15772 * @param {Object} config The config object
15775 Roo.bootstrap.HtmlEditor = function(config){
15776 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
15777 if (!this.toolbars) {
15778 this.toolbars = [];
15780 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
15783 * @event initialize
15784 * Fires when the editor is fully initialized (including the iframe)
15785 * @param {HtmlEditor} this
15790 * Fires when the editor is first receives the focus. Any insertion must wait
15791 * until after this event.
15792 * @param {HtmlEditor} this
15796 * @event beforesync
15797 * Fires before the textarea is updated with content from the editor iframe. Return false
15798 * to cancel the sync.
15799 * @param {HtmlEditor} this
15800 * @param {String} html
15804 * @event beforepush
15805 * Fires before the iframe editor is updated with content from the textarea. Return false
15806 * to cancel the push.
15807 * @param {HtmlEditor} this
15808 * @param {String} html
15813 * Fires when the textarea is updated with content from the editor iframe.
15814 * @param {HtmlEditor} this
15815 * @param {String} html
15820 * Fires when the iframe editor is updated with content from the textarea.
15821 * @param {HtmlEditor} this
15822 * @param {String} html
15826 * @event editmodechange
15827 * Fires when the editor switches edit modes
15828 * @param {HtmlEditor} this
15829 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
15831 editmodechange: true,
15833 * @event editorevent
15834 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
15835 * @param {HtmlEditor} this
15839 * @event firstfocus
15840 * Fires when on first focus - needed by toolbars..
15841 * @param {HtmlEditor} this
15846 * Auto save the htmlEditor value as a file into Events
15847 * @param {HtmlEditor} this
15851 * @event savedpreview
15852 * preview the saved version of htmlEditor
15853 * @param {HtmlEditor} this
15860 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
15864 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15869 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
15874 * @cfg {Number} height (in pixels)
15878 * @cfg {Number} width (in pixels)
15883 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15886 stylesheets: false,
15891 // private properties
15892 validationEvent : false,
15894 initialized : false,
15897 onFocus : Roo.emptyFn,
15899 hideMode:'offsets',
15902 tbContainer : false,
15904 toolbarContainer :function() {
15905 return this.wrap.select('.x-html-editor-tb',true).first();
15909 * Protected method that will not generally be called directly. It
15910 * is called when the editor creates its toolbar. Override this method if you need to
15911 * add custom toolbar buttons.
15912 * @param {HtmlEditor} editor
15914 createToolbar : function(){
15916 Roo.log("create toolbars");
15918 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15919 this.toolbars[0].render(this.toolbarContainer());
15923 // if (!editor.toolbars || !editor.toolbars.length) {
15924 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15927 // for (var i =0 ; i < editor.toolbars.length;i++) {
15928 // editor.toolbars[i] = Roo.factory(
15929 // typeof(editor.toolbars[i]) == 'string' ?
15930 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
15931 // Roo.bootstrap.HtmlEditor);
15932 // editor.toolbars[i].init(editor);
15938 onRender : function(ct, position)
15940 // Roo.log("Call onRender: " + this.xtype);
15942 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15944 this.wrap = this.inputEl().wrap({
15945 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15948 this.editorcore.onRender(ct, position);
15950 if (this.resizable) {
15951 this.resizeEl = new Roo.Resizable(this.wrap, {
15955 minHeight : this.height,
15956 height: this.height,
15957 handles : this.resizable,
15960 resize : function(r, w, h) {
15961 _t.onResize(w,h); // -something
15967 this.createToolbar(this);
15970 if(!this.width && this.resizable){
15971 this.setSize(this.wrap.getSize());
15973 if (this.resizeEl) {
15974 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15975 // should trigger onReize..
15981 onResize : function(w, h)
15983 Roo.log('resize: ' +w + ',' + h );
15984 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15988 if(this.inputEl() ){
15989 if(typeof w == 'number'){
15990 var aw = w - this.wrap.getFrameWidth('lr');
15991 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15994 if(typeof h == 'number'){
15995 var tbh = -11; // fixme it needs to tool bar size!
15996 for (var i =0; i < this.toolbars.length;i++) {
15997 // fixme - ask toolbars for heights?
15998 tbh += this.toolbars[i].el.getHeight();
15999 //if (this.toolbars[i].footer) {
16000 // tbh += this.toolbars[i].footer.el.getHeight();
16008 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
16009 ah -= 5; // knock a few pixes off for look..
16010 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
16014 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
16015 this.editorcore.onResize(ew,eh);
16020 * Toggles the editor between standard and source edit mode.
16021 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
16023 toggleSourceEdit : function(sourceEditMode)
16025 this.editorcore.toggleSourceEdit(sourceEditMode);
16027 if(this.editorcore.sourceEditMode){
16028 Roo.log('editor - showing textarea');
16031 // Roo.log(this.syncValue());
16033 this.inputEl().removeClass('hide');
16034 this.inputEl().dom.removeAttribute('tabIndex');
16035 this.inputEl().focus();
16037 Roo.log('editor - hiding textarea');
16039 // Roo.log(this.pushValue());
16042 this.inputEl().addClass('hide');
16043 this.inputEl().dom.setAttribute('tabIndex', -1);
16044 //this.deferFocus();
16047 if(this.resizable){
16048 this.setSize(this.wrap.getSize());
16051 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
16054 // private (for BoxComponent)
16055 adjustSize : Roo.BoxComponent.prototype.adjustSize,
16057 // private (for BoxComponent)
16058 getResizeEl : function(){
16062 // private (for BoxComponent)
16063 getPositionEl : function(){
16068 initEvents : function(){
16069 this.originalValue = this.getValue();
16073 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16076 // markInvalid : Roo.emptyFn,
16078 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
16081 // clearInvalid : Roo.emptyFn,
16083 setValue : function(v){
16084 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
16085 this.editorcore.pushValue();
16090 deferFocus : function(){
16091 this.focus.defer(10, this);
16095 focus : function(){
16096 this.editorcore.focus();
16102 onDestroy : function(){
16108 for (var i =0; i < this.toolbars.length;i++) {
16109 // fixme - ask toolbars for heights?
16110 this.toolbars[i].onDestroy();
16113 this.wrap.dom.innerHTML = '';
16114 this.wrap.remove();
16119 onFirstFocus : function(){
16120 //Roo.log("onFirstFocus");
16121 this.editorcore.onFirstFocus();
16122 for (var i =0; i < this.toolbars.length;i++) {
16123 this.toolbars[i].onFirstFocus();
16129 syncValue : function()
16131 this.editorcore.syncValue();
16134 pushValue : function()
16136 this.editorcore.pushValue();
16140 // hide stuff that is not compatible
16154 * @event specialkey
16158 * @cfg {String} fieldClass @hide
16161 * @cfg {String} focusClass @hide
16164 * @cfg {String} autoCreate @hide
16167 * @cfg {String} inputType @hide
16170 * @cfg {String} invalidClass @hide
16173 * @cfg {String} invalidText @hide
16176 * @cfg {String} msgFx @hide
16179 * @cfg {String} validateOnBlur @hide
16190 * @class Roo.bootstrap.HtmlEditorToolbar1
16195 new Roo.bootstrap.HtmlEditor({
16198 new Roo.bootstrap.HtmlEditorToolbar1({
16199 disable : { fonts: 1 , format: 1, ..., ... , ...],
16205 * @cfg {Object} disable List of elements to disable..
16206 * @cfg {Array} btns List of additional buttons.
16210 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
16213 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
16216 Roo.apply(this, config);
16218 // default disabled, based on 'good practice'..
16219 this.disable = this.disable || {};
16220 Roo.applyIf(this.disable, {
16223 specialElements : true
16225 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
16227 this.editor = config.editor;
16228 this.editorcore = config.editor.editorcore;
16230 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
16232 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
16233 // dont call parent... till later.
16235 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.NavSimplebar, {
16241 editorcore : false,
16246 "h1","h2","h3","h4","h5","h6",
16248 "abbr", "acronym", "address", "cite", "samp", "var",
16252 onRender : function(ct, position)
16254 // Roo.log("Call onRender: " + this.xtype);
16256 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
16258 this.el.dom.style.marginBottom = '0';
16260 var editorcore = this.editorcore;
16261 var editor= this.editor;
16264 var btn = function(id,cmd , toggle, handler){
16266 var event = toggle ? 'toggle' : 'click';
16271 xns: Roo.bootstrap,
16274 enableToggle:toggle !== false,
16276 pressed : toggle ? false : null,
16279 a.listeners[toggle ? 'toggle' : 'click'] = function() {
16280 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
16289 xns: Roo.bootstrap,
16290 glyphicon : 'font',
16294 xns: Roo.bootstrap,
16298 Roo.each(this.formats, function(f) {
16299 style.menu.items.push({
16301 xns: Roo.bootstrap,
16302 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
16307 editorcore.insertTag(this.tagname);
16314 children.push(style);
16317 btn('bold',false,true);
16318 btn('italic',false,true);
16319 btn('align-left', 'justifyleft',true);
16320 btn('align-center', 'justifycenter',true);
16321 btn('align-right' , 'justifyright',true);
16322 btn('link', false, false, function(btn) {
16323 //Roo.log("create link?");
16324 var url = prompt(this.createLinkText, this.defaultLinkValue);
16325 if(url && url != 'http:/'+'/'){
16326 this.editorcore.relayCmd('createlink', url);
16329 btn('list','insertunorderedlist',true);
16330 btn('pencil', false,true, function(btn){
16333 this.toggleSourceEdit(btn.pressed);
16339 xns: Roo.bootstrap,
16344 xns: Roo.bootstrap,
16349 cog.menu.items.push({
16351 xns: Roo.bootstrap,
16352 html : Clean styles,
16357 editorcore.insertTag(this.tagname);
16366 this.xtype = 'NavSimplebar';
16368 for(var i=0;i< children.length;i++) {
16370 this.buttons.add(this.addxtypeChild(children[i]));
16374 editor.on('editorevent', this.updateToolbar, this);
16376 onBtnClick : function(id)
16378 this.editorcore.relayCmd(id);
16379 this.editorcore.focus();
16383 * Protected method that will not generally be called directly. It triggers
16384 * a toolbar update by reading the markup state of the current selection in the editor.
16386 updateToolbar: function(){
16388 if(!this.editorcore.activated){
16389 this.editor.onFirstFocus(); // is this neeed?
16393 var btns = this.buttons;
16394 var doc = this.editorcore.doc;
16395 btns.get('bold').setActive(doc.queryCommandState('bold'));
16396 btns.get('italic').setActive(doc.queryCommandState('italic'));
16397 //btns.get('underline').setActive(doc.queryCommandState('underline'));
16399 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
16400 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
16401 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
16403 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
16404 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
16407 var ans = this.editorcore.getAllAncestors();
16408 if (this.formatCombo) {
16411 var store = this.formatCombo.store;
16412 this.formatCombo.setValue("");
16413 for (var i =0; i < ans.length;i++) {
16414 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
16416 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
16424 // hides menus... - so this cant be on a menu...
16425 Roo.bootstrap.MenuMgr.hideAll();
16427 Roo.bootstrap.MenuMgr.hideAll();
16428 //this.editorsyncValue();
16430 onFirstFocus: function() {
16431 this.buttons.each(function(item){
16435 toggleSourceEdit : function(sourceEditMode){
16438 if(sourceEditMode){
16439 Roo.log("disabling buttons");
16440 this.buttons.each( function(item){
16441 if(item.cmd != 'pencil'){
16447 Roo.log("enabling buttons");
16448 if(this.editorcore.initialized){
16449 this.buttons.each( function(item){
16455 Roo.log("calling toggole on editor");
16456 // tell the editor that it's been pressed..
16457 this.editor.toggleSourceEdit(sourceEditMode);
16467 * @class Roo.bootstrap.Table.AbstractSelectionModel
16468 * @extends Roo.util.Observable
16469 * Abstract base class for grid SelectionModels. It provides the interface that should be
16470 * implemented by descendant classes. This class should not be directly instantiated.
16473 Roo.bootstrap.Table.AbstractSelectionModel = function(){
16474 this.locked = false;
16475 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
16479 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
16480 /** @ignore Called by the grid automatically. Do not call directly. */
16481 init : function(grid){
16487 * Locks the selections.
16490 this.locked = true;
16494 * Unlocks the selections.
16496 unlock : function(){
16497 this.locked = false;
16501 * Returns true if the selections are locked.
16502 * @return {Boolean}
16504 isLocked : function(){
16505 return this.locked;
16509 * @class Roo.bootstrap.Table.ColumnModel
16510 * @extends Roo.util.Observable
16511 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
16512 * the columns in the table.
16515 * @param {Object} config An Array of column config objects. See this class's
16516 * config objects for details.
16518 Roo.bootstrap.Table.ColumnModel = function(config){
16520 * The config passed into the constructor
16522 this.config = config;
16525 // if no id, create one
16526 // if the column does not have a dataIndex mapping,
16527 // map it to the order it is in the config
16528 for(var i = 0, len = config.length; i < len; i++){
16530 if(typeof c.dataIndex == "undefined"){
16533 if(typeof c.renderer == "string"){
16534 c.renderer = Roo.util.Format[c.renderer];
16536 if(typeof c.id == "undefined"){
16539 // if(c.editor && c.editor.xtype){
16540 // c.editor = Roo.factory(c.editor, Roo.grid);
16542 // if(c.editor && c.editor.isFormField){
16543 // c.editor = new Roo.grid.GridEditor(c.editor);
16546 this.lookup[c.id] = c;
16550 * The width of columns which have no width specified (defaults to 100)
16553 this.defaultWidth = 100;
16556 * Default sortable of columns which have no sortable specified (defaults to false)
16559 this.defaultSortable = false;
16563 * @event widthchange
16564 * Fires when the width of a column changes.
16565 * @param {ColumnModel} this
16566 * @param {Number} columnIndex The column index
16567 * @param {Number} newWidth The new width
16569 "widthchange": true,
16571 * @event headerchange
16572 * Fires when the text of a header changes.
16573 * @param {ColumnModel} this
16574 * @param {Number} columnIndex The column index
16575 * @param {Number} newText The new header text
16577 "headerchange": true,
16579 * @event hiddenchange
16580 * Fires when a column is hidden or "unhidden".
16581 * @param {ColumnModel} this
16582 * @param {Number} columnIndex The column index
16583 * @param {Boolean} hidden true if hidden, false otherwise
16585 "hiddenchange": true,
16587 * @event columnmoved
16588 * Fires when a column is moved.
16589 * @param {ColumnModel} this
16590 * @param {Number} oldIndex
16591 * @param {Number} newIndex
16593 "columnmoved" : true,
16595 * @event columlockchange
16596 * Fires when a column's locked state is changed
16597 * @param {ColumnModel} this
16598 * @param {Number} colIndex
16599 * @param {Boolean} locked true if locked
16601 "columnlockchange" : true
16603 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
16605 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
16607 * @cfg {String} header The header text to display in the Grid view.
16610 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
16611 * {@link Roo.data.Record} definition from which to draw the column's value. If not
16612 * specified, the column's index is used as an index into the Record's data Array.
16615 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
16616 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
16619 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
16620 * Defaults to the value of the {@link #defaultSortable} property.
16621 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
16624 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
16627 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
16630 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
16633 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
16636 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
16637 * given the cell's data value. See {@link #setRenderer}. If not specified, the
16638 * default renderer uses the raw data value.
16641 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
16645 * Returns the id of the column at the specified index.
16646 * @param {Number} index The column index
16647 * @return {String} the id
16649 getColumnId : function(index){
16650 return this.config[index].id;
16654 * Returns the column for a specified id.
16655 * @param {String} id The column id
16656 * @return {Object} the column
16658 getColumnById : function(id){
16659 return this.lookup[id];
16664 * Returns the column for a specified dataIndex.
16665 * @param {String} dataIndex The column dataIndex
16666 * @return {Object|Boolean} the column or false if not found
16668 getColumnByDataIndex: function(dataIndex){
16669 var index = this.findColumnIndex(dataIndex);
16670 return index > -1 ? this.config[index] : false;
16674 * Returns the index for a specified column id.
16675 * @param {String} id The column id
16676 * @return {Number} the index, or -1 if not found
16678 getIndexById : function(id){
16679 for(var i = 0, len = this.config.length; i < len; i++){
16680 if(this.config[i].id == id){
16688 * Returns the index for a specified column dataIndex.
16689 * @param {String} dataIndex The column dataIndex
16690 * @return {Number} the index, or -1 if not found
16693 findColumnIndex : function(dataIndex){
16694 for(var i = 0, len = this.config.length; i < len; i++){
16695 if(this.config[i].dataIndex == dataIndex){
16703 moveColumn : function(oldIndex, newIndex){
16704 var c = this.config[oldIndex];
16705 this.config.splice(oldIndex, 1);
16706 this.config.splice(newIndex, 0, c);
16707 this.dataMap = null;
16708 this.fireEvent("columnmoved", this, oldIndex, newIndex);
16711 isLocked : function(colIndex){
16712 return this.config[colIndex].locked === true;
16715 setLocked : function(colIndex, value, suppressEvent){
16716 if(this.isLocked(colIndex) == value){
16719 this.config[colIndex].locked = value;
16720 if(!suppressEvent){
16721 this.fireEvent("columnlockchange", this, colIndex, value);
16725 getTotalLockedWidth : function(){
16726 var totalWidth = 0;
16727 for(var i = 0; i < this.config.length; i++){
16728 if(this.isLocked(i) && !this.isHidden(i)){
16729 this.totalWidth += this.getColumnWidth(i);
16735 getLockedCount : function(){
16736 for(var i = 0, len = this.config.length; i < len; i++){
16737 if(!this.isLocked(i)){
16744 * Returns the number of columns.
16747 getColumnCount : function(visibleOnly){
16748 if(visibleOnly === true){
16750 for(var i = 0, len = this.config.length; i < len; i++){
16751 if(!this.isHidden(i)){
16757 return this.config.length;
16761 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
16762 * @param {Function} fn
16763 * @param {Object} scope (optional)
16764 * @return {Array} result
16766 getColumnsBy : function(fn, scope){
16768 for(var i = 0, len = this.config.length; i < len; i++){
16769 var c = this.config[i];
16770 if(fn.call(scope||this, c, i) === true){
16778 * Returns true if the specified column is sortable.
16779 * @param {Number} col The column index
16780 * @return {Boolean}
16782 isSortable : function(col){
16783 if(typeof this.config[col].sortable == "undefined"){
16784 return this.defaultSortable;
16786 return this.config[col].sortable;
16790 * Returns the rendering (formatting) function defined for the column.
16791 * @param {Number} col The column index.
16792 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
16794 getRenderer : function(col){
16795 if(!this.config[col].renderer){
16796 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
16798 return this.config[col].renderer;
16802 * Sets the rendering (formatting) function for a column.
16803 * @param {Number} col The column index
16804 * @param {Function} fn The function to use to process the cell's raw data
16805 * to return HTML markup for the grid view. The render function is called with
16806 * the following parameters:<ul>
16807 * <li>Data value.</li>
16808 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
16809 * <li>css A CSS style string to apply to the table cell.</li>
16810 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
16811 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
16812 * <li>Row index</li>
16813 * <li>Column index</li>
16814 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
16816 setRenderer : function(col, fn){
16817 this.config[col].renderer = fn;
16821 * Returns the width for the specified column.
16822 * @param {Number} col The column index
16825 getColumnWidth : function(col){
16826 return this.config[col].width * 1 || this.defaultWidth;
16830 * Sets the width for a column.
16831 * @param {Number} col The column index
16832 * @param {Number} width The new width
16834 setColumnWidth : function(col, width, suppressEvent){
16835 this.config[col].width = width;
16836 this.totalWidth = null;
16837 if(!suppressEvent){
16838 this.fireEvent("widthchange", this, col, width);
16843 * Returns the total width of all columns.
16844 * @param {Boolean} includeHidden True to include hidden column widths
16847 getTotalWidth : function(includeHidden){
16848 if(!this.totalWidth){
16849 this.totalWidth = 0;
16850 for(var i = 0, len = this.config.length; i < len; i++){
16851 if(includeHidden || !this.isHidden(i)){
16852 this.totalWidth += this.getColumnWidth(i);
16856 return this.totalWidth;
16860 * Returns the header for the specified column.
16861 * @param {Number} col The column index
16864 getColumnHeader : function(col){
16865 return this.config[col].header;
16869 * Sets the header for a column.
16870 * @param {Number} col The column index
16871 * @param {String} header The new header
16873 setColumnHeader : function(col, header){
16874 this.config[col].header = header;
16875 this.fireEvent("headerchange", this, col, header);
16879 * Returns the tooltip for the specified column.
16880 * @param {Number} col The column index
16883 getColumnTooltip : function(col){
16884 return this.config[col].tooltip;
16887 * Sets the tooltip for a column.
16888 * @param {Number} col The column index
16889 * @param {String} tooltip The new tooltip
16891 setColumnTooltip : function(col, tooltip){
16892 this.config[col].tooltip = tooltip;
16896 * Returns the dataIndex for the specified column.
16897 * @param {Number} col The column index
16900 getDataIndex : function(col){
16901 return this.config[col].dataIndex;
16905 * Sets the dataIndex for a column.
16906 * @param {Number} col The column index
16907 * @param {Number} dataIndex The new dataIndex
16909 setDataIndex : function(col, dataIndex){
16910 this.config[col].dataIndex = dataIndex;
16916 * Returns true if the cell is editable.
16917 * @param {Number} colIndex The column index
16918 * @param {Number} rowIndex The row index
16919 * @return {Boolean}
16921 isCellEditable : function(colIndex, rowIndex){
16922 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16926 * Returns the editor defined for the cell/column.
16927 * return false or null to disable editing.
16928 * @param {Number} colIndex The column index
16929 * @param {Number} rowIndex The row index
16932 getCellEditor : function(colIndex, rowIndex){
16933 return this.config[colIndex].editor;
16937 * Sets if a column is editable.
16938 * @param {Number} col The column index
16939 * @param {Boolean} editable True if the column is editable
16941 setEditable : function(col, editable){
16942 this.config[col].editable = editable;
16947 * Returns true if the column is hidden.
16948 * @param {Number} colIndex The column index
16949 * @return {Boolean}
16951 isHidden : function(colIndex){
16952 return this.config[colIndex].hidden;
16957 * Returns true if the column width cannot be changed
16959 isFixed : function(colIndex){
16960 return this.config[colIndex].fixed;
16964 * Returns true if the column can be resized
16965 * @return {Boolean}
16967 isResizable : function(colIndex){
16968 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16971 * Sets if a column is hidden.
16972 * @param {Number} colIndex The column index
16973 * @param {Boolean} hidden True if the column is hidden
16975 setHidden : function(colIndex, hidden){
16976 this.config[colIndex].hidden = hidden;
16977 this.totalWidth = null;
16978 this.fireEvent("hiddenchange", this, colIndex, hidden);
16982 * Sets the editor for a column.
16983 * @param {Number} col The column index
16984 * @param {Object} editor The editor object
16986 setEditor : function(col, editor){
16987 this.config[col].editor = editor;
16991 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16992 if(typeof value == "string" && value.length < 1){
16998 // Alias for backwards compatibility
16999 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
17002 * @extends Roo.bootstrap.Table.AbstractSelectionModel
17003 * @class Roo.bootstrap.Table.RowSelectionModel
17004 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
17005 * It supports multiple selections and keyboard selection/navigation.
17007 * @param {Object} config
17010 Roo.bootstrap.Table.RowSelectionModel = function(config){
17011 Roo.apply(this, config);
17012 this.selections = new Roo.util.MixedCollection(false, function(o){
17017 this.lastActive = false;
17021 * @event selectionchange
17022 * Fires when the selection changes
17023 * @param {SelectionModel} this
17025 "selectionchange" : true,
17027 * @event afterselectionchange
17028 * Fires after the selection changes (eg. by key press or clicking)
17029 * @param {SelectionModel} this
17031 "afterselectionchange" : true,
17033 * @event beforerowselect
17034 * Fires when a row is selected being selected, return false to cancel.
17035 * @param {SelectionModel} this
17036 * @param {Number} rowIndex The selected index
17037 * @param {Boolean} keepExisting False if other selections will be cleared
17039 "beforerowselect" : true,
17042 * Fires when a row is selected.
17043 * @param {SelectionModel} this
17044 * @param {Number} rowIndex The selected index
17045 * @param {Roo.data.Record} r The record
17047 "rowselect" : true,
17049 * @event rowdeselect
17050 * Fires when a row is deselected.
17051 * @param {SelectionModel} this
17052 * @param {Number} rowIndex The selected index
17054 "rowdeselect" : true
17056 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
17057 this.locked = false;
17060 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
17062 * @cfg {Boolean} singleSelect
17063 * True to allow selection of only one row at a time (defaults to false)
17065 singleSelect : false,
17068 initEvents : function(){
17070 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
17071 this.grid.on("mousedown", this.handleMouseDown, this);
17072 }else{ // allow click to work like normal
17073 this.grid.on("rowclick", this.handleDragableRowClick, this);
17076 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
17077 "up" : function(e){
17079 this.selectPrevious(e.shiftKey);
17080 }else if(this.last !== false && this.lastActive !== false){
17081 var last = this.last;
17082 this.selectRange(this.last, this.lastActive-1);
17083 this.grid.getView().focusRow(this.lastActive);
17084 if(last !== false){
17088 this.selectFirstRow();
17090 this.fireEvent("afterselectionchange", this);
17092 "down" : function(e){
17094 this.selectNext(e.shiftKey);
17095 }else if(this.last !== false && this.lastActive !== false){
17096 var last = this.last;
17097 this.selectRange(this.last, this.lastActive+1);
17098 this.grid.getView().focusRow(this.lastActive);
17099 if(last !== false){
17103 this.selectFirstRow();
17105 this.fireEvent("afterselectionchange", this);
17110 var view = this.grid.view;
17111 view.on("refresh", this.onRefresh, this);
17112 view.on("rowupdated", this.onRowUpdated, this);
17113 view.on("rowremoved", this.onRemove, this);
17117 onRefresh : function(){
17118 var ds = this.grid.dataSource, i, v = this.grid.view;
17119 var s = this.selections;
17120 s.each(function(r){
17121 if((i = ds.indexOfId(r.id)) != -1){
17130 onRemove : function(v, index, r){
17131 this.selections.remove(r);
17135 onRowUpdated : function(v, index, r){
17136 if(this.isSelected(r)){
17137 v.onRowSelect(index);
17143 * @param {Array} records The records to select
17144 * @param {Boolean} keepExisting (optional) True to keep existing selections
17146 selectRecords : function(records, keepExisting){
17148 this.clearSelections();
17150 var ds = this.grid.dataSource;
17151 for(var i = 0, len = records.length; i < len; i++){
17152 this.selectRow(ds.indexOf(records[i]), true);
17157 * Gets the number of selected rows.
17160 getCount : function(){
17161 return this.selections.length;
17165 * Selects the first row in the grid.
17167 selectFirstRow : function(){
17172 * Select the last row.
17173 * @param {Boolean} keepExisting (optional) True to keep existing selections
17175 selectLastRow : function(keepExisting){
17176 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
17180 * Selects the row immediately following the last selected row.
17181 * @param {Boolean} keepExisting (optional) True to keep existing selections
17183 selectNext : function(keepExisting){
17184 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
17185 this.selectRow(this.last+1, keepExisting);
17186 this.grid.getView().focusRow(this.last);
17191 * Selects the row that precedes the last selected row.
17192 * @param {Boolean} keepExisting (optional) True to keep existing selections
17194 selectPrevious : function(keepExisting){
17196 this.selectRow(this.last-1, keepExisting);
17197 this.grid.getView().focusRow(this.last);
17202 * Returns the selected records
17203 * @return {Array} Array of selected records
17205 getSelections : function(){
17206 return [].concat(this.selections.items);
17210 * Returns the first selected record.
17213 getSelected : function(){
17214 return this.selections.itemAt(0);
17219 * Clears all selections.
17221 clearSelections : function(fast){
17222 if(this.locked) return;
17224 var ds = this.grid.dataSource;
17225 var s = this.selections;
17226 s.each(function(r){
17227 this.deselectRow(ds.indexOfId(r.id));
17231 this.selections.clear();
17238 * Selects all rows.
17240 selectAll : function(){
17241 if(this.locked) return;
17242 this.selections.clear();
17243 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
17244 this.selectRow(i, true);
17249 * Returns True if there is a selection.
17250 * @return {Boolean}
17252 hasSelection : function(){
17253 return this.selections.length > 0;
17257 * Returns True if the specified row is selected.
17258 * @param {Number/Record} record The record or index of the record to check
17259 * @return {Boolean}
17261 isSelected : function(index){
17262 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
17263 return (r && this.selections.key(r.id) ? true : false);
17267 * Returns True if the specified record id is selected.
17268 * @param {String} id The id of record to check
17269 * @return {Boolean}
17271 isIdSelected : function(id){
17272 return (this.selections.key(id) ? true : false);
17276 handleMouseDown : function(e, t){
17277 var view = this.grid.getView(), rowIndex;
17278 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
17281 if(e.shiftKey && this.last !== false){
17282 var last = this.last;
17283 this.selectRange(last, rowIndex, e.ctrlKey);
17284 this.last = last; // reset the last
17285 view.focusRow(rowIndex);
17287 var isSelected = this.isSelected(rowIndex);
17288 if(e.button !== 0 && isSelected){
17289 view.focusRow(rowIndex);
17290 }else if(e.ctrlKey && isSelected){
17291 this.deselectRow(rowIndex);
17292 }else if(!isSelected){
17293 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
17294 view.focusRow(rowIndex);
17297 this.fireEvent("afterselectionchange", this);
17300 handleDragableRowClick : function(grid, rowIndex, e)
17302 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
17303 this.selectRow(rowIndex, false);
17304 grid.view.focusRow(rowIndex);
17305 this.fireEvent("afterselectionchange", this);
17310 * Selects multiple rows.
17311 * @param {Array} rows Array of the indexes of the row to select
17312 * @param {Boolean} keepExisting (optional) True to keep existing selections
17314 selectRows : function(rows, keepExisting){
17316 this.clearSelections();
17318 for(var i = 0, len = rows.length; i < len; i++){
17319 this.selectRow(rows[i], true);
17324 * Selects a range of rows. All rows in between startRow and endRow are also selected.
17325 * @param {Number} startRow The index of the first row in the range
17326 * @param {Number} endRow The index of the last row in the range
17327 * @param {Boolean} keepExisting (optional) True to retain existing selections
17329 selectRange : function(startRow, endRow, keepExisting){
17330 if(this.locked) return;
17332 this.clearSelections();
17334 if(startRow <= endRow){
17335 for(var i = startRow; i <= endRow; i++){
17336 this.selectRow(i, true);
17339 for(var i = startRow; i >= endRow; i--){
17340 this.selectRow(i, true);
17346 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
17347 * @param {Number} startRow The index of the first row in the range
17348 * @param {Number} endRow The index of the last row in the range
17350 deselectRange : function(startRow, endRow, preventViewNotify){
17351 if(this.locked) return;
17352 for(var i = startRow; i <= endRow; i++){
17353 this.deselectRow(i, preventViewNotify);
17359 * @param {Number} row The index of the row to select
17360 * @param {Boolean} keepExisting (optional) True to keep existing selections
17362 selectRow : function(index, keepExisting, preventViewNotify){
17363 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
17364 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
17365 if(!keepExisting || this.singleSelect){
17366 this.clearSelections();
17368 var r = this.grid.dataSource.getAt(index);
17369 this.selections.add(r);
17370 this.last = this.lastActive = index;
17371 if(!preventViewNotify){
17372 this.grid.getView().onRowSelect(index);
17374 this.fireEvent("rowselect", this, index, r);
17375 this.fireEvent("selectionchange", this);
17381 * @param {Number} row The index of the row to deselect
17383 deselectRow : function(index, preventViewNotify){
17384 if(this.locked) return;
17385 if(this.last == index){
17388 if(this.lastActive == index){
17389 this.lastActive = false;
17391 var r = this.grid.dataSource.getAt(index);
17392 this.selections.remove(r);
17393 if(!preventViewNotify){
17394 this.grid.getView().onRowDeselect(index);
17396 this.fireEvent("rowdeselect", this, index);
17397 this.fireEvent("selectionchange", this);
17401 restoreLast : function(){
17403 this.last = this._last;
17408 acceptsNav : function(row, col, cm){
17409 return !cm.isHidden(col) && cm.isCellEditable(col, row);
17413 onEditorKey : function(field, e){
17414 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
17419 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
17421 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
17423 }else if(k == e.ENTER && !e.ctrlKey){
17427 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
17429 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
17431 }else if(k == e.ESC){
17435 g.startEditing(newCell[0], newCell[1]);
17446 * @class Roo.bootstrap.MessageBar
17447 * @extends Roo.bootstrap.Component
17448 * Bootstrap MessageBar class
17449 * @cfg {String} html contents of the MessageBar
17450 * @cfg {String} weight (info | success | warning | danger) default info
17451 * @cfg {String} beforeClass insert the bar before the given class
17452 * @cfg {Boolean} closable (true | false) default false
17453 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
17456 * Create a new Element
17457 * @param {Object} config The config object
17460 Roo.bootstrap.MessageBar = function(config){
17461 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
17464 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
17470 beforeClass: 'bootstrap-sticky-wrap',
17472 getAutoCreate : function(){
17476 cls: 'alert alert-dismissable alert-' + this.weight,
17481 html: this.html || ''
17487 cfg.cls += ' alert-messages-fixed';
17501 onRender : function(ct, position)
17503 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17506 var cfg = Roo.apply({}, this.getAutoCreate());
17510 cfg.cls += ' ' + this.cls;
17513 cfg.style = this.style;
17515 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
17517 this.el.setVisibilityMode(Roo.Element.DISPLAY);
17520 this.el.select('>button.close').on('click', this.hide, this);
17526 if (!this.rendered) {
17532 this.fireEvent('show', this);
17538 if (!this.rendered) {
17544 this.fireEvent('hide', this);
17547 update : function()
17549 // var e = this.el.dom.firstChild;
17551 // if(this.closable){
17552 // e = e.nextSibling;
17555 // e.data = this.html || '';
17557 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';