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..
248 if (typeof (tree.menu) != 'undefined') {
249 tree.menu.parentType = cn.xtype;
250 tree.menu.triggerEl = cn.el;
251 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
255 if (!tree.items || !tree.items.length) {
259 var items = tree.items;
262 //Roo.log(items.length);
264 for(var i =0;i < items.length;i++) {
265 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
286 * @class Roo.bootstrap.Body
287 * @extends Roo.bootstrap.Component
288 * Bootstrap Body class
292 * @param {Object} config The config object
295 Roo.bootstrap.Body = function(config){
296 Roo.bootstrap.Body.superclass.constructor.call(this, config);
297 this.el = Roo.get(document.body);
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
305 onRender : function(ct, position){
308 //this.el.addClass([this.fieldClass, this.cls]);
326 * @class Roo.bootstrap.ButtonGroup
327 * @extends Roo.bootstrap.Component
328 * Bootstrap ButtonGroup class
329 * @cfg {String} size lg | sm | xs (default empty normal)
330 * @cfg {String} align vertical | justified (default none)
331 * @cfg {String} direction up | down (default down)
332 * @cfg {Boolean} toolbar false | true
333 * @cfg {Boolean} btn true | false
338 * @param {Object} config The config object
341 Roo.bootstrap.ButtonGroup = function(config){
342 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
353 getAutoCreate : function(){
359 cfg.html = this.html || cfg.html;
370 if (['vertical','justified'].indexOf(this.align)!==-1) {
371 cfg.cls = 'btn-group-' + this.align;
373 if (this.align == 'justified') {
374 console.log(this.items);
378 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379 cfg.cls += ' btn-group-' + this.size;
382 if (this.direction == 'up') {
383 cfg.cls += ' dropup' ;
399 * @class Roo.bootstrap.Button
400 * @extends Roo.bootstrap.Component
401 * Bootstrap Button class
402 * @cfg {String} html The button content
403 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404 * @cfg {String} size empty | lg | sm | xs
405 * @cfg {String} tag empty | a | input | submit
406 * @cfg {String} href empty or href
407 * @cfg {Boolean} disabled false | true
408 * @cfg {Boolean} isClose false | true
409 * @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
410 * @cfg {String} badge text for badge
411 * @cfg {String} theme default (or empty) | glow
412 * @cfg {Boolean} inverse false | true
413 * @cfg {Boolean} toggle false | true
414 * @cfg {String} ontext text for on toggle state
415 * @cfg {String} offtext text for off toggle state
416 * @cfg {Boolean} defaulton true | false
417 * @cfg {Boolean} preventDefault (true | false) default true
418 * @cfg {Boolean} removeClass true | false remove the standard class..
419 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
422 * Create a new button
423 * @param {Object} config The config object
427 Roo.bootstrap.Button = function(config){
428 Roo.bootstrap.Button.superclass.constructor.call(this, config);
433 * When a butotn is pressed
434 * @param {Roo.EventObject} e
439 * After the button has been toggles
440 * @param {Roo.EventObject} e
441 * @param {boolean} pressed (also available as button.pressed)
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
465 preventDefault: true,
474 getAutoCreate : function(){
482 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
488 cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
490 if (this.toggle == true) {
493 cls: 'slider-frame roo-button',
498 'data-off-text':'OFF',
499 cls: 'slider-button',
505 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506 cfg.cls += ' '+this.weight;
515 cfg["aria-hidden"] = true;
517 cfg.html = "×";
523 if (this.theme==='default') {
524 cfg.cls = 'btn roo-button';
526 //if (this.parentType != 'Navbar') {
527 this.weight = this.weight.length ? this.weight : 'default';
529 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
531 cfg.cls += ' btn-' + this.weight;
533 } else if (this.theme==='glow') {
536 cfg.cls = 'btn-glow roo-button';
538 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
540 cfg.cls += ' ' + this.weight;
546 this.cls += ' inverse';
551 cfg.cls += ' active';
555 cfg.disabled = 'disabled';
559 Roo.log('changing to ul' );
561 this.glyphicon = 'caret';
564 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
566 //gsRoo.log(this.parentType);
567 if (this.parentType === 'Navbar' && !this.parent().bar) {
568 Roo.log('changing to li?');
577 href : this.href || '#'
580 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
581 cfg.cls += ' dropdown';
588 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
590 if (this.glyphicon) {
591 cfg.html = ' ' + cfg.html;
596 cls: 'glyphicon glyphicon-' + this.glyphicon
606 // cfg.cls='btn roo-button';
610 var value = cfg.html;
615 cls: 'glyphicon glyphicon-' + this.glyphicon,
634 cfg.cls += ' dropdown';
635 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
638 if (cfg.tag !== 'a' && this.href !== '') {
639 throw "Tag must be a to set href.";
640 } else if (this.href.length > 0) {
641 cfg.href = this.href;
644 if(this.removeClass){
649 cfg.target = this.target;
654 initEvents: function() {
655 // Roo.log('init events?');
656 // Roo.log(this.el.dom);
657 if (this.el.hasClass('roo-button')) {
658 this.el.on('click', this.onClick, this);
660 this.el.select('.roo-button').on('click', this.onClick, this);
663 this.el.enableDisplayMode();
666 onClick : function(e)
672 Roo.log('button on click ');
673 if(this.preventDefault){
676 if (this.pressed === true || this.pressed === false) {
677 this.pressed = !this.pressed;
678 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679 this.fireEvent('toggle', this, e, this.pressed);
683 this.fireEvent('click', this, e);
687 * Enables this button
691 this.disabled = false;
692 this.el.removeClass('disabled');
696 * Disable this button
700 this.disabled = true;
701 this.el.addClass('disabled');
704 * sets the active state on/off,
705 * @param {Boolean} state (optional) Force a particular state
707 setActive : function(v) {
709 this.el[v ? 'addClass' : 'removeClass']('active');
712 * toggles the current active state
714 toggleActive : function()
716 var active = this.el.hasClass('active');
717 this.setActive(!active);
721 setText : function(str)
723 this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
746 * @class Roo.bootstrap.Column
747 * @extends Roo.bootstrap.Component
748 * Bootstrap Column class
749 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
750 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
751 * @cfg {Number} md colspan out of 12 for computer-sized screens
752 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
753 * @cfg {String} html content of column.
756 * Create a new Column
757 * @param {Object} config The config object
760 Roo.bootstrap.Column = function(config){
761 Roo.bootstrap.Column.superclass.constructor.call(this, config);
764 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
773 getAutoCreate : function(){
774 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
782 ['xs','sm','md','lg'].map(function(size){
783 if (settings[size]) {
784 cfg.cls += ' col-' + size + '-' + settings[size];
787 if (this.html.length) {
788 cfg.html = this.html;
807 * @class Roo.bootstrap.Container
808 * @extends Roo.bootstrap.Component
809 * Bootstrap Container class
810 * @cfg {Boolean} jumbotron is it a jumbotron element
811 * @cfg {String} html content of element
812 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
813 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
814 * @cfg {String} header content of header (for panel)
815 * @cfg {String} footer content of footer (for panel)
816 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
819 * Create a new Container
820 * @param {Object} config The config object
823 Roo.bootstrap.Container = function(config){
824 Roo.bootstrap.Container.superclass.constructor.call(this, config);
827 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
837 getChildContainer : function() {
843 if (this.panel.length) {
844 return this.el.select('.panel-body',true).first();
851 getAutoCreate : function(){
857 if (this.jumbotron) {
858 cfg.cls = 'jumbotron';
861 cfg.cls = this.cls + '';
864 if (this.sticky.length) {
866 var bd = Roo.get(document.body);
867 if (!bd.hasClass('bootstrap-sticky')) {
868 bd.addClass('bootstrap-sticky');
869 Roo.select('html',true).setStyle('height', '100%');
872 cfg.cls += 'bootstrap-sticky-' + this.sticky;
876 if (this.well.length) {
880 cfg.cls +=' well well-' +this.well;
890 if (this.panel.length) {
891 cfg.cls += ' panel panel-' + this.panel;
893 if (this.header.length) {
896 cls : 'panel-heading',
912 if (this.footer.length) {
914 cls : 'panel-footer',
922 body.html = this.html || cfg.html;
924 if (!cfg.cls.length) {
925 cfg.cls = 'container';
942 * @class Roo.bootstrap.Img
943 * @extends Roo.bootstrap.Component
944 * Bootstrap Img class
945 * @cfg {Boolean} imgResponsive false | true
946 * @cfg {String} border rounded | circle | thumbnail
947 * @cfg {String} src image source
948 * @cfg {String} alt image alternative text
949 * @cfg {String} href a tag href
950 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
954 * @param {Object} config The config object
957 Roo.bootstrap.Img = function(config){
958 Roo.bootstrap.Img.superclass.constructor.call(this, config);
964 * The img click event for the img.
965 * @param {Roo.EventObject} e
971 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
979 getAutoCreate : function(){
983 cls: (this.imgResponsive) ? 'img-responsive' : '',
987 cfg.html = this.html || cfg.html;
989 cfg.src = this.src || cfg.src;
991 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
992 cfg.cls += ' img-' + this.border;
1009 a.target = this.target;
1015 return (this.href) ? a : cfg;
1018 initEvents: function() {
1021 this.el.on('click', this.onClick, this);
1025 onClick : function(e)
1027 Roo.log('img onclick');
1028 this.fireEvent('click', this, e);
1041 * @class Roo.bootstrap.Header
1042 * @extends Roo.bootstrap.Component
1043 * Bootstrap Header class
1044 * @cfg {String} html content of header
1045 * @cfg {Number} level (1|2|3|4|5|6) default 1
1048 * Create a new Header
1049 * @param {Object} config The config object
1053 Roo.bootstrap.Header = function(config){
1054 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1057 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1065 getAutoCreate : function(){
1068 tag: 'h' + (1 *this.level),
1069 html: this.html || 'fill in html'
1081 * Ext JS Library 1.1.1
1082 * Copyright(c) 2006-2007, Ext JS, LLC.
1084 * Originally Released Under LGPL - original licence link has changed is not relivant.
1087 * <script type="text/javascript">
1091 * @class Roo.bootstrap.MenuMgr
1092 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1095 Roo.bootstrap.MenuMgr = function(){
1096 var menus, active, groups = {}, attached = false, lastShow = new Date();
1098 // private - called when first menu is created
1101 active = new Roo.util.MixedCollection();
1102 Roo.get(document).addKeyListener(27, function(){
1103 if(active.length > 0){
1111 if(active && active.length > 0){
1112 var c = active.clone();
1122 if(active.length < 1){
1123 Roo.get(document).un("mouseup", onMouseDown);
1131 var last = active.last();
1132 lastShow = new Date();
1135 Roo.get(document).on("mouseup", onMouseDown);
1140 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1141 m.parentMenu.activeChild = m;
1142 }else if(last && last.isVisible()){
1143 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1148 function onBeforeHide(m){
1150 m.activeChild.hide();
1152 if(m.autoHideTimer){
1153 clearTimeout(m.autoHideTimer);
1154 delete m.autoHideTimer;
1159 function onBeforeShow(m){
1160 var pm = m.parentMenu;
1161 if(!pm && !m.allowOtherMenus){
1163 }else if(pm && pm.activeChild && active != m){
1164 pm.activeChild.hide();
1169 function onMouseDown(e){
1170 Roo.log("on MouseDown");
1171 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1179 function onBeforeCheck(mi, state){
1181 var g = groups[mi.group];
1182 for(var i = 0, l = g.length; i < l; i++){
1184 g[i].setChecked(false);
1193 * Hides all menus that are currently visible
1195 hideAll : function(){
1200 register : function(menu){
1204 menus[menu.id] = menu;
1205 menu.on("beforehide", onBeforeHide);
1206 menu.on("hide", onHide);
1207 menu.on("beforeshow", onBeforeShow);
1208 menu.on("show", onShow);
1210 if(g && menu.events["checkchange"]){
1214 groups[g].push(menu);
1215 menu.on("checkchange", onCheck);
1220 * Returns a {@link Roo.menu.Menu} object
1221 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1222 * be used to generate and return a new Menu instance.
1224 get : function(menu){
1225 if(typeof menu == "string"){ // menu id
1227 }else if(menu.events){ // menu instance
1230 /*else if(typeof menu.length == 'number'){ // array of menu items?
1231 return new Roo.bootstrap.Menu({items:menu});
1232 }else{ // otherwise, must be a config
1233 return new Roo.bootstrap.Menu(menu);
1240 unregister : function(menu){
1241 delete menus[menu.id];
1242 menu.un("beforehide", onBeforeHide);
1243 menu.un("hide", onHide);
1244 menu.un("beforeshow", onBeforeShow);
1245 menu.un("show", onShow);
1247 if(g && menu.events["checkchange"]){
1248 groups[g].remove(menu);
1249 menu.un("checkchange", onCheck);
1254 registerCheckable : function(menuItem){
1255 var g = menuItem.group;
1260 groups[g].push(menuItem);
1261 menuItem.on("beforecheckchange", onBeforeCheck);
1266 unregisterCheckable : function(menuItem){
1267 var g = menuItem.group;
1269 groups[g].remove(menuItem);
1270 menuItem.un("beforecheckchange", onBeforeCheck);
1282 * @class Roo.bootstrap.Menu
1283 * @extends Roo.bootstrap.Component
1284 * Bootstrap Menu class - container for MenuItems
1285 * @cfg {String} type type of menu
1289 * @param {Object} config The config object
1293 Roo.bootstrap.Menu = function(config){
1294 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1295 if (this.registerMenu) {
1296 Roo.bootstrap.MenuMgr.register(this);
1301 * Fires before this menu is displayed
1302 * @param {Roo.menu.Menu} this
1307 * Fires before this menu is hidden
1308 * @param {Roo.menu.Menu} this
1313 * Fires after this menu is displayed
1314 * @param {Roo.menu.Menu} this
1319 * Fires after this menu is hidden
1320 * @param {Roo.menu.Menu} this
1325 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1326 * @param {Roo.menu.Menu} this
1327 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1328 * @param {Roo.EventObject} e
1333 * Fires when the mouse is hovering over this menu
1334 * @param {Roo.menu.Menu} this
1335 * @param {Roo.EventObject} e
1336 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1341 * Fires when the mouse exits this menu
1342 * @param {Roo.menu.Menu} this
1343 * @param {Roo.EventObject} e
1344 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1349 * Fires when a menu item contained in this menu is clicked
1350 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1351 * @param {Roo.EventObject} e
1355 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1358 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1362 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1365 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1367 registerMenu : true,
1369 menuItems :false, // stores the menu items..
1375 getChildContainer : function() {
1379 getAutoCreate : function(){
1381 //if (['right'].indexOf(this.align)!==-1) {
1382 // cfg.cn[1].cls += ' pull-right'
1386 cls : 'dropdown-menu' ,
1387 style : 'z-index:1000'
1391 if (this.type === 'submenu') {
1392 cfg.cls = 'submenu active'
1397 initEvents : function() {
1399 // Roo.log("ADD event");
1400 // Roo.log(this.triggerEl.dom);
1401 this.triggerEl.on('click', this.onTriggerPress, this);
1402 this.triggerEl.addClass('dropdown-toggle');
1403 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1405 this.el.on("mouseover", this.onMouseOver, this);
1406 this.el.on("mouseout", this.onMouseOut, this);
1410 findTargetItem : function(e){
1411 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1415 //Roo.log(t); Roo.log(t.id);
1417 //Roo.log(this.menuitems);
1418 return this.menuitems.get(t.id);
1420 //return this.items.get(t.menuItemId);
1425 onClick : function(e){
1426 Roo.log("menu.onClick");
1427 var t = this.findTargetItem(e);
1433 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1434 if(t == this.activeItem && t.shouldDeactivate(e)){
1435 this.activeItem.deactivate();
1436 delete this.activeItem;
1440 this.setActiveItem(t, true);
1447 Roo.log('pass click event');
1451 this.fireEvent("click", this, t, e);
1455 onMouseOver : function(e){
1456 var t = this.findTargetItem(e);
1459 // if(t.canActivate && !t.disabled){
1460 // this.setActiveItem(t, true);
1464 this.fireEvent("mouseover", this, e, t);
1466 isVisible : function(){
1467 return !this.hidden;
1469 onMouseOut : function(e){
1470 var t = this.findTargetItem(e);
1473 // if(t == this.activeItem && t.shouldDeactivate(e)){
1474 // this.activeItem.deactivate();
1475 // delete this.activeItem;
1478 this.fireEvent("mouseout", this, e, t);
1483 * Displays this menu relative to another element
1484 * @param {String/HTMLElement/Roo.Element} element The element to align to
1485 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1486 * the element (defaults to this.defaultAlign)
1487 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1489 show : function(el, pos, parentMenu){
1490 this.parentMenu = parentMenu;
1494 this.fireEvent("beforeshow", this);
1495 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1498 * Displays this menu at a specific xy position
1499 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1500 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1502 showAt : function(xy, parentMenu, /* private: */_e){
1503 this.parentMenu = parentMenu;
1508 this.fireEvent("beforeshow", this);
1510 //xy = this.el.adjustForConstraints(xy);
1512 //this.el.setXY(xy);
1514 this.hideMenuItems();
1515 this.hidden = false;
1516 this.triggerEl.addClass('open');
1518 this.fireEvent("show", this);
1524 this.doFocus.defer(50, this);
1528 doFocus : function(){
1530 this.focusEl.focus();
1535 * Hides this menu and optionally all parent menus
1536 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1538 hide : function(deep){
1540 this.hideMenuItems();
1541 if(this.el && this.isVisible()){
1542 this.fireEvent("beforehide", this);
1543 if(this.activeItem){
1544 this.activeItem.deactivate();
1545 this.activeItem = null;
1547 this.triggerEl.removeClass('open');;
1549 this.fireEvent("hide", this);
1551 if(deep === true && this.parentMenu){
1552 this.parentMenu.hide(true);
1556 onTriggerPress : function(e)
1559 Roo.log('trigger press');
1560 //Roo.log(e.getTarget());
1561 // Roo.log(this.triggerEl.dom);
1562 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1565 if (this.isVisible()) {
1569 this.show(this.triggerEl, false, false);
1578 hideMenuItems : function()
1580 //$(backdrop).remove()
1581 Roo.select('.open',true).each(function(aa) {
1583 aa.removeClass('open');
1584 //var parent = getParent($(this))
1585 //var relatedTarget = { relatedTarget: this }
1587 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1588 //if (e.isDefaultPrevented()) return
1589 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1592 addxtypeChild : function (tree, cntr) {
1593 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1595 this.menuitems.add(comp);
1616 * @class Roo.bootstrap.MenuItem
1617 * @extends Roo.bootstrap.Component
1618 * Bootstrap MenuItem class
1619 * @cfg {String} html the menu label
1620 * @cfg {String} href the link
1621 * @cfg {Boolean} preventDefault (true | false) default true
1625 * Create a new MenuItem
1626 * @param {Object} config The config object
1630 Roo.bootstrap.MenuItem = function(config){
1631 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1636 * The raw click event for the entire grid.
1637 * @param {Roo.EventObject} e
1643 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1647 preventDefault: true,
1649 getAutoCreate : function(){
1652 cls: 'dropdown-menu-item',
1662 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1663 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1667 initEvents: function() {
1669 //this.el.select('a').on('click', this.onClick, this);
1672 onClick : function(e)
1674 Roo.log('item on click ');
1675 //if(this.preventDefault){
1676 // e.preventDefault();
1678 //this.parent().hideMenuItems();
1680 this.fireEvent('click', this, e);
1699 * @class Roo.bootstrap.MenuSeparator
1700 * @extends Roo.bootstrap.Component
1701 * Bootstrap MenuSeparator class
1704 * Create a new MenuItem
1705 * @param {Object} config The config object
1709 Roo.bootstrap.MenuSeparator = function(config){
1710 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1713 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1715 getAutoCreate : function(){
1730 <div class="modal fade">
1731 <div class="modal-dialog">
1732 <div class="modal-content">
1733 <div class="modal-header">
1734 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1735 <h4 class="modal-title">Modal title</h4>
1737 <div class="modal-body">
1738 <p>One fine body…</p>
1740 <div class="modal-footer">
1741 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1742 <button type="button" class="btn btn-primary">Save changes</button>
1744 </div><!-- /.modal-content -->
1745 </div><!-- /.modal-dialog -->
1746 </div><!-- /.modal -->
1756 * @class Roo.bootstrap.Modal
1757 * @extends Roo.bootstrap.Component
1758 * Bootstrap Modal class
1759 * @cfg {String} title Title of dialog
1760 * @cfg {Boolean} specificTitle (true|false) default false
1761 * @cfg {Array} buttons Array of buttons or standard button set..
1762 * @cfg {String} buttonPosition (left|right|center) default right
1765 * Create a new Modal Dialog
1766 * @param {Object} config The config object
1769 Roo.bootstrap.Modal = function(config){
1770 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1775 * The raw btnclick event for the button
1776 * @param {Roo.EventObject} e
1780 this.buttons = this.buttons || [];
1783 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1785 title : 'test dialog',
1792 specificTitle: false,
1794 buttonPosition: 'right',
1796 onRender : function(ct, position)
1798 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1801 var cfg = Roo.apply({}, this.getAutoCreate());
1804 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1806 //if (!cfg.name.length) {
1810 cfg.cls += ' ' + this.cls;
1813 cfg.style = this.style;
1815 this.el = Roo.get(document.body).createChild(cfg, position);
1817 //var type = this.el.dom.type;
1819 if(this.tabIndex !== undefined){
1820 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1825 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1826 this.maskEl.enableDisplayMode("block");
1828 //this.el.addClass("x-dlg-modal");
1830 if (this.buttons.length) {
1831 Roo.each(this.buttons, function(bb) {
1832 b = Roo.apply({}, bb);
1833 b.xns = b.xns || Roo.bootstrap;
1834 b.xtype = b.xtype || 'Button';
1835 if (typeof(b.listeners) == 'undefined') {
1836 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1839 var btn = Roo.factory(b);
1841 btn.onRender(this.el.select('.modal-footer div').first());
1845 // render the children.
1848 if(typeof(this.items) != 'undefined'){
1849 var items = this.items;
1852 for(var i =0;i < items.length;i++) {
1853 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1857 this.items = nitems;
1859 this.body = this.el.select('.modal-body',true).first();
1860 this.close = this.el.select('.modal-header .close', true).first();
1861 this.footer = this.el.select('.modal-footer',true).first();
1863 //this.el.addClass([this.fieldClass, this.cls]);
1866 getAutoCreate : function(){
1871 html : this.html || ''
1876 cls : 'modal-title',
1880 if(this.specificTitle){
1886 style : 'display: none',
1889 cls: "modal-dialog",
1892 cls : "modal-content",
1895 cls : 'modal-header',
1907 cls : 'modal-footer',
1911 cls: 'btn-' + this.buttonPosition
1930 getChildContainer : function() {
1932 return this.el.select('.modal-body',true).first();
1935 getButtonContainer : function() {
1936 return this.el.select('.modal-footer div',true).first();
1939 initEvents : function()
1941 this.el.select('.modal-header .close').on('click', this.hide, this);
1943 // this.addxtype(this);
1947 if (!this.rendered) {
1951 this.el.addClass('on');
1952 this.el.removeClass('fade');
1953 this.el.setStyle('display', 'block');
1954 Roo.get(document.body).addClass("x-body-masked");
1955 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1957 this.el.setStyle('zIndex', '10001');
1958 this.fireEvent('show', this);
1964 Roo.log('Modal hide?!');
1966 Roo.get(document.body).removeClass("x-body-masked");
1967 this.el.removeClass('on');
1968 this.el.addClass('fade');
1969 this.el.setStyle('display', 'none');
1970 this.fireEvent('hide', this);
1973 addButton : function(str, cb)
1977 var b = Roo.apply({}, { html : str } );
1978 b.xns = b.xns || Roo.bootstrap;
1979 b.xtype = b.xtype || 'Button';
1980 if (typeof(b.listeners) == 'undefined') {
1981 b.listeners = { click : cb.createDelegate(this) };
1984 var btn = Roo.factory(b);
1986 btn.onRender(this.el.select('.modal-footer div').first());
1992 setDefaultButton : function(btn)
1994 //this.el.select('.modal-footer').()
1996 resizeTo: function(w,h)
2000 setContentSize : function(w, h)
2004 onButtonClick: function(btn,e)
2007 this.fireEvent('btnclick', btn.name, e);
2009 setTitle: function(str) {
2010 this.el.select('.modal-title',true).first().dom.innerHTML = str;
2016 Roo.apply(Roo.bootstrap.Modal, {
2018 * Button config that displays a single OK button
2027 * Button config that displays Yes and No buttons
2043 * Button config that displays OK and Cancel buttons
2058 * Button config that displays Yes, No and Cancel buttons
2080 * messagebox - can be used as a replace
2084 * @class Roo.MessageBox
2085 * Utility class for generating different styles of message boxes. The alias Roo.Msg can also be used.
2089 Roo.Msg.alert('Status', 'Changes saved successfully.');
2091 // Prompt for user data:
2092 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2094 // process text value...
2098 // Show a dialog using config options:
2100 title:'Save Changes?',
2101 msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2102 buttons: Roo.Msg.YESNOCANCEL,
2109 Roo.bootstrap.MessageBox = function(){
2110 var dlg, opt, mask, waitTimer;
2111 var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2112 var buttons, activeTextEl, bwidth;
2116 var handleButton = function(button){
2118 Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2122 var handleHide = function(){
2124 dlg.el.removeClass(opt.cls);
2127 // Roo.TaskMgr.stop(waitTimer);
2128 // waitTimer = null;
2133 var updateButtons = function(b){
2136 buttons["ok"].hide();
2137 buttons["cancel"].hide();
2138 buttons["yes"].hide();
2139 buttons["no"].hide();
2140 //dlg.footer.dom.style.display = 'none';
2143 dlg.footer.dom.style.display = '';
2144 for(var k in buttons){
2145 if(typeof buttons[k] != "function"){
2148 buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2149 width += buttons[k].el.getWidth()+15;
2159 var handleEsc = function(d, k, e){
2160 if(opt && opt.closable !== false){
2170 * Returns a reference to the underlying {@link Roo.BasicDialog} element
2171 * @return {Roo.BasicDialog} The BasicDialog element
2173 getDialog : function(){
2175 dlg = new Roo.bootstrap.Modal( {
2178 //constraintoviewport:false,
2180 //collapsible : false,
2185 //buttonAlign:"center",
2186 closeClick : function(){
2187 if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
2190 handleButton("cancel");
2195 dlg.on("hide", handleHide);
2197 //dlg.addKeyListener(27, handleEsc);
2199 this.buttons = buttons;
2200 var bt = this.buttonText;
2201 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
2202 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
2203 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
2204 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
2206 bodyEl = dlg.body.createChild({
2208 html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
2209 '<textarea class="roo-mb-textarea"></textarea>' +
2210 '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar"> </div></div></div>'
2212 msgEl = bodyEl.dom.firstChild;
2213 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
2214 textboxEl.enableDisplayMode();
2215 textboxEl.addKeyListener([10,13], function(){
2216 if(dlg.isVisible() && opt && opt.buttons){
2219 }else if(opt.buttons.yes){
2220 handleButton("yes");
2224 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
2225 textareaEl.enableDisplayMode();
2226 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
2227 progressEl.enableDisplayMode();
2228 var pf = progressEl.dom.firstChild;
2230 pp = Roo.get(pf.firstChild);
2231 pp.setHeight(pf.offsetHeight);
2239 * Updates the message box body text
2240 * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
2241 * the XHTML-compliant non-breaking space character '&#160;')
2242 * @return {Roo.MessageBox} This message box
2244 updateText : function(text){
2245 if(!dlg.isVisible() && !opt.width){
2246 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
2248 msgEl.innerHTML = text || ' ';
2250 var cw = Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
2251 //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
2253 Math.min(opt.width || cw , this.maxWidth),
2254 Math.max(opt.minWidth || this.minWidth, bwidth)
2257 activeTextEl.setWidth(w);
2259 if(dlg.isVisible()){
2260 dlg.fixedcenter = false;
2262 // to big, make it scroll. = But as usual stupid IE does not support
2265 if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
2266 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
2267 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
2269 bodyEl.dom.style.height = '';
2270 bodyEl.dom.style.overflowY = '';
2273 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
2275 bodyEl.dom.style.overflowX = '';
2278 dlg.setContentSize(w, bodyEl.getHeight());
2279 if(dlg.isVisible()){
2280 dlg.fixedcenter = true;
2286 * Updates a progress-style message box's text and progress bar. Only relevant on message boxes
2287 * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
2288 * @param {Number} value Any number between 0 and 1 (e.g., .5)
2289 * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
2290 * @return {Roo.MessageBox} This message box
2292 updateProgress : function(value, text){
2294 this.updateText(text);
2296 if (pp) { // weird bug on my firefox - for some reason this is not defined
2297 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
2303 * Returns true if the message box is currently displayed
2304 * @return {Boolean} True if the message box is visible, else false
2306 isVisible : function(){
2307 return dlg && dlg.isVisible();
2311 * Hides the message box if it is displayed
2314 if(this.isVisible()){
2320 * Displays a new message box, or reinitializes an existing message box, based on the config options
2321 * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
2322 * The following config object properties are supported:
2324 Property Type Description
2325 ---------- --------------- ------------------------------------------------------------------------------------
2326 animEl String/Element An id or Element from which the message box should animate as it opens and
2327 closes (defaults to undefined)
2328 buttons Object/Boolean A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
2329 cancel:'Bar'}), or false to not show any buttons (defaults to false)
2330 closable Boolean False to hide the top-right close button (defaults to true). Note that
2331 progress and wait dialogs will ignore this property and always hide the
2332 close button as they can only be closed programmatically.
2333 cls String A custom CSS class to apply to the message box element
2334 defaultTextHeight Number The default height in pixels of the message box's multiline textarea if
2335 displayed (defaults to 75)
2336 fn Function A callback function to execute after closing the dialog. The arguments to the
2337 function will be btn (the name of the button that was clicked, if applicable,
2338 e.g. "ok"), and text (the value of the active text field, if applicable).
2339 Progress and wait dialogs will ignore this option since they do not respond to
2340 user actions and can only be closed programmatically, so any required function
2341 should be called by the same code after it closes the dialog.
2342 icon String A CSS class that provides a background image to be used as an icon for
2343 the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
2344 maxWidth Number The maximum width in pixels of the message box (defaults to 600)
2345 minWidth Number The minimum width in pixels of the message box (defaults to 100)
2346 modal Boolean False to allow user interaction with the page while the message box is
2347 displayed (defaults to true)
2348 msg String A string that will replace the existing message box body text (defaults
2349 to the XHTML-compliant non-breaking space character ' ')
2350 multiline Boolean True to prompt the user to enter multi-line text (defaults to false)
2351 progress Boolean True to display a progress bar (defaults to false)
2352 progressText String The text to display inside the progress bar if progress = true (defaults to '')
2353 prompt Boolean True to prompt the user to enter single-line text (defaults to false)
2354 proxyDrag Boolean True to display a lightweight proxy while dragging (defaults to false)
2355 title String The title text
2356 value String The string value to set into the active textbox element if displayed
2357 wait Boolean True to display a progress bar (defaults to false)
2358 width Number The width of the dialog in pixels
2365 msg: 'Please enter your address:',
2367 buttons: Roo.MessageBox.OKCANCEL,
2370 animEl: 'addAddressBtn'
2373 * @param {Object} config Configuration options
2374 * @return {Roo.MessageBox} This message box
2376 show : function(options)
2379 // this causes nightmares if you show one dialog after another
2380 // especially on callbacks..
2382 if(this.isVisible()){
2385 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
2386 Roo.log("Old Dialog Message:" + msgEl.innerHTML );
2387 Roo.log("New Dialog Message:" + options.msg )
2388 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
2389 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
2392 var d = this.getDialog();
2394 d.setTitle(opt.title || " ");
2395 d.close.setDisplayed(opt.closable !== false);
2396 activeTextEl = textboxEl;
2397 opt.prompt = opt.prompt || (opt.multiline ? true : false);
2402 textareaEl.setHeight(typeof opt.multiline == "number" ?
2403 opt.multiline : this.defaultTextHeight);
2404 activeTextEl = textareaEl;
2413 progressEl.setDisplayed(opt.progress === true);
2414 this.updateProgress(0);
2415 activeTextEl.dom.value = opt.value || "";
2417 dlg.setDefaultButton(activeTextEl);
2419 var bs = opt.buttons;
2423 }else if(bs && bs.yes){
2424 db = buttons["yes"];
2426 dlg.setDefaultButton(db);
2428 bwidth = updateButtons(opt.buttons);
2429 this.updateText(opt.msg);
2431 d.el.addClass(opt.cls);
2433 d.proxyDrag = opt.proxyDrag === true;
2434 d.modal = opt.modal !== false;
2435 d.mask = opt.modal !== false ? mask : false;
2437 // force it to the end of the z-index stack so it gets a cursor in FF
2438 document.body.appendChild(dlg.el.dom);
2439 d.animateTarget = null;
2440 d.show(options.animEl);
2446 * Displays a message box with a progress bar. This message box has no buttons and is not closeable by
2447 * the user. You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
2448 * and closing the message box when the process is complete.
2449 * @param {String} title The title bar text
2450 * @param {String} msg The message box body text
2451 * @return {Roo.MessageBox} This message box
2453 progress : function(title, msg){
2460 minWidth: this.minProgressWidth,
2467 * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
2468 * If a callback function is passed it will be called after the user clicks the button, and the
2469 * id of the button that was clicked will be passed as the only parameter to the callback
2470 * (could also be the top-right close button).
2471 * @param {String} title The title bar text
2472 * @param {String} msg The message box body text
2473 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2474 * @param {Object} scope (optional) The scope of the callback function
2475 * @return {Roo.MessageBox} This message box
2477 alert : function(title, msg, fn, scope){
2490 * Displays a message box with an infinitely auto-updating progress bar. This can be used to block user
2491 * interaction while waiting for a long-running process to complete that does not have defined intervals.
2492 * You are responsible for closing the message box when the process is complete.
2493 * @param {String} msg The message box body text
2494 * @param {String} title (optional) The title bar text
2495 * @return {Roo.MessageBox} This message box
2497 wait : function(msg, title){
2508 waitTimer = Roo.TaskMgr.start({
2510 Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
2518 * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
2519 * If a callback function is passed it will be called after the user clicks either button, and the id of the
2520 * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
2521 * @param {String} title The title bar text
2522 * @param {String} msg The message box body text
2523 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2524 * @param {Object} scope (optional) The scope of the callback function
2525 * @return {Roo.MessageBox} This message box
2527 confirm : function(title, msg, fn, scope){
2531 buttons: this.YESNO,
2540 * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
2541 * JavaScript's Window.prompt). The prompt can be a single-line or multi-line textbox. If a callback function
2542 * is passed it will be called after the user clicks either button, and the id of the button that was clicked
2543 * (could also be the top-right close button) and the text that was entered will be passed as the two
2544 * parameters to the callback.
2545 * @param {String} title The title bar text
2546 * @param {String} msg The message box body text
2547 * @param {Function} fn (optional) The callback function invoked after the message box is closed
2548 * @param {Object} scope (optional) The scope of the callback function
2549 * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
2550 * property, or the height in pixels to create the textbox (defaults to false / single-line)
2551 * @return {Roo.MessageBox} This message box
2553 prompt : function(title, msg, fn, scope, multiline){
2557 buttons: this.OKCANCEL,
2562 multiline: multiline,
2569 * Button config that displays a single OK button
2574 * Button config that displays Yes and No buttons
2577 YESNO : {yes:true, no:true},
2579 * Button config that displays OK and Cancel buttons
2582 OKCANCEL : {ok:true, cancel:true},
2584 * Button config that displays Yes, No and Cancel buttons
2587 YESNOCANCEL : {yes:true, no:true, cancel:true},
2590 * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
2593 defaultTextHeight : 75,
2595 * The maximum width in pixels of the message box (defaults to 600)
2600 * The minimum width in pixels of the message box (defaults to 100)
2605 * The minimum width in pixels of the message box if it is a progress-style dialog. This is useful
2606 * for setting a different minimum width than text-only dialogs may need (defaults to 250)
2609 minProgressWidth : 250,
2611 * An object containing the default button text strings that can be overriden for localized language support.
2612 * Supported properties are: ok, cancel, yes and no.
2613 * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
2626 * Shorthand for {@link Roo.MessageBox}
2628 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox
2629 Roo.Msg = Roo.Msg || Roo.MessageBox;
2638 * @class Roo.bootstrap.Navbar
2639 * @extends Roo.bootstrap.Component
2640 * Bootstrap Navbar class
2641 * @cfg {Boolean} sidebar has side bar
2642 * @cfg {Boolean} bar is a bar?
2643 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2644 * @cfg {String} brand what is brand
2645 * @cfg {Boolean} inverse is inverted color
2646 * @cfg {String} type (nav | pills | tabs)
2647 * @cfg {Boolean} arrangement stacked | justified
2648 * @cfg {String} align (left | right) alignment
2649 * @cfg {String} brand_href href of the brand
2650 * @cfg {Boolean} main (true|false) main nav bar? default false
2651 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2655 * Create a new Navbar
2656 * @param {Object} config The config object
2660 Roo.bootstrap.Navbar = function(config){
2661 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2666 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2685 getAutoCreate : function(){
2690 if (this.sidebar === true) {
2698 if (this.bar === true) {
2706 cls: 'navbar-header',
2711 cls: 'navbar-toggle',
2712 'data-toggle': 'collapse',
2717 html: 'Toggle navigation'
2737 cls: 'collapse navbar-collapse'
2742 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2744 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2745 cfg.cls += ' navbar-' + this.position;
2746 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2749 if (this.brand !== '') {
2752 href: this.brand_href ? this.brand_href : '#',
2753 cls: 'navbar-brand',
2761 cfg.cls += ' main-nav';
2767 } else if (this.bar === false) {
2770 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2780 if (['tabs','pills'].indexOf(this.type)!==-1) {
2781 cfg.cn[0].cls += ' nav-' + this.type
2783 if (this.type!=='nav') {
2784 Roo.log('nav type must be nav/tabs/pills')
2786 cfg.cn[0].cls += ' navbar-nav'
2789 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2790 cfg.cn[0].cls += ' nav-' + this.arrangement;
2793 if (this.align === 'right') {
2794 cfg.cn[0].cls += ' navbar-right';
2797 cfg.cls += ' navbar-inverse';
2805 initEvents :function ()
2807 //Roo.log(this.el.select('.navbar-toggle',true));
2808 this.el.select('.navbar-toggle',true).on('click', function() {
2809 // Roo.log('click');
2810 this.el.select('.navbar-collapse',true).toggleClass('in');
2818 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2820 var size = this.el.getSize();
2821 this.maskEl.setSize(size.width, size.height);
2822 this.maskEl.enableDisplayMode("block");
2831 getChildContainer : function()
2833 if (this.bar === true) {
2834 return this.el.select('.collapse',true).first();
2866 * @class Roo.bootstrap.NavGroup
2867 * @extends Roo.bootstrap.Component
2868 * Bootstrap NavGroup class
2869 * @cfg {String} align left | right
2870 * @cfg {Boolean} inverse false | true
2871 * @cfg {String} type (nav|pills|tab) default nav
2872 * @cfg {String} navId - reference Id for navbar.
2876 * Create a new nav group
2877 * @param {Object} config The config object
2880 Roo.bootstrap.NavGroup = function(config){
2881 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2883 Roo.bootstrap.NavGroup.register(this);
2887 * Fires when the active item changes
2888 * @param {Roo.bootstrap.NavGroup} this
2889 * @param {Roo.bootstrap.Navbar.Item} item The item selected
2890 * @param {Roo.bootstrap.Navbar.Item} item The previously selected item
2897 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2908 getAutoCreate : function()
2910 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2917 if (['tabs','pills'].indexOf(this.type)!==-1) {
2918 cfg.cls += ' nav-' + this.type
2920 if (this.type!=='nav') {
2921 Roo.log('nav type must be nav/tabs/pills')
2923 cfg.cls += ' navbar-nav'
2926 if (this.parent().sidebar === true) {
2929 cls: 'dashboard-menu'
2935 if (this.form === true) {
2941 if (this.align === 'right') {
2942 cfg.cls += ' navbar-right';
2944 cfg.cls += ' navbar-left';
2948 if (this.align === 'right') {
2949 cfg.cls += ' navbar-right';
2953 cfg.cls += ' navbar-inverse';
2961 setActiveItem : function(item)
2964 Roo.each(this.navItems, function(v){
2969 v.setActive(false, true);
2976 item.setActive(true, true);
2977 this.fireEvent('changed', this, item, prev);
2983 register : function(item)
2985 this.navItems.push( item);
2986 item.navId = this.navId;
2989 getNavItem: function(tabId)
2992 Roo.each(this.navItems, function(e) {
2993 if (e.tabId == tabId) {
3005 Roo.apply(Roo.bootstrap.NavGroup, {
3009 register : function(navgrp)
3011 this.groups[navgrp.navId] = navgrp;
3014 get: function(navId) {
3015 return this.groups[navId];
3030 * @class Roo.bootstrap.Navbar.Item
3031 * @extends Roo.bootstrap.Component
3032 * Bootstrap Navbar.Button class
3033 * @cfg {String} href link to
3034 * @cfg {String} html content of button
3035 * @cfg {String} badge text inside badge
3036 * @cfg {String} glyphicon name of glyphicon
3037 * @cfg {String} icon name of font awesome icon
3038 * @cfg {Boolean} active Is item active
3039 * @cfg {Boolean} preventDefault (true | false) default false
3040 * @cfg {String} tabId the tab that this item activates.
3043 * Create a new Navbar Button
3044 * @param {Object} config The config object
3046 Roo.bootstrap.Navbar.Item = function(config){
3047 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
3052 * The raw click event for the entire grid.
3053 * @param {Roo.EventObject} e
3058 * Fires when the active item active state changes
3059 * @param {Roo.bootstrap.Navbar.Item} this
3060 * @param {boolean} state the new state
3068 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
3076 preventDefault : false,
3079 getAutoCreate : function(){
3081 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
3083 if (this.parent().parent().sidebar === true) {
3096 cfg.cn[0].html = this.html;
3100 this.cls += ' active';
3104 cfg.cn[0].cls += ' dropdown-toggle';
3105 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
3109 cfg.cn[0].tag = 'a',
3110 cfg.cn[0].href = this.href;
3113 if (this.glyphicon) {
3114 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3118 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3130 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
3140 if (this.glyphicon) {
3141 if(cfg.html){cfg.html = ' ' + this.html};
3145 cls: 'glyphicon glyphicon-' + this.glyphicon
3150 cfg.cn[0].html = this.html || cfg.cn[0].html ;
3155 cfg.cn[0].html += " <span class='caret'></span>";
3156 //}else if (!this.href) {
3157 // cfg.cn[0].tag='p';
3158 // cfg.cn[0].cls='navbar-text';
3161 cfg.cn[0].href=this.href||'#';
3162 cfg.cn[0].html=this.html;
3165 if (this.badge !== '') {
3168 cfg.cn[0].html + ' ',
3179 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
3184 initEvents: function() {
3185 // Roo.log('init events?');
3186 // Roo.log(this.el.dom);
3187 this.el.select('a',true).on('click', this.onClick, this);
3188 // at this point parent should be available..
3189 this.parent().register(this);
3192 onClick : function(e)
3194 if(this.preventDefault){
3198 if(this.fireEvent('click', this, e) === false){
3202 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
3203 if (typeof(this.parent().setActiveItem) !== 'undefined') {
3204 this.parent().setActiveItem(this);
3212 isActive: function () {
3215 setActive : function(state, fire)
3217 this.active = state;
3219 this.el.removeClass('active');
3220 } else if (!this.el.hasClass('active')) {
3221 this.el.addClass('active');
3224 this.fireEvent('changed', this, state);
3229 // this should not be here...
3242 * @class Roo.bootstrap.Row
3243 * @extends Roo.bootstrap.Component
3244 * Bootstrap Row class (contains columns...)
3248 * @param {Object} config The config object
3251 Roo.bootstrap.Row = function(config){
3252 Roo.bootstrap.Row.superclass.constructor.call(this, config);
3255 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
3257 getAutoCreate : function(){
3276 * @class Roo.bootstrap.Element
3277 * @extends Roo.bootstrap.Component
3278 * Bootstrap Element class
3279 * @cfg {String} html contents of the element
3280 * @cfg {String} tag tag of the element
3281 * @cfg {String} cls class of the element
3284 * Create a new Element
3285 * @param {Object} config The config object
3288 Roo.bootstrap.Element = function(config){
3289 Roo.bootstrap.Element.superclass.constructor.call(this, config);
3292 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
3299 getAutoCreate : function(){
3324 * @class Roo.bootstrap.Pagination
3325 * @extends Roo.bootstrap.Component
3326 * Bootstrap Pagination class
3327 * @cfg {String} size xs | sm | md | lg
3328 * @cfg {Boolean} inverse false | true
3331 * Create a new Pagination
3332 * @param {Object} config The config object
3335 Roo.bootstrap.Pagination = function(config){
3336 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
3339 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
3345 getAutoCreate : function(){
3351 cfg.cls += ' inverse';
3357 cfg.cls += " " + this.cls;
3375 * @class Roo.bootstrap.PaginationItem
3376 * @extends Roo.bootstrap.Component
3377 * Bootstrap PaginationItem class
3378 * @cfg {String} html text
3379 * @cfg {String} href the link
3380 * @cfg {Boolean} preventDefault (true | false) default true
3381 * @cfg {Boolean} active (true | false) default false
3385 * Create a new PaginationItem
3386 * @param {Object} config The config object
3390 Roo.bootstrap.PaginationItem = function(config){
3391 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
3396 * The raw click event for the entire grid.
3397 * @param {Roo.EventObject} e
3403 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
3407 preventDefault: true,
3411 getAutoCreate : function(){
3417 href : this.href ? this.href : '#',
3418 html : this.html ? this.html : ''
3428 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
3434 initEvents: function() {
3436 this.el.on('click', this.onClick, this);
3439 onClick : function(e)
3441 Roo.log('PaginationItem on click ');
3442 if(this.preventDefault){
3446 this.fireEvent('click', this, e);
3462 * @class Roo.bootstrap.Slider
3463 * @extends Roo.bootstrap.Component
3464 * Bootstrap Slider class
3467 * Create a new Slider
3468 * @param {Object} config The config object
3471 Roo.bootstrap.Slider = function(config){
3472 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
3475 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
3477 getAutoCreate : function(){
3481 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
3485 cls: 'ui-slider-handle ui-state-default ui-corner-all'
3503 * @class Roo.bootstrap.Table
3504 * @extends Roo.bootstrap.Component
3505 * Bootstrap Table class
3506 * @cfg {String} cls table class
3507 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
3508 * @cfg {String} bgcolor Specifies the background color for a table
3509 * @cfg {Number} border Specifies whether the table cells should have borders or not
3510 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
3511 * @cfg {Number} cellspacing Specifies the space between cells
3512 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
3513 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
3514 * @cfg {String} sortable Specifies that the table should be sortable
3515 * @cfg {String} summary Specifies a summary of the content of a table
3516 * @cfg {Number} width Specifies the width of a table
3518 * @cfg {boolean} striped Should the rows be alternative striped
3519 * @cfg {boolean} bordered Add borders to the table
3520 * @cfg {boolean} hover Add hover highlighting
3521 * @cfg {boolean} condensed Format condensed
3522 * @cfg {boolean} responsive Format condensed
3528 * Create a new Table
3529 * @param {Object} config The config object
3532 Roo.bootstrap.Table = function(config){
3533 Roo.bootstrap.Table.superclass.constructor.call(this, config);
3536 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
3537 this.sm = this.selModel;
3538 this.sm.xmodule = this.xmodule || false;
3540 if (this.cm && typeof(this.cm.config) == 'undefined') {
3541 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
3542 this.cm = this.colModel;
3543 this.cm.xmodule = this.xmodule || false;
3546 this.store= Roo.factory(this.store, Roo.data);
3547 this.ds = this.store;
3548 this.ds.xmodule = this.xmodule || false;
3553 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
3575 getAutoCreate : function(){
3576 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
3585 cfg.cls += ' table-striped';
3588 cfg.cls += ' table-hover';
3590 if (this.bordered) {
3591 cfg.cls += ' table-bordered';
3593 if (this.condensed) {
3594 cfg.cls += ' table-condensed';
3596 if (this.responsive) {
3597 cfg.cls += ' table-responsive';
3604 cfg.cls+= ' ' +this.cls;
3607 // this lot should be simplifed...
3610 cfg.align=this.align;
3613 cfg.bgcolor=this.bgcolor;
3616 cfg.border=this.border;
3618 if (this.cellpadding) {
3619 cfg.cellpadding=this.cellpadding;
3621 if (this.cellspacing) {
3622 cfg.cellspacing=this.cellspacing;
3625 cfg.frame=this.frame;
3628 cfg.rules=this.rules;
3630 if (this.sortable) {
3631 cfg.sortable=this.sortable;
3634 cfg.summary=this.summary;
3637 cfg.width=this.width;
3640 if(this.store || this.cm){
3641 cfg.cn.push(this.renderHeader());
3642 cfg.cn.push(this.renderBody());
3643 cfg.cn.push(this.renderFooter());
3645 cfg.cls+= ' TableGrid';
3651 // initTableGrid : function()
3660 // var cm = this.cm;
3662 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3665 // html: cm.getColumnHeader(i)
3669 // cfg.push(header);
3676 initEvents : function()
3678 if(!this.store || !this.cm){
3682 Roo.log('initEvents with ds!!!!');
3686 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3687 e.on('click', _this.sort, _this);
3689 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3690 // this.maskEl.enableDisplayMode("block");
3691 // this.maskEl.show();
3693 this.store.on('load', this.onLoad, this);
3694 this.store.on('beforeload', this.onBeforeLoad, this);
3702 sort : function(e,el)
3704 var col = Roo.get(el)
3706 if(!col.hasClass('sortable')){
3710 var sort = col.attr('sort');
3713 if(col.hasClass('glyphicon-arrow-up')){
3717 this.store.sortInfo = {field : sort, direction : dir};
3722 renderHeader : function()
3731 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3733 var config = cm.config[i];
3737 html: cm.getColumnHeader(i)
3740 if(typeof(config.dataIndex) != 'undefined'){
3741 c.sort = config.dataIndex;
3744 if(typeof(config.sortable) != 'undefined' && config.sortable){
3748 if(typeof(config.width) != 'undefined'){
3749 c.style = 'width:' + config.width + 'px';
3758 renderBody : function()
3768 renderFooter : function()
3780 Roo.log('ds onload');
3785 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3786 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3788 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3789 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3792 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3793 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3797 var tbody = this.el.select('tbody', true).first();
3801 if(this.store.getCount() > 0){
3802 this.store.data.each(function(d){
3808 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3809 var renderer = cm.getRenderer(i);
3810 var config = cm.config[i];
3814 if(typeof(renderer) !== 'undefined'){
3815 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3818 if(typeof(value) === 'object'){
3828 html: (typeof(value) === 'object') ? '' : value
3831 if(typeof(config.width) != 'undefined'){
3832 td.style = 'width:' + config.width + 'px';
3839 tbody.createChild(row);
3847 Roo.each(renders, function(r){
3848 _this.renderColumn(r);
3852 // if(this.loadMask){
3853 // this.maskEl.hide();
3857 onBeforeLoad : function()
3859 Roo.log('ds onBeforeLoad');
3863 // if(this.loadMask){
3864 // this.maskEl.show();
3870 this.el.select('tbody', true).first().dom.innerHTML = '';
3873 getSelectionModel : function(){
3875 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3877 return this.selModel;
3880 renderColumn : function(r)
3883 r.cfg.render(Roo.get(r.id));
3886 Roo.each(r.cfg.cn, function(c){
3891 _this.renderColumn(child);
3908 * @class Roo.bootstrap.TableCell
3909 * @extends Roo.bootstrap.Component
3910 * Bootstrap TableCell class
3911 * @cfg {String} html cell contain text
3912 * @cfg {String} cls cell class
3913 * @cfg {String} tag cell tag (td|th) default td
3914 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3915 * @cfg {String} align Aligns the content in a cell
3916 * @cfg {String} axis Categorizes cells
3917 * @cfg {String} bgcolor Specifies the background color of a cell
3918 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3919 * @cfg {Number} colspan Specifies the number of columns a cell should span
3920 * @cfg {String} headers Specifies one or more header cells a cell is related to
3921 * @cfg {Number} height Sets the height of a cell
3922 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3923 * @cfg {Number} rowspan Sets the number of rows a cell should span
3924 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3925 * @cfg {String} valign Vertical aligns the content in a cell
3926 * @cfg {Number} width Specifies the width of a cell
3929 * Create a new TableCell
3930 * @param {Object} config The config object
3933 Roo.bootstrap.TableCell = function(config){
3934 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3937 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3957 getAutoCreate : function(){
3958 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3978 cfg.align=this.align
3984 cfg.bgcolor=this.bgcolor
3987 cfg.charoff=this.charoff
3990 cfg.colspan=this.colspan
3993 cfg.headers=this.headers
3996 cfg.height=this.height
3999 cfg.nowrap=this.nowrap
4002 cfg.rowspan=this.rowspan
4005 cfg.scope=this.scope
4008 cfg.valign=this.valign
4011 cfg.width=this.width
4030 * @class Roo.bootstrap.TableRow
4031 * @extends Roo.bootstrap.Component
4032 * Bootstrap TableRow class
4033 * @cfg {String} cls row class
4034 * @cfg {String} align Aligns the content in a table row
4035 * @cfg {String} bgcolor Specifies a background color for a table row
4036 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
4037 * @cfg {String} valign Vertical aligns the content in a table row
4040 * Create a new TableRow
4041 * @param {Object} config The config object
4044 Roo.bootstrap.TableRow = function(config){
4045 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
4048 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
4056 getAutoCreate : function(){
4057 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
4067 cfg.align = this.align;
4070 cfg.bgcolor = this.bgcolor;
4073 cfg.charoff = this.charoff;
4076 cfg.valign = this.valign;
4094 * @class Roo.bootstrap.TableBody
4095 * @extends Roo.bootstrap.Component
4096 * Bootstrap TableBody class
4097 * @cfg {String} cls element class
4098 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
4099 * @cfg {String} align Aligns the content inside the element
4100 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
4101 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
4104 * Create a new TableBody
4105 * @param {Object} config The config object
4108 Roo.bootstrap.TableBody = function(config){
4109 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
4112 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
4120 getAutoCreate : function(){
4121 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
4135 cfg.align = this.align;
4138 cfg.charoff = this.charoff;
4141 cfg.valign = this.valign;
4148 // initEvents : function()
4155 // this.store = Roo.factory(this.store, Roo.data);
4156 // this.store.on('load', this.onLoad, this);
4158 // this.store.load();
4162 // onLoad: function ()
4164 // this.fireEvent('load', this);
4174 * Ext JS Library 1.1.1
4175 * Copyright(c) 2006-2007, Ext JS, LLC.
4177 * Originally Released Under LGPL - original licence link has changed is not relivant.
4180 * <script type="text/javascript">
4183 // as we use this in bootstrap.
4184 Roo.namespace('Roo.form');
4186 * @class Roo.form.Action
4187 * Internal Class used to handle form actions
4189 * @param {Roo.form.BasicForm} el The form element or its id
4190 * @param {Object} config Configuration options
4195 // define the action interface
4196 Roo.form.Action = function(form, options){
4198 this.options = options || {};
4201 * Client Validation Failed
4204 Roo.form.Action.CLIENT_INVALID = 'client';
4206 * Server Validation Failed
4209 Roo.form.Action.SERVER_INVALID = 'server';
4211 * Connect to Server Failed
4214 Roo.form.Action.CONNECT_FAILURE = 'connect';
4216 * Reading Data from Server Failed
4219 Roo.form.Action.LOAD_FAILURE = 'load';
4221 Roo.form.Action.prototype = {
4223 failureType : undefined,
4224 response : undefined,
4228 run : function(options){
4233 success : function(response){
4238 handleResponse : function(response){
4242 // default connection failure
4243 failure : function(response){
4245 this.response = response;
4246 this.failureType = Roo.form.Action.CONNECT_FAILURE;
4247 this.form.afterAction(this, false);
4250 processResponse : function(response){
4251 this.response = response;
4252 if(!response.responseText){
4255 this.result = this.handleResponse(response);
4259 // utility functions used internally
4260 getUrl : function(appendParams){
4261 var url = this.options.url || this.form.url || this.form.el.dom.action;
4263 var p = this.getParams();
4265 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
4271 getMethod : function(){
4272 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
4275 getParams : function(){
4276 var bp = this.form.baseParams;
4277 var p = this.options.params;
4279 if(typeof p == "object"){
4280 p = Roo.urlEncode(Roo.applyIf(p, bp));
4281 }else if(typeof p == 'string' && bp){
4282 p += '&' + Roo.urlEncode(bp);
4285 p = Roo.urlEncode(bp);
4290 createCallback : function(){
4292 success: this.success,
4293 failure: this.failure,
4295 timeout: (this.form.timeout*1000),
4296 upload: this.form.fileUpload ? this.success : undefined
4301 Roo.form.Action.Submit = function(form, options){
4302 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
4305 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
4308 haveProgress : false,
4309 uploadComplete : false,
4311 // uploadProgress indicator.
4312 uploadProgress : function()
4314 if (!this.form.progressUrl) {
4318 if (!this.haveProgress) {
4319 Roo.MessageBox.progress("Uploading", "Uploading");
4321 if (this.uploadComplete) {
4322 Roo.MessageBox.hide();
4326 this.haveProgress = true;
4328 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
4330 var c = new Roo.data.Connection();
4332 url : this.form.progressUrl,
4337 success : function(req){
4338 //console.log(data);
4342 rdata = Roo.decode(req.responseText)
4344 Roo.log("Invalid data from server..");
4348 if (!rdata || !rdata.success) {
4350 Roo.MessageBox.alert(Roo.encode(rdata));
4353 var data = rdata.data;
4355 if (this.uploadComplete) {
4356 Roo.MessageBox.hide();
4361 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
4362 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
4365 this.uploadProgress.defer(2000,this);
4368 failure: function(data) {
4369 Roo.log('progress url failed ');
4380 // run get Values on the form, so it syncs any secondary forms.
4381 this.form.getValues();
4383 var o = this.options;
4384 var method = this.getMethod();
4385 var isPost = method == 'POST';
4386 if(o.clientValidation === false || this.form.isValid()){
4388 if (this.form.progressUrl) {
4389 this.form.findField('UPLOAD_IDENTIFIER').setValue(
4390 (new Date() * 1) + '' + Math.random());
4395 Roo.Ajax.request(Roo.apply(this.createCallback(), {
4396 form:this.form.el.dom,
4397 url:this.getUrl(!isPost),
4399 params:isPost ? this.getParams() : null,
4400 isUpload: this.form.fileUpload
4403 this.uploadProgress();
4405 }else if (o.clientValidation !== false){ // client validation failed
4406 this.failureType = Roo.form.Action.CLIENT_INVALID;
4407 this.form.afterAction(this, false);
4411 success : function(response)
4413 this.uploadComplete= true;
4414 if (this.haveProgress) {
4415 Roo.MessageBox.hide();
4419 var result = this.processResponse(response);
4420 if(result === true || result.success){
4421 this.form.afterAction(this, true);
4425 this.form.markInvalid(result.errors);
4426 this.failureType = Roo.form.Action.SERVER_INVALID;
4428 this.form.afterAction(this, false);
4430 failure : function(response)
4432 this.uploadComplete= true;
4433 if (this.haveProgress) {
4434 Roo.MessageBox.hide();
4437 this.response = response;
4438 this.failureType = Roo.form.Action.CONNECT_FAILURE;
4439 this.form.afterAction(this, false);
4442 handleResponse : function(response){
4443 if(this.form.errorReader){
4444 var rs = this.form.errorReader.read(response);
4447 for(var i = 0, len = rs.records.length; i < len; i++) {
4448 var r = rs.records[i];
4452 if(errors.length < 1){
4456 success : rs.success,
4462 ret = Roo.decode(response.responseText);
4466 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
4476 Roo.form.Action.Load = function(form, options){
4477 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
4478 this.reader = this.form.reader;
4481 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
4486 Roo.Ajax.request(Roo.apply(
4487 this.createCallback(), {
4488 method:this.getMethod(),
4489 url:this.getUrl(false),
4490 params:this.getParams()
4494 success : function(response){
4496 var result = this.processResponse(response);
4497 if(result === true || !result.success || !result.data){
4498 this.failureType = Roo.form.Action.LOAD_FAILURE;
4499 this.form.afterAction(this, false);
4502 this.form.clearInvalid();
4503 this.form.setValues(result.data);
4504 this.form.afterAction(this, true);
4507 handleResponse : function(response){
4508 if(this.form.reader){
4509 var rs = this.form.reader.read(response);
4510 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
4512 success : rs.success,
4516 return Roo.decode(response.responseText);
4520 Roo.form.Action.ACTION_TYPES = {
4521 'load' : Roo.form.Action.Load,
4522 'submit' : Roo.form.Action.Submit
4531 * @class Roo.bootstrap.Form
4532 * @extends Roo.bootstrap.Component
4533 * Bootstrap Form class
4534 * @cfg {String} method GET | POST (default POST)
4535 * @cfg {String} labelAlign top | left (default top)
4536 * @cfg {String} align left | right - for navbars
4541 * @param {Object} config The config object
4545 Roo.bootstrap.Form = function(config){
4546 Roo.bootstrap.Form.superclass.constructor.call(this, config);
4549 * @event clientvalidation
4550 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
4551 * @param {Form} this
4552 * @param {Boolean} valid true if the form has passed client-side validation
4554 clientvalidation: true,
4556 * @event beforeaction
4557 * Fires before any action is performed. Return false to cancel the action.
4558 * @param {Form} this
4559 * @param {Action} action The action to be performed
4563 * @event actionfailed
4564 * Fires when an action fails.
4565 * @param {Form} this
4566 * @param {Action} action The action that failed
4568 actionfailed : true,
4570 * @event actioncomplete
4571 * Fires when an action is completed.
4572 * @param {Form} this
4573 * @param {Action} action The action that completed
4575 actioncomplete : true
4580 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
4583 * @cfg {String} method
4584 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
4589 * The URL to use for form actions if one isn't supplied in the action options.
4592 * @cfg {Boolean} fileUpload
4593 * Set to true if this form is a file upload.
4597 * @cfg {Object} baseParams
4598 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
4602 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
4606 * @cfg {Sting} align (left|right) for navbar forms
4611 activeAction : null,
4614 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4615 * element by passing it or its id or mask the form itself by passing in true.
4618 waitMsgTarget : false,
4623 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
4624 * element by passing it or its id or mask the form itself by passing in true.
4628 getAutoCreate : function(){
4632 method : this.method || 'POST',
4633 id : this.id || Roo.id(),
4636 if (this.parent().xtype.match(/^Nav/)) {
4637 cfg.cls = 'navbar-form navbar-' + this.align;
4641 if (this.labelAlign == 'left' ) {
4642 cfg.cls += ' form-horizontal';
4648 initEvents : function()
4650 this.el.on('submit', this.onSubmit, this);
4655 onSubmit : function(e){
4660 * Returns true if client-side validation on the form is successful.
4663 isValid : function(){
4664 var items = this.getItems();
4666 items.each(function(f){
4675 * Returns true if any fields in this form have changed since their original load.
4678 isDirty : function(){
4680 var items = this.getItems();
4681 items.each(function(f){
4691 * Performs a predefined action (submit or load) or custom actions you define on this form.
4692 * @param {String} actionName The name of the action type
4693 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
4694 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4695 * accept other config options):
4697 Property Type Description
4698 ---------------- --------------- ----------------------------------------------------------------------------------
4699 url String The url for the action (defaults to the form's url)
4700 method String The form method to use (defaults to the form's method, or POST if not defined)
4701 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
4702 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
4703 validate the form on the client (defaults to false)
4705 * @return {BasicForm} this
4707 doAction : function(action, options){
4708 if(typeof action == 'string'){
4709 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4711 if(this.fireEvent('beforeaction', this, action) !== false){
4712 this.beforeAction(action);
4713 action.run.defer(100, action);
4719 beforeAction : function(action){
4720 var o = action.options;
4722 // not really supported yet.. ??
4724 //if(this.waitMsgTarget === true){
4725 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4726 //}else if(this.waitMsgTarget){
4727 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4728 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4730 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4736 afterAction : function(action, success){
4737 this.activeAction = null;
4738 var o = action.options;
4740 //if(this.waitMsgTarget === true){
4742 //}else if(this.waitMsgTarget){
4743 // this.waitMsgTarget.unmask();
4745 // Roo.MessageBox.updateProgress(1);
4746 // Roo.MessageBox.hide();
4753 Roo.callback(o.success, o.scope, [this, action]);
4754 this.fireEvent('actioncomplete', this, action);
4758 // failure condition..
4759 // we have a scenario where updates need confirming.
4760 // eg. if a locking scenario exists..
4761 // we look for { errors : { needs_confirm : true }} in the response.
4763 (typeof(action.result) != 'undefined') &&
4764 (typeof(action.result.errors) != 'undefined') &&
4765 (typeof(action.result.errors.needs_confirm) != 'undefined')
4768 Roo.log("not supported yet");
4771 Roo.MessageBox.confirm(
4772 "Change requires confirmation",
4773 action.result.errorMsg,
4778 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4788 Roo.callback(o.failure, o.scope, [this, action]);
4789 // show an error message if no failed handler is set..
4790 if (!this.hasListener('actionfailed')) {
4791 Roo.log("need to add dialog support");
4793 Roo.MessageBox.alert("Error",
4794 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4795 action.result.errorMsg :
4796 "Saving Failed, please check your entries or try again"
4801 this.fireEvent('actionfailed', this, action);
4806 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4807 * @param {String} id The value to search for
4810 findField : function(id){
4811 var items = this.getItems();
4812 var field = items.get(id);
4814 items.each(function(f){
4815 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4822 return field || null;
4825 * Mark fields in this form invalid in bulk.
4826 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4827 * @return {BasicForm} this
4829 markInvalid : function(errors){
4830 if(errors instanceof Array){
4831 for(var i = 0, len = errors.length; i < len; i++){
4832 var fieldError = errors[i];
4833 var f = this.findField(fieldError.id);
4835 f.markInvalid(fieldError.msg);
4841 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4842 field.markInvalid(errors[id]);
4846 //Roo.each(this.childForms || [], function (f) {
4847 // f.markInvalid(errors);
4854 * Set values for fields in this form in bulk.
4855 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4856 * @return {BasicForm} this
4858 setValues : function(values){
4859 if(values instanceof Array){ // array of objects
4860 for(var i = 0, len = values.length; i < len; i++){
4862 var f = this.findField(v.id);
4864 f.setValue(v.value);
4865 if(this.trackResetOnLoad){
4866 f.originalValue = f.getValue();
4870 }else{ // object hash
4873 if(typeof values[id] != 'function' && (field = this.findField(id))){
4875 if (field.setFromData &&
4877 field.displayField &&
4878 // combos' with local stores can
4879 // be queried via setValue()
4880 // to set their value..
4881 (field.store && !field.store.isLocal)
4885 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4886 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4887 field.setFromData(sd);
4890 field.setValue(values[id]);
4894 if(this.trackResetOnLoad){
4895 field.originalValue = field.getValue();
4901 //Roo.each(this.childForms || [], function (f) {
4902 // f.setValues(values);
4909 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4910 * they are returned as an array.
4911 * @param {Boolean} asString
4914 getValues : function(asString){
4915 //if (this.childForms) {
4916 // copy values from the child forms
4917 // Roo.each(this.childForms, function (f) {
4918 // this.setValues(f.getValues());
4924 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4925 if(asString === true){
4928 return Roo.urlDecode(fs);
4932 * Returns the fields in this form as an object with key/value pairs.
4933 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4936 getFieldValues : function(with_hidden)
4938 var items = this.getItems();
4940 items.each(function(f){
4944 var v = f.getValue();
4945 if (f.inputType =='radio') {
4946 if (typeof(ret[f.getName()]) == 'undefined') {
4947 ret[f.getName()] = ''; // empty..
4950 if (!f.el.dom.checked) {
4958 // not sure if this supported any more..
4959 if ((typeof(v) == 'object') && f.getRawValue) {
4960 v = f.getRawValue() ; // dates..
4962 // combo boxes where name != hiddenName...
4963 if (f.name != f.getName()) {
4964 ret[f.name] = f.getRawValue();
4966 ret[f.getName()] = v;
4973 * Clears all invalid messages in this form.
4974 * @return {BasicForm} this
4976 clearInvalid : function(){
4977 var items = this.getItems();
4979 items.each(function(f){
4990 * @return {BasicForm} this
4993 var items = this.getItems();
4994 items.each(function(f){
4998 Roo.each(this.childForms || [], function (f) {
5005 getItems : function()
5007 var r=new Roo.util.MixedCollection(false, function(o){
5008 return o.id || (o.id = Roo.id());
5010 var iter = function(el) {
5017 Roo.each(el.items,function(e) {
5036 * Ext JS Library 1.1.1
5037 * Copyright(c) 2006-2007, Ext JS, LLC.
5039 * Originally Released Under LGPL - original licence link has changed is not relivant.
5042 * <script type="text/javascript">
5045 * @class Roo.form.VTypes
5046 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
5049 Roo.form.VTypes = function(){
5050 // closure these in so they are only created once.
5051 var alpha = /^[a-zA-Z_]+$/;
5052 var alphanum = /^[a-zA-Z0-9_]+$/;
5053 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
5054 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
5056 // All these messages and functions are configurable
5059 * The function used to validate email addresses
5060 * @param {String} value The email address
5062 'email' : function(v){
5063 return email.test(v);
5066 * The error text to display when the email validation function returns false
5069 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
5071 * The keystroke filter mask to be applied on email input
5074 'emailMask' : /[a-z0-9_\.\-@]/i,
5077 * The function used to validate URLs
5078 * @param {String} value The URL
5080 'url' : function(v){
5084 * The error text to display when the url validation function returns false
5087 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
5090 * The function used to validate alpha values
5091 * @param {String} value The value
5093 'alpha' : function(v){
5094 return alpha.test(v);
5097 * The error text to display when the alpha validation function returns false
5100 'alphaText' : 'This field should only contain letters and _',
5102 * The keystroke filter mask to be applied on alpha input
5105 'alphaMask' : /[a-z_]/i,
5108 * The function used to validate alphanumeric values
5109 * @param {String} value The value
5111 'alphanum' : function(v){
5112 return alphanum.test(v);
5115 * The error text to display when the alphanumeric validation function returns false
5118 'alphanumText' : 'This field should only contain letters, numbers and _',
5120 * The keystroke filter mask to be applied on alphanumeric input
5123 'alphanumMask' : /[a-z0-9_]/i
5133 * @class Roo.bootstrap.Input
5134 * @extends Roo.bootstrap.Component
5135 * Bootstrap Input class
5136 * @cfg {Boolean} disabled is it disabled
5137 * @cfg {String} fieldLabel - the label associated
5138 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
5139 * @cfg {String} name name of the input
5140 * @cfg {string} fieldLabel - the label associated
5141 * @cfg {string} inputType - input / file submit ...
5142 * @cfg {string} placeholder - placeholder to put in text.
5143 * @cfg {string} before - input group add on before
5144 * @cfg {string} after - input group add on after
5145 * @cfg {string} size - (lg|sm) or leave empty..
5146 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
5147 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
5148 * @cfg {Number} md colspan out of 12 for computer-sized screens
5149 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
5150 * @cfg {string} value default value of the input
5151 * @cfg {Number} labelWidth set the width of label (0-12)
5152 * @cfg {String} labelAlign (top|left)
5153 * @cfg {Boolean} readOnly Specifies that the field should be read-only
5157 * Create a new Input
5158 * @param {Object} config The config object
5161 Roo.bootstrap.Input = function(config){
5162 Roo.bootstrap.Input.superclass.constructor.call(this, config);
5167 * Fires when this field receives input focus.
5168 * @param {Roo.form.Field} this
5173 * Fires when this field loses input focus.
5174 * @param {Roo.form.Field} this
5179 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
5180 * {@link Roo.EventObject#getKey} to determine which key was pressed.
5181 * @param {Roo.form.Field} this
5182 * @param {Roo.EventObject} e The event object
5187 * Fires just before the field blurs if the field value has changed.
5188 * @param {Roo.form.Field} this
5189 * @param {Mixed} newValue The new value
5190 * @param {Mixed} oldValue The original value
5195 * Fires after the field has been marked as invalid.
5196 * @param {Roo.form.Field} this
5197 * @param {String} msg The validation message
5202 * Fires after the field has been validated with no errors.
5203 * @param {Roo.form.Field} this
5208 * Fires after the key up
5209 * @param {Roo.form.Field} this
5210 * @param {Roo.EventObject} e The event Object
5216 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
5218 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
5219 automatic validation (defaults to "keyup").
5221 validationEvent : "keyup",
5223 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
5225 validateOnBlur : true,
5227 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
5229 validationDelay : 250,
5231 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
5233 focusClass : "x-form-focus", // not needed???
5237 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
5239 invalidClass : "has-error",
5242 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
5244 selectOnFocus : false,
5247 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
5251 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
5256 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
5258 disableKeyFilter : false,
5261 * @cfg {Boolean} disabled True to disable the field (defaults to false).
5265 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
5269 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
5271 blankText : "This field is required",
5274 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
5278 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
5280 maxLength : Number.MAX_VALUE,
5282 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
5284 minLengthText : "The minimum length for this field is {0}",
5286 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
5288 maxLengthText : "The maximum length for this field is {0}",
5292 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
5293 * If available, this function will be called only after the basic validators all return true, and will be passed the
5294 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
5298 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
5299 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
5300 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
5304 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
5327 parentLabelAlign : function()
5330 while (parent.parent()) {
5331 parent = parent.parent();
5332 if (typeof(parent.labelAlign) !='undefined') {
5333 return parent.labelAlign;
5340 getAutoCreate : function(){
5342 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5348 if(this.inputType != 'hidden'){
5349 cfg.cls = 'form-group' //input-group
5355 type : this.inputType,
5357 cls : 'form-control',
5358 placeholder : this.placeholder || ''
5362 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5363 input.maxLength = this.maxLength;
5366 if (this.disabled) {
5367 input.disabled=true;
5370 if (this.readOnly) {
5371 input.readonly=true;
5375 input.name = this.name;
5378 input.cls += ' input-' + this.size;
5381 ['xs','sm','md','lg'].map(function(size){
5382 if (settings[size]) {
5383 cfg.cls += ' col-' + size + '-' + settings[size];
5387 var inputblock = input;
5389 if (this.before || this.after) {
5392 cls : 'input-group',
5396 inputblock.cn.push({
5398 cls : 'input-group-addon',
5402 inputblock.cn.push(input);
5404 inputblock.cn.push({
5406 cls : 'input-group-addon',
5413 if (align ==='left' && this.fieldLabel.length) {
5414 Roo.log("left and has label");
5420 cls : 'control-label col-sm-' + this.labelWidth,
5421 html : this.fieldLabel
5425 cls : "col-sm-" + (12 - this.labelWidth),
5432 } else if ( this.fieldLabel.length) {
5438 //cls : 'input-group-addon',
5439 html : this.fieldLabel
5449 Roo.log(" no label && no align");
5458 Roo.log('input-parentType: ' + this.parentType);
5460 if (this.parentType === 'Navbar' && this.parent().bar) {
5461 cfg.cls += ' navbar-form';
5469 * return the real input element.
5471 inputEl: function ()
5473 return this.el.select('input.form-control',true).first();
5475 setDisabled : function(v)
5477 var i = this.inputEl().dom;
5479 i.removeAttribute('disabled');
5483 i.setAttribute('disabled','true');
5485 initEvents : function()
5488 this.inputEl().on("keydown" , this.fireKey, this);
5489 this.inputEl().on("focus", this.onFocus, this);
5490 this.inputEl().on("blur", this.onBlur, this);
5492 this.inputEl().relayEvent('keyup', this);
5494 // reference to original value for reset
5495 this.originalValue = this.getValue();
5496 //Roo.form.TextField.superclass.initEvents.call(this);
5497 if(this.validationEvent == 'keyup'){
5498 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
5499 this.inputEl().on('keyup', this.filterValidation, this);
5501 else if(this.validationEvent !== false){
5502 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
5505 if(this.selectOnFocus){
5506 this.on("focus", this.preFocus, this);
5509 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
5510 this.inputEl().on("keypress", this.filterKeys, this);
5513 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
5514 this.el.on("click", this.autoSize, this);
5517 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
5518 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
5522 filterValidation : function(e){
5523 if(!e.isNavKeyPress()){
5524 this.validationTask.delay(this.validationDelay);
5528 * Validates the field value
5529 * @return {Boolean} True if the value is valid, else false
5531 validate : function(){
5532 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
5533 if(this.disabled || this.validateValue(this.getRawValue())){
5534 this.clearInvalid();
5542 * Validates a value according to the field's validation rules and marks the field as invalid
5543 * if the validation fails
5544 * @param {Mixed} value The value to validate
5545 * @return {Boolean} True if the value is valid, else false
5547 validateValue : function(value){
5548 if(value.length < 1) { // if it's blank
5549 if(this.allowBlank){
5550 this.clearInvalid();
5553 this.markInvalid(this.blankText);
5557 if(value.length < this.minLength){
5558 this.markInvalid(String.format(this.minLengthText, this.minLength));
5561 if(value.length > this.maxLength){
5562 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
5566 var vt = Roo.form.VTypes;
5567 if(!vt[this.vtype](value, this)){
5568 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
5572 if(typeof this.validator == "function"){
5573 var msg = this.validator(value);
5575 this.markInvalid(msg);
5579 if(this.regex && !this.regex.test(value)){
5580 this.markInvalid(this.regexText);
5589 fireKey : function(e){
5590 //Roo.log('field ' + e.getKey());
5591 if(e.isNavKeyPress()){
5592 this.fireEvent("specialkey", this, e);
5595 focus : function (selectText){
5597 this.inputEl().focus();
5598 if(selectText === true){
5599 this.inputEl().dom.select();
5605 onFocus : function(){
5606 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5607 // this.el.addClass(this.focusClass);
5610 this.hasFocus = true;
5611 this.startValue = this.getValue();
5612 this.fireEvent("focus", this);
5616 beforeBlur : Roo.emptyFn,
5620 onBlur : function(){
5622 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
5623 //this.el.removeClass(this.focusClass);
5625 this.hasFocus = false;
5626 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
5629 var v = this.getValue();
5630 if(String(v) !== String(this.startValue)){
5631 this.fireEvent('change', this, v, this.startValue);
5633 this.fireEvent("blur", this);
5637 * Resets the current field value to the originally loaded value and clears any validation messages
5640 this.setValue(this.originalValue);
5641 this.clearInvalid();
5644 * Returns the name of the field
5645 * @return {Mixed} name The name field
5647 getName: function(){
5651 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
5652 * @return {Mixed} value The field value
5654 getValue : function(){
5655 return this.inputEl().getValue();
5658 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
5659 * @return {Mixed} value The field value
5661 getRawValue : function(){
5662 var v = this.inputEl().getValue();
5668 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
5669 * @param {Mixed} value The value to set
5671 setRawValue : function(v){
5672 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5675 selectText : function(start, end){
5676 var v = this.getRawValue();
5678 start = start === undefined ? 0 : start;
5679 end = end === undefined ? v.length : end;
5680 var d = this.inputEl().dom;
5681 if(d.setSelectionRange){
5682 d.setSelectionRange(start, end);
5683 }else if(d.createTextRange){
5684 var range = d.createTextRange();
5685 range.moveStart("character", start);
5686 range.moveEnd("character", v.length-end);
5693 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
5694 * @param {Mixed} value The value to set
5696 setValue : function(v){
5699 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5705 processValue : function(value){
5706 if(this.stripCharsRe){
5707 var newValue = value.replace(this.stripCharsRe, '');
5708 if(newValue !== value){
5709 this.setRawValue(newValue);
5716 preFocus : function(){
5718 if(this.selectOnFocus){
5719 this.inputEl().dom.select();
5722 filterKeys : function(e){
5724 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5727 var c = e.getCharCode(), cc = String.fromCharCode(c);
5728 if(Roo.isIE && (e.isSpecialKey() || !cc)){
5731 if(!this.maskRe.test(cc)){
5736 * Clear any invalid styles/messages for this field
5738 clearInvalid : function(){
5740 if(!this.el || this.preventMark){ // not rendered
5743 this.el.removeClass(this.invalidClass);
5745 switch(this.msgTarget){
5747 this.el.dom.qtip = '';
5750 this.el.dom.title = '';
5754 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5759 this.errorIcon.dom.qtip = '';
5760 this.errorIcon.hide();
5761 this.un('resize', this.alignErrorIcon, this);
5765 var t = Roo.getDom(this.msgTarget);
5767 t.style.display = 'none';
5771 this.fireEvent('valid', this);
5774 * Mark this field as invalid
5775 * @param {String} msg The validation message
5777 markInvalid : function(msg){
5778 if(!this.el || this.preventMark){ // not rendered
5781 this.el.addClass(this.invalidClass);
5783 msg = msg || this.invalidText;
5784 switch(this.msgTarget){
5786 this.el.dom.qtip = msg;
5787 this.el.dom.qclass = 'x-form-invalid-tip';
5788 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5789 Roo.QuickTips.enable();
5793 this.el.dom.title = msg;
5797 var elp = this.el.findParent('.x-form-element', 5, true);
5798 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5799 this.errorEl.setWidth(elp.getWidth(true)-20);
5801 this.errorEl.update(msg);
5802 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5805 if(!this.errorIcon){
5806 var elp = this.el.findParent('.x-form-element', 5, true);
5807 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5809 this.alignErrorIcon();
5810 this.errorIcon.dom.qtip = msg;
5811 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5812 this.errorIcon.show();
5813 this.on('resize', this.alignErrorIcon, this);
5816 var t = Roo.getDom(this.msgTarget);
5818 t.style.display = this.msgDisplay;
5822 this.fireEvent('invalid', this, msg);
5825 SafariOnKeyDown : function(event)
5827 // this is a workaround for a password hang bug on chrome/ webkit.
5829 var isSelectAll = false;
5831 if(this.inputEl().dom.selectionEnd > 0){
5832 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5834 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5835 event.preventDefault();
5840 if(isSelectAll){ // backspace and delete key
5842 event.preventDefault();
5843 // this is very hacky as keydown always get's upper case.
5845 var cc = String.fromCharCode(event.getCharCode());
5846 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5850 adjustWidth : function(tag, w){
5851 tag = tag.toLowerCase();
5852 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5853 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5857 if(tag == 'textarea'){
5860 }else if(Roo.isOpera){
5864 if(tag == 'textarea'){
5883 * @class Roo.bootstrap.TextArea
5884 * @extends Roo.bootstrap.Input
5885 * Bootstrap TextArea class
5886 * @cfg {Number} cols Specifies the visible width of a text area
5887 * @cfg {Number} rows Specifies the visible number of lines in a text area
5888 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5889 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5890 * @cfg {string} html text
5893 * Create a new TextArea
5894 * @param {Object} config The config object
5897 Roo.bootstrap.TextArea = function(config){
5898 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5902 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5912 getAutoCreate : function(){
5914 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5925 value : this.value || '',
5926 html: this.html || '',
5927 cls : 'form-control',
5928 placeholder : this.placeholder || ''
5932 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5933 input.maxLength = this.maxLength;
5937 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5941 input.cols = this.cols;
5944 if (this.readOnly) {
5945 input.readonly = true;
5949 input.name = this.name;
5953 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5957 ['xs','sm','md','lg'].map(function(size){
5958 if (settings[size]) {
5959 cfg.cls += ' col-' + size + '-' + settings[size];
5963 var inputblock = input;
5965 if (this.before || this.after) {
5968 cls : 'input-group',
5972 inputblock.cn.push({
5974 cls : 'input-group-addon',
5978 inputblock.cn.push(input);
5980 inputblock.cn.push({
5982 cls : 'input-group-addon',
5989 if (align ==='left' && this.fieldLabel.length) {
5990 Roo.log("left and has label");
5996 cls : 'control-label col-sm-' + this.labelWidth,
5997 html : this.fieldLabel
6001 cls : "col-sm-" + (12 - this.labelWidth),
6008 } else if ( this.fieldLabel.length) {
6014 //cls : 'input-group-addon',
6015 html : this.fieldLabel
6025 Roo.log(" no label && no align");
6035 if (this.disabled) {
6036 input.disabled=true;
6043 * return the real textarea element.
6045 inputEl: function ()
6047 return this.el.select('textarea.form-control',true).first();
6055 * trigger field - base class for combo..
6060 * @class Roo.bootstrap.TriggerField
6061 * @extends Roo.bootstrap.Input
6062 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
6063 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
6064 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
6065 * for which you can provide a custom implementation. For example:
6067 var trigger = new Roo.bootstrap.TriggerField();
6068 trigger.onTriggerClick = myTriggerFn;
6069 trigger.applyTo('my-field');
6072 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
6073 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
6074 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
6075 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
6077 * Create a new TriggerField.
6078 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
6079 * to the base TextField)
6081 Roo.bootstrap.TriggerField = function(config){
6082 this.mimicing = false;
6083 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
6086 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
6088 * @cfg {String} triggerClass A CSS class to apply to the trigger
6091 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
6095 /** @cfg {Boolean} grow @hide */
6096 /** @cfg {Number} growMin @hide */
6097 /** @cfg {Number} growMax @hide */
6103 autoSize: Roo.emptyFn,
6110 actionMode : 'wrap',
6114 getAutoCreate : function(){
6116 var parent = this.parent();
6118 var align = this.parentLabelAlign();
6123 cls: 'form-group' //input-group
6130 type : this.inputType,
6131 cls : 'form-control',
6132 autocomplete: 'off',
6133 placeholder : this.placeholder || ''
6137 input.name = this.name;
6140 input.cls += ' input-' + this.size;
6143 if (this.disabled) {
6144 input.disabled=true;
6147 var inputblock = input;
6149 if (this.before || this.after) {
6152 cls : 'input-group',
6156 inputblock.cn.push({
6158 cls : 'input-group-addon',
6162 inputblock.cn.push(input);
6164 inputblock.cn.push({
6166 cls : 'input-group-addon',
6179 cls: 'form-hidden-field'
6187 Roo.log('multiple');
6195 cls: 'form-hidden-field'
6199 cls: 'select2-choices',
6203 cls: 'select2-search-field',
6216 cls: 'select2-container input-group',
6221 cls: 'typeahead typeahead-long dropdown-menu',
6222 style: 'display:none'
6230 cls : 'input-group-addon btn dropdown-toggle',
6238 cls: 'combobox-clear',
6252 combobox.cls += ' select2-container-multi';
6255 if (align ==='left' && this.fieldLabel.length) {
6257 Roo.log("left and has label");
6263 cls : 'control-label col-sm-' + this.labelWidth,
6264 html : this.fieldLabel
6268 cls : "col-sm-" + (12 - this.labelWidth),
6275 } else if ( this.fieldLabel.length) {
6281 //cls : 'input-group-addon',
6282 html : this.fieldLabel
6292 Roo.log(" no label && no align");
6299 ['xs','sm','md','lg'].map(function(size){
6300 if (settings[size]) {
6301 cfg.cls += ' col-' + size + '-' + settings[size];
6312 onResize : function(w, h){
6313 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
6314 // if(typeof w == 'number'){
6315 // var x = w - this.trigger.getWidth();
6316 // this.inputEl().setWidth(this.adjustWidth('input', x));
6317 // this.trigger.setStyle('left', x+'px');
6322 adjustSize : Roo.BoxComponent.prototype.adjustSize,
6325 getResizeEl : function(){
6326 return this.inputEl();
6330 getPositionEl : function(){
6331 return this.inputEl();
6335 alignErrorIcon : function(){
6336 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
6340 initEvents : function(){
6342 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
6343 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
6345 this.trigger = this.el.select('span.dropdown-toggle',true).first();
6346 if(this.hideTrigger){
6347 this.trigger.setDisplayed(false);
6349 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
6353 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
6356 //this.trigger.addClassOnOver('x-form-trigger-over');
6357 //this.trigger.addClassOnClick('x-form-trigger-click');
6360 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
6365 initTrigger : function(){
6370 onDestroy : function(){
6372 this.trigger.removeAllListeners();
6373 // this.trigger.remove();
6376 // this.wrap.remove();
6378 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
6382 onFocus : function(){
6383 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
6386 this.wrap.addClass('x-trigger-wrap-focus');
6387 this.mimicing = true;
6388 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
6389 if(this.monitorTab){
6390 this.el.on("keydown", this.checkTab, this);
6397 checkTab : function(e){
6398 if(e.getKey() == e.TAB){
6404 onBlur : function(){
6409 mimicBlur : function(e, t){
6411 if(!this.wrap.contains(t) && this.validateBlur()){
6418 triggerBlur : function(){
6419 this.mimicing = false;
6420 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
6421 if(this.monitorTab){
6422 this.el.un("keydown", this.checkTab, this);
6424 //this.wrap.removeClass('x-trigger-wrap-focus');
6425 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
6429 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
6430 validateBlur : function(e, t){
6435 onDisable : function(){
6436 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
6438 // this.wrap.addClass('x-item-disabled');
6443 onEnable : function(){
6444 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
6446 // this.el.removeClass('x-item-disabled');
6451 onShow : function(){
6452 var ae = this.getActionEl();
6455 ae.dom.style.display = '';
6456 ae.dom.style.visibility = 'visible';
6462 onHide : function(){
6463 var ae = this.getActionEl();
6464 ae.dom.style.display = 'none';
6468 * The function that should handle the trigger's click event. This method does nothing by default until overridden
6469 * by an implementing function.
6471 * @param {EventObject} e
6473 onTriggerClick : Roo.emptyFn
6477 * Ext JS Library 1.1.1
6478 * Copyright(c) 2006-2007, Ext JS, LLC.
6480 * Originally Released Under LGPL - original licence link has changed is not relivant.
6483 * <script type="text/javascript">
6488 * @class Roo.data.SortTypes
6490 * Defines the default sorting (casting?) comparison functions used when sorting data.
6492 Roo.data.SortTypes = {
6494 * Default sort that does nothing
6495 * @param {Mixed} s The value being converted
6496 * @return {Mixed} The comparison value
6503 * The regular expression used to strip tags
6507 stripTagsRE : /<\/?[^>]+>/gi,
6510 * Strips all HTML tags to sort on text only
6511 * @param {Mixed} s The value being converted
6512 * @return {String} The comparison value
6514 asText : function(s){
6515 return String(s).replace(this.stripTagsRE, "");
6519 * Strips all HTML tags to sort on text only - Case insensitive
6520 * @param {Mixed} s The value being converted
6521 * @return {String} The comparison value
6523 asUCText : function(s){
6524 return String(s).toUpperCase().replace(this.stripTagsRE, "");
6528 * Case insensitive string
6529 * @param {Mixed} s The value being converted
6530 * @return {String} The comparison value
6532 asUCString : function(s) {
6533 return String(s).toUpperCase();
6538 * @param {Mixed} s The value being converted
6539 * @return {Number} The comparison value
6541 asDate : function(s) {
6545 if(s instanceof Date){
6548 return Date.parse(String(s));
6553 * @param {Mixed} s The value being converted
6554 * @return {Float} The comparison value
6556 asFloat : function(s) {
6557 var val = parseFloat(String(s).replace(/,/g, ""));
6558 if(isNaN(val)) val = 0;
6564 * @param {Mixed} s The value being converted
6565 * @return {Number} The comparison value
6567 asInt : function(s) {
6568 var val = parseInt(String(s).replace(/,/g, ""));
6569 if(isNaN(val)) val = 0;
6574 * Ext JS Library 1.1.1
6575 * Copyright(c) 2006-2007, Ext JS, LLC.
6577 * Originally Released Under LGPL - original licence link has changed is not relivant.
6580 * <script type="text/javascript">
6584 * @class Roo.data.Record
6585 * Instances of this class encapsulate both record <em>definition</em> information, and record
6586 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
6587 * to access Records cached in an {@link Roo.data.Store} object.<br>
6589 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
6590 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
6593 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
6595 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
6596 * {@link #create}. The parameters are the same.
6597 * @param {Array} data An associative Array of data values keyed by the field name.
6598 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
6599 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
6600 * not specified an integer id is generated.
6602 Roo.data.Record = function(data, id){
6603 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
6608 * Generate a constructor for a specific record layout.
6609 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
6610 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
6611 * Each field definition object may contain the following properties: <ul>
6612 * <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,
6613 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
6614 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
6615 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
6616 * is being used, then this is a string containing the javascript expression to reference the data relative to
6617 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
6618 * to the data item relative to the record element. If the mapping expression is the same as the field name,
6619 * this may be omitted.</p></li>
6620 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
6621 * <ul><li>auto (Default, implies no conversion)</li>
6626 * <li>date</li></ul></p></li>
6627 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
6628 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
6629 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6630 * by the Reader into an object that will be stored in the Record. It is passed the
6631 * following parameters:<ul>
6632 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6634 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6636 * <br>usage:<br><pre><code>
6637 var TopicRecord = Roo.data.Record.create(
6638 {name: 'title', mapping: 'topic_title'},
6639 {name: 'author', mapping: 'username'},
6640 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6641 {name: 'lastPost', mapping: 'post_time', type: 'date'},
6642 {name: 'lastPoster', mapping: 'user2'},
6643 {name: 'excerpt', mapping: 'post_text'}
6646 var myNewRecord = new TopicRecord({
6647 title: 'Do my job please',
6650 lastPost: new Date(),
6651 lastPoster: 'Animal',
6652 excerpt: 'No way dude!'
6654 myStore.add(myNewRecord);
6659 Roo.data.Record.create = function(o){
6661 f.superclass.constructor.apply(this, arguments);
6663 Roo.extend(f, Roo.data.Record);
6664 var p = f.prototype;
6665 p.fields = new Roo.util.MixedCollection(false, function(field){
6668 for(var i = 0, len = o.length; i < len; i++){
6669 p.fields.add(new Roo.data.Field(o[i]));
6671 f.getField = function(name){
6672 return p.fields.get(name);
6677 Roo.data.Record.AUTO_ID = 1000;
6678 Roo.data.Record.EDIT = 'edit';
6679 Roo.data.Record.REJECT = 'reject';
6680 Roo.data.Record.COMMIT = 'commit';
6682 Roo.data.Record.prototype = {
6684 * Readonly flag - true if this record has been modified.
6693 join : function(store){
6698 * Set the named field to the specified value.
6699 * @param {String} name The name of the field to set.
6700 * @param {Object} value The value to set the field to.
6702 set : function(name, value){
6703 if(this.data[name] == value){
6710 if(typeof this.modified[name] == 'undefined'){
6711 this.modified[name] = this.data[name];
6713 this.data[name] = value;
6714 if(!this.editing && this.store){
6715 this.store.afterEdit(this);
6720 * Get the value of the named field.
6721 * @param {String} name The name of the field to get the value of.
6722 * @return {Object} The value of the field.
6724 get : function(name){
6725 return this.data[name];
6729 beginEdit : function(){
6730 this.editing = true;
6735 cancelEdit : function(){
6736 this.editing = false;
6737 delete this.modified;
6741 endEdit : function(){
6742 this.editing = false;
6743 if(this.dirty && this.store){
6744 this.store.afterEdit(this);
6749 * Usually called by the {@link Roo.data.Store} which owns the Record.
6750 * Rejects all changes made to the Record since either creation, or the last commit operation.
6751 * Modified fields are reverted to their original values.
6753 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6754 * of reject operations.
6756 reject : function(){
6757 var m = this.modified;
6759 if(typeof m[n] != "function"){
6760 this.data[n] = m[n];
6764 delete this.modified;
6765 this.editing = false;
6767 this.store.afterReject(this);
6772 * Usually called by the {@link Roo.data.Store} which owns the Record.
6773 * Commits all changes made to the Record since either creation, or the last commit operation.
6775 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6776 * of commit operations.
6778 commit : function(){
6780 delete this.modified;
6781 this.editing = false;
6783 this.store.afterCommit(this);
6788 hasError : function(){
6789 return this.error != null;
6793 clearError : function(){
6798 * Creates a copy of this record.
6799 * @param {String} id (optional) A new record id if you don't want to use this record's id
6802 copy : function(newId) {
6803 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6807 * Ext JS Library 1.1.1
6808 * Copyright(c) 2006-2007, Ext JS, LLC.
6810 * Originally Released Under LGPL - original licence link has changed is not relivant.
6813 * <script type="text/javascript">
6819 * @class Roo.data.Store
6820 * @extends Roo.util.Observable
6821 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6822 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6824 * 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
6825 * has no knowledge of the format of the data returned by the Proxy.<br>
6827 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6828 * instances from the data object. These records are cached and made available through accessor functions.
6830 * Creates a new Store.
6831 * @param {Object} config A config object containing the objects needed for the Store to access data,
6832 * and read the data into Records.
6834 Roo.data.Store = function(config){
6835 this.data = new Roo.util.MixedCollection(false);
6836 this.data.getKey = function(o){
6839 this.baseParams = {};
6846 "multisort" : "_multisort"
6849 if(config && config.data){
6850 this.inlineData = config.data;
6854 Roo.apply(this, config);
6856 if(this.reader){ // reader passed
6857 this.reader = Roo.factory(this.reader, Roo.data);
6858 this.reader.xmodule = this.xmodule || false;
6859 if(!this.recordType){
6860 this.recordType = this.reader.recordType;
6862 if(this.reader.onMetaChange){
6863 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6867 if(this.recordType){
6868 this.fields = this.recordType.prototype.fields;
6874 * @event datachanged
6875 * Fires when the data cache has changed, and a widget which is using this Store
6876 * as a Record cache should refresh its view.
6877 * @param {Store} this
6882 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6883 * @param {Store} this
6884 * @param {Object} meta The JSON metadata
6889 * Fires when Records have been added to the Store
6890 * @param {Store} this
6891 * @param {Roo.data.Record[]} records The array of Records added
6892 * @param {Number} index The index at which the record(s) were added
6897 * Fires when a Record has been removed from the Store
6898 * @param {Store} this
6899 * @param {Roo.data.Record} record The Record that was removed
6900 * @param {Number} index The index at which the record was removed
6905 * Fires when a Record has been updated
6906 * @param {Store} this
6907 * @param {Roo.data.Record} record The Record that was updated
6908 * @param {String} operation The update operation being performed. Value may be one of:
6910 Roo.data.Record.EDIT
6911 Roo.data.Record.REJECT
6912 Roo.data.Record.COMMIT
6918 * Fires when the data cache has been cleared.
6919 * @param {Store} this
6924 * Fires before a request is made for a new data object. If the beforeload handler returns false
6925 * the load action will be canceled.
6926 * @param {Store} this
6927 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6931 * @event beforeloadadd
6932 * Fires after a new set of Records has been loaded.
6933 * @param {Store} this
6934 * @param {Roo.data.Record[]} records The Records that were loaded
6935 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6937 beforeloadadd : true,
6940 * Fires after a new set of Records has been loaded, before they are added to the store.
6941 * @param {Store} this
6942 * @param {Roo.data.Record[]} records The Records that were loaded
6943 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6944 * @params {Object} return from reader
6948 * @event loadexception
6949 * Fires if an exception occurs in the Proxy during loading.
6950 * Called with the signature of the Proxy's "loadexception" event.
6951 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6954 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6955 * @param {Object} load options
6956 * @param {Object} jsonData from your request (normally this contains the Exception)
6958 loadexception : true
6962 this.proxy = Roo.factory(this.proxy, Roo.data);
6963 this.proxy.xmodule = this.xmodule || false;
6964 this.relayEvents(this.proxy, ["loadexception"]);
6966 this.sortToggle = {};
6967 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6969 Roo.data.Store.superclass.constructor.call(this);
6971 if(this.inlineData){
6972 this.loadData(this.inlineData);
6973 delete this.inlineData;
6977 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6979 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6980 * without a remote query - used by combo/forms at present.
6984 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6987 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6990 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6991 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6994 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6995 * on any HTTP request
6998 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
7001 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
7005 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
7006 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
7011 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
7012 * loaded or when a record is removed. (defaults to false).
7014 pruneModifiedRecords : false,
7020 * Add Records to the Store and fires the add event.
7021 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7023 add : function(records){
7024 records = [].concat(records);
7025 for(var i = 0, len = records.length; i < len; i++){
7026 records[i].join(this);
7028 var index = this.data.length;
7029 this.data.addAll(records);
7030 this.fireEvent("add", this, records, index);
7034 * Remove a Record from the Store and fires the remove event.
7035 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
7037 remove : function(record){
7038 var index = this.data.indexOf(record);
7039 this.data.removeAt(index);
7040 if(this.pruneModifiedRecords){
7041 this.modified.remove(record);
7043 this.fireEvent("remove", this, record, index);
7047 * Remove all Records from the Store and fires the clear event.
7049 removeAll : function(){
7051 if(this.pruneModifiedRecords){
7054 this.fireEvent("clear", this);
7058 * Inserts Records to the Store at the given index and fires the add event.
7059 * @param {Number} index The start index at which to insert the passed Records.
7060 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
7062 insert : function(index, records){
7063 records = [].concat(records);
7064 for(var i = 0, len = records.length; i < len; i++){
7065 this.data.insert(index, records[i]);
7066 records[i].join(this);
7068 this.fireEvent("add", this, records, index);
7072 * Get the index within the cache of the passed Record.
7073 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
7074 * @return {Number} The index of the passed Record. Returns -1 if not found.
7076 indexOf : function(record){
7077 return this.data.indexOf(record);
7081 * Get the index within the cache of the Record with the passed id.
7082 * @param {String} id The id of the Record to find.
7083 * @return {Number} The index of the Record. Returns -1 if not found.
7085 indexOfId : function(id){
7086 return this.data.indexOfKey(id);
7090 * Get the Record with the specified id.
7091 * @param {String} id The id of the Record to find.
7092 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
7094 getById : function(id){
7095 return this.data.key(id);
7099 * Get the Record at the specified index.
7100 * @param {Number} index The index of the Record to find.
7101 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
7103 getAt : function(index){
7104 return this.data.itemAt(index);
7108 * Returns a range of Records between specified indices.
7109 * @param {Number} startIndex (optional) The starting index (defaults to 0)
7110 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
7111 * @return {Roo.data.Record[]} An array of Records
7113 getRange : function(start, end){
7114 return this.data.getRange(start, end);
7118 storeOptions : function(o){
7119 o = Roo.apply({}, o);
7122 this.lastOptions = o;
7126 * Loads the Record cache from the configured Proxy using the configured Reader.
7128 * If using remote paging, then the first load call must specify the <em>start</em>
7129 * and <em>limit</em> properties in the options.params property to establish the initial
7130 * position within the dataset, and the number of Records to cache on each read from the Proxy.
7132 * <strong>It is important to note that for remote data sources, loading is asynchronous,
7133 * and this call will return before the new data has been loaded. Perform any post-processing
7134 * in a callback function, or in a "load" event handler.</strong>
7136 * @param {Object} options An object containing properties which control loading options:<ul>
7137 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
7138 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
7139 * passed the following arguments:<ul>
7140 * <li>r : Roo.data.Record[]</li>
7141 * <li>options: Options object from the load call</li>
7142 * <li>success: Boolean success indicator</li></ul></li>
7143 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
7144 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
7147 load : function(options){
7148 options = options || {};
7149 if(this.fireEvent("beforeload", this, options) !== false){
7150 this.storeOptions(options);
7151 var p = Roo.apply(options.params || {}, this.baseParams);
7152 // if meta was not loaded from remote source.. try requesting it.
7153 if (!this.reader.metaFromRemote) {
7156 if(this.sortInfo && this.remoteSort){
7157 var pn = this.paramNames;
7158 p[pn["sort"]] = this.sortInfo.field;
7159 p[pn["dir"]] = this.sortInfo.direction;
7161 if (this.multiSort) {
7162 var pn = this.paramNames;
7163 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
7166 this.proxy.load(p, this.reader, this.loadRecords, this, options);
7171 * Reloads the Record cache from the configured Proxy using the configured Reader and
7172 * the options from the last load operation performed.
7173 * @param {Object} options (optional) An object containing properties which may override the options
7174 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
7175 * the most recently used options are reused).
7177 reload : function(options){
7178 this.load(Roo.applyIf(options||{}, this.lastOptions));
7182 // Called as a callback by the Reader during a load operation.
7183 loadRecords : function(o, options, success){
7184 if(!o || success === false){
7185 if(success !== false){
7186 this.fireEvent("load", this, [], options, o);
7188 if(options.callback){
7189 options.callback.call(options.scope || this, [], options, false);
7193 // if data returned failure - throw an exception.
7194 if (o.success === false) {
7195 // show a message if no listener is registered.
7196 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
7197 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
7199 // loadmask wil be hooked into this..
7200 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
7203 var r = o.records, t = o.totalRecords || r.length;
7205 this.fireEvent("beforeloadadd", this, r, options, o);
7207 if(!options || options.add !== true){
7208 if(this.pruneModifiedRecords){
7211 for(var i = 0, len = r.length; i < len; i++){
7215 this.data = this.snapshot;
7216 delete this.snapshot;
7219 this.data.addAll(r);
7220 this.totalLength = t;
7222 this.fireEvent("datachanged", this);
7224 this.totalLength = Math.max(t, this.data.length+r.length);
7227 this.fireEvent("load", this, r, options, o);
7228 if(options.callback){
7229 options.callback.call(options.scope || this, r, options, true);
7235 * Loads data from a passed data block. A Reader which understands the format of the data
7236 * must have been configured in the constructor.
7237 * @param {Object} data The data block from which to read the Records. The format of the data expected
7238 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
7239 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
7241 loadData : function(o, append){
7242 var r = this.reader.readRecords(o);
7243 this.loadRecords(r, {add: append}, true);
7247 * Gets the number of cached records.
7249 * <em>If using paging, this may not be the total size of the dataset. If the data object
7250 * used by the Reader contains the dataset size, then the getTotalCount() function returns
7251 * the data set size</em>
7253 getCount : function(){
7254 return this.data.length || 0;
7258 * Gets the total number of records in the dataset as returned by the server.
7260 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
7261 * the dataset size</em>
7263 getTotalCount : function(){
7264 return this.totalLength || 0;
7268 * Returns the sort state of the Store as an object with two properties:
7270 field {String} The name of the field by which the Records are sorted
7271 direction {String} The sort order, "ASC" or "DESC"
7274 getSortState : function(){
7275 return this.sortInfo;
7279 applySort : function(){
7280 if(this.sortInfo && !this.remoteSort){
7281 var s = this.sortInfo, f = s.field;
7282 var st = this.fields.get(f).sortType;
7283 var fn = function(r1, r2){
7284 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
7285 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
7287 this.data.sort(s.direction, fn);
7288 if(this.snapshot && this.snapshot != this.data){
7289 this.snapshot.sort(s.direction, fn);
7295 * Sets the default sort column and order to be used by the next load operation.
7296 * @param {String} fieldName The name of the field to sort by.
7297 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7299 setDefaultSort : function(field, dir){
7300 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
7305 * If remote sorting is used, the sort is performed on the server, and the cache is
7306 * reloaded. If local sorting is used, the cache is sorted internally.
7307 * @param {String} fieldName The name of the field to sort by.
7308 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
7310 sort : function(fieldName, dir){
7311 var f = this.fields.get(fieldName);
7313 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
7315 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
7316 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
7321 this.sortToggle[f.name] = dir;
7322 this.sortInfo = {field: f.name, direction: dir};
7323 if(!this.remoteSort){
7325 this.fireEvent("datachanged", this);
7327 this.load(this.lastOptions);
7332 * Calls the specified function for each of the Records in the cache.
7333 * @param {Function} fn The function to call. The Record is passed as the first parameter.
7334 * Returning <em>false</em> aborts and exits the iteration.
7335 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
7337 each : function(fn, scope){
7338 this.data.each(fn, scope);
7342 * Gets all records modified since the last commit. Modified records are persisted across load operations
7343 * (e.g., during paging).
7344 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
7346 getModifiedRecords : function(){
7347 return this.modified;
7351 createFilterFn : function(property, value, anyMatch){
7352 if(!value.exec){ // not a regex
7353 value = String(value);
7354 if(value.length == 0){
7357 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
7360 return value.test(r.data[property]);
7365 * Sums the value of <i>property</i> for each record between start and end and returns the result.
7366 * @param {String} property A field on your records
7367 * @param {Number} start The record index to start at (defaults to 0)
7368 * @param {Number} end The last record index to include (defaults to length - 1)
7369 * @return {Number} The sum
7371 sum : function(property, start, end){
7372 var rs = this.data.items, v = 0;
7374 end = (end || end === 0) ? end : rs.length-1;
7376 for(var i = start; i <= end; i++){
7377 v += (rs[i].data[property] || 0);
7383 * Filter the records by a specified property.
7384 * @param {String} field A field on your records
7385 * @param {String/RegExp} value Either a string that the field
7386 * should start with or a RegExp to test against the field
7387 * @param {Boolean} anyMatch True to match any part not just the beginning
7389 filter : function(property, value, anyMatch){
7390 var fn = this.createFilterFn(property, value, anyMatch);
7391 return fn ? this.filterBy(fn) : this.clearFilter();
7395 * Filter by a function. The specified function will be called with each
7396 * record in this data source. If the function returns true the record is included,
7397 * otherwise it is filtered.
7398 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7399 * @param {Object} scope (optional) The scope of the function (defaults to this)
7401 filterBy : function(fn, scope){
7402 this.snapshot = this.snapshot || this.data;
7403 this.data = this.queryBy(fn, scope||this);
7404 this.fireEvent("datachanged", this);
7408 * Query the records by a specified property.
7409 * @param {String} field A field on your records
7410 * @param {String/RegExp} value Either a string that the field
7411 * should start with or a RegExp to test against the field
7412 * @param {Boolean} anyMatch True to match any part not just the beginning
7413 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7415 query : function(property, value, anyMatch){
7416 var fn = this.createFilterFn(property, value, anyMatch);
7417 return fn ? this.queryBy(fn) : this.data.clone();
7421 * Query by a function. The specified function will be called with each
7422 * record in this data source. If the function returns true the record is included
7424 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
7425 * @param {Object} scope (optional) The scope of the function (defaults to this)
7426 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
7428 queryBy : function(fn, scope){
7429 var data = this.snapshot || this.data;
7430 return data.filterBy(fn, scope||this);
7434 * Collects unique values for a particular dataIndex from this store.
7435 * @param {String} dataIndex The property to collect
7436 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
7437 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
7438 * @return {Array} An array of the unique values
7440 collect : function(dataIndex, allowNull, bypassFilter){
7441 var d = (bypassFilter === true && this.snapshot) ?
7442 this.snapshot.items : this.data.items;
7443 var v, sv, r = [], l = {};
7444 for(var i = 0, len = d.length; i < len; i++){
7445 v = d[i].data[dataIndex];
7447 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
7456 * Revert to a view of the Record cache with no filtering applied.
7457 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
7459 clearFilter : function(suppressEvent){
7460 if(this.snapshot && this.snapshot != this.data){
7461 this.data = this.snapshot;
7462 delete this.snapshot;
7463 if(suppressEvent !== true){
7464 this.fireEvent("datachanged", this);
7470 afterEdit : function(record){
7471 if(this.modified.indexOf(record) == -1){
7472 this.modified.push(record);
7474 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
7478 afterReject : function(record){
7479 this.modified.remove(record);
7480 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
7484 afterCommit : function(record){
7485 this.modified.remove(record);
7486 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
7490 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
7491 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
7493 commitChanges : function(){
7494 var m = this.modified.slice(0);
7496 for(var i = 0, len = m.length; i < len; i++){
7502 * Cancel outstanding changes on all changed records.
7504 rejectChanges : function(){
7505 var m = this.modified.slice(0);
7507 for(var i = 0, len = m.length; i < len; i++){
7512 onMetaChange : function(meta, rtype, o){
7513 this.recordType = rtype;
7514 this.fields = rtype.prototype.fields;
7515 delete this.snapshot;
7516 this.sortInfo = meta.sortInfo || this.sortInfo;
7518 this.fireEvent('metachange', this, this.reader.meta);
7521 moveIndex : function(data, type)
7523 var index = this.indexOf(data);
7525 var newIndex = index + type;
7529 this.insert(newIndex, data);
7534 * Ext JS Library 1.1.1
7535 * Copyright(c) 2006-2007, Ext JS, LLC.
7537 * Originally Released Under LGPL - original licence link has changed is not relivant.
7540 * <script type="text/javascript">
7544 * @class Roo.data.SimpleStore
7545 * @extends Roo.data.Store
7546 * Small helper class to make creating Stores from Array data easier.
7547 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
7548 * @cfg {Array} fields An array of field definition objects, or field name strings.
7549 * @cfg {Array} data The multi-dimensional array of data
7551 * @param {Object} config
7553 Roo.data.SimpleStore = function(config){
7554 Roo.data.SimpleStore.superclass.constructor.call(this, {
7556 reader: new Roo.data.ArrayReader({
7559 Roo.data.Record.create(config.fields)
7561 proxy : new Roo.data.MemoryProxy(config.data)
7565 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
7567 * Ext JS Library 1.1.1
7568 * Copyright(c) 2006-2007, Ext JS, LLC.
7570 * Originally Released Under LGPL - original licence link has changed is not relivant.
7573 * <script type="text/javascript">
7578 * @extends Roo.data.Store
7579 * @class Roo.data.JsonStore
7580 * Small helper class to make creating Stores for JSON data easier. <br/>
7582 var store = new Roo.data.JsonStore({
7583 url: 'get-images.php',
7585 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
7588 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
7589 * JsonReader and HttpProxy (unless inline data is provided).</b>
7590 * @cfg {Array} fields An array of field definition objects, or field name strings.
7592 * @param {Object} config
7594 Roo.data.JsonStore = function(c){
7595 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
7596 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
7597 reader: new Roo.data.JsonReader(c, c.fields)
7600 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
7602 * Ext JS Library 1.1.1
7603 * Copyright(c) 2006-2007, Ext JS, LLC.
7605 * Originally Released Under LGPL - original licence link has changed is not relivant.
7608 * <script type="text/javascript">
7612 Roo.data.Field = function(config){
7613 if(typeof config == "string"){
7614 config = {name: config};
7616 Roo.apply(this, config);
7622 var st = Roo.data.SortTypes;
7623 // named sortTypes are supported, here we look them up
7624 if(typeof this.sortType == "string"){
7625 this.sortType = st[this.sortType];
7628 // set default sortType for strings and dates
7632 this.sortType = st.asUCString;
7635 this.sortType = st.asDate;
7638 this.sortType = st.none;
7643 var stripRe = /[\$,%]/g;
7645 // prebuilt conversion function for this field, instead of
7646 // switching every time we're reading a value
7648 var cv, dateFormat = this.dateFormat;
7653 cv = function(v){ return v; };
7656 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7660 return v !== undefined && v !== null && v !== '' ?
7661 parseInt(String(v).replace(stripRe, ""), 10) : '';
7666 return v !== undefined && v !== null && v !== '' ?
7667 parseFloat(String(v).replace(stripRe, ""), 10) : '';
7672 cv = function(v){ return v === true || v === "true" || v == 1; };
7679 if(v instanceof Date){
7683 if(dateFormat == "timestamp"){
7684 return new Date(v*1000);
7686 return Date.parseDate(v, dateFormat);
7688 var parsed = Date.parse(v);
7689 return parsed ? new Date(parsed) : null;
7698 Roo.data.Field.prototype = {
7706 * Ext JS Library 1.1.1
7707 * Copyright(c) 2006-2007, Ext JS, LLC.
7709 * Originally Released Under LGPL - original licence link has changed is not relivant.
7712 * <script type="text/javascript">
7715 // Base class for reading structured data from a data source. This class is intended to be
7716 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7719 * @class Roo.data.DataReader
7720 * Base class for reading structured data from a data source. This class is intended to be
7721 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7724 Roo.data.DataReader = function(meta, recordType){
7728 this.recordType = recordType instanceof Array ?
7729 Roo.data.Record.create(recordType) : recordType;
7732 Roo.data.DataReader.prototype = {
7734 * Create an empty record
7735 * @param {Object} data (optional) - overlay some values
7736 * @return {Roo.data.Record} record created.
7738 newRow : function(d) {
7740 this.recordType.prototype.fields.each(function(c) {
7742 case 'int' : da[c.name] = 0; break;
7743 case 'date' : da[c.name] = new Date(); break;
7744 case 'float' : da[c.name] = 0.0; break;
7745 case 'boolean' : da[c.name] = false; break;
7746 default : da[c.name] = ""; break;
7750 return new this.recordType(Roo.apply(da, d));
7755 * Ext JS Library 1.1.1
7756 * Copyright(c) 2006-2007, Ext JS, LLC.
7758 * Originally Released Under LGPL - original licence link has changed is not relivant.
7761 * <script type="text/javascript">
7765 * @class Roo.data.DataProxy
7766 * @extends Roo.data.Observable
7767 * This class is an abstract base class for implementations which provide retrieval of
7768 * unformatted data objects.<br>
7770 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7771 * (of the appropriate type which knows how to parse the data object) to provide a block of
7772 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7774 * Custom implementations must implement the load method as described in
7775 * {@link Roo.data.HttpProxy#load}.
7777 Roo.data.DataProxy = function(){
7781 * Fires before a network request is made to retrieve a data object.
7782 * @param {Object} This DataProxy object.
7783 * @param {Object} params The params parameter to the load function.
7788 * Fires before the load method's callback is called.
7789 * @param {Object} This DataProxy object.
7790 * @param {Object} o The data object.
7791 * @param {Object} arg The callback argument object passed to the load function.
7795 * @event loadexception
7796 * Fires if an Exception occurs during data retrieval.
7797 * @param {Object} This DataProxy object.
7798 * @param {Object} o The data object.
7799 * @param {Object} arg The callback argument object passed to the load function.
7800 * @param {Object} e The Exception.
7802 loadexception : true
7804 Roo.data.DataProxy.superclass.constructor.call(this);
7807 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7810 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7814 * Ext JS Library 1.1.1
7815 * Copyright(c) 2006-2007, Ext JS, LLC.
7817 * Originally Released Under LGPL - original licence link has changed is not relivant.
7820 * <script type="text/javascript">
7823 * @class Roo.data.MemoryProxy
7824 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7825 * to the Reader when its load method is called.
7827 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7829 Roo.data.MemoryProxy = function(data){
7833 Roo.data.MemoryProxy.superclass.constructor.call(this);
7837 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7839 * Load data from the requested source (in this case an in-memory
7840 * data object passed to the constructor), read the data object into
7841 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7842 * process that block using the passed callback.
7843 * @param {Object} params This parameter is not used by the MemoryProxy class.
7844 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7845 * object into a block of Roo.data.Records.
7846 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7847 * The function must be passed <ul>
7848 * <li>The Record block object</li>
7849 * <li>The "arg" argument from the load function</li>
7850 * <li>A boolean success indicator</li>
7852 * @param {Object} scope The scope in which to call the callback
7853 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7855 load : function(params, reader, callback, scope, arg){
7856 params = params || {};
7859 result = reader.readRecords(this.data);
7861 this.fireEvent("loadexception", this, arg, null, e);
7862 callback.call(scope, null, arg, false);
7865 callback.call(scope, result, arg, true);
7869 update : function(params, records){
7874 * Ext JS Library 1.1.1
7875 * Copyright(c) 2006-2007, Ext JS, LLC.
7877 * Originally Released Under LGPL - original licence link has changed is not relivant.
7880 * <script type="text/javascript">
7883 * @class Roo.data.HttpProxy
7884 * @extends Roo.data.DataProxy
7885 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7886 * configured to reference a certain URL.<br><br>
7888 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7889 * from which the running page was served.<br><br>
7891 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7893 * Be aware that to enable the browser to parse an XML document, the server must set
7894 * the Content-Type header in the HTTP response to "text/xml".
7896 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7897 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7898 * will be used to make the request.
7900 Roo.data.HttpProxy = function(conn){
7901 Roo.data.HttpProxy.superclass.constructor.call(this);
7902 // is conn a conn config or a real conn?
7904 this.useAjax = !conn || !conn.events;
7908 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7909 // thse are take from connection...
7912 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7915 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7916 * extra parameters to each request made by this object. (defaults to undefined)
7919 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7920 * to each request made by this object. (defaults to undefined)
7923 * @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)
7926 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7929 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7935 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7939 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7940 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7941 * a finer-grained basis than the DataProxy events.
7943 getConnection : function(){
7944 return this.useAjax ? Roo.Ajax : this.conn;
7948 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7949 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7950 * process that block using the passed callback.
7951 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7952 * for the request to the remote server.
7953 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7954 * object into a block of Roo.data.Records.
7955 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7956 * The function must be passed <ul>
7957 * <li>The Record block object</li>
7958 * <li>The "arg" argument from the load function</li>
7959 * <li>A boolean success indicator</li>
7961 * @param {Object} scope The scope in which to call the callback
7962 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7964 load : function(params, reader, callback, scope, arg){
7965 if(this.fireEvent("beforeload", this, params) !== false){
7967 params : params || {},
7969 callback : callback,
7974 callback : this.loadResponse,
7978 Roo.applyIf(o, this.conn);
7979 if(this.activeRequest){
7980 Roo.Ajax.abort(this.activeRequest);
7982 this.activeRequest = Roo.Ajax.request(o);
7984 this.conn.request(o);
7987 callback.call(scope||this, null, arg, false);
7992 loadResponse : function(o, success, response){
7993 delete this.activeRequest;
7995 this.fireEvent("loadexception", this, o, response);
7996 o.request.callback.call(o.request.scope, null, o.request.arg, false);
8001 result = o.reader.read(response);
8003 this.fireEvent("loadexception", this, o, response, e);
8004 o.request.callback.call(o.request.scope, null, o.request.arg, false);
8008 this.fireEvent("load", this, o, o.request.arg);
8009 o.request.callback.call(o.request.scope, result, o.request.arg, true);
8013 update : function(dataSet){
8018 updateResponse : function(dataSet){
8023 * Ext JS Library 1.1.1
8024 * Copyright(c) 2006-2007, Ext JS, LLC.
8026 * Originally Released Under LGPL - original licence link has changed is not relivant.
8029 * <script type="text/javascript">
8033 * @class Roo.data.ScriptTagProxy
8034 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
8035 * other than the originating domain of the running page.<br><br>
8037 * <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
8038 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
8040 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
8041 * source code that is used as the source inside a <script> tag.<br><br>
8043 * In order for the browser to process the returned data, the server must wrap the data object
8044 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
8045 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
8046 * depending on whether the callback name was passed:
8049 boolean scriptTag = false;
8050 String cb = request.getParameter("callback");
8053 response.setContentType("text/javascript");
8055 response.setContentType("application/x-json");
8057 Writer out = response.getWriter();
8059 out.write(cb + "(");
8061 out.print(dataBlock.toJsonString());
8068 * @param {Object} config A configuration object.
8070 Roo.data.ScriptTagProxy = function(config){
8071 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
8072 Roo.apply(this, config);
8073 this.head = document.getElementsByTagName("head")[0];
8076 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
8078 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
8080 * @cfg {String} url The URL from which to request the data object.
8083 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
8087 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
8088 * the server the name of the callback function set up by the load call to process the returned data object.
8089 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
8090 * javascript output which calls this named function passing the data object as its only parameter.
8092 callbackParam : "callback",
8094 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
8095 * name to the request.
8100 * Load data from the configured URL, read the data object into
8101 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
8102 * process that block using the passed callback.
8103 * @param {Object} params An object containing properties which are to be used as HTTP parameters
8104 * for the request to the remote server.
8105 * @param {Roo.data.DataReader} reader The Reader object which converts the data
8106 * object into a block of Roo.data.Records.
8107 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
8108 * The function must be passed <ul>
8109 * <li>The Record block object</li>
8110 * <li>The "arg" argument from the load function</li>
8111 * <li>A boolean success indicator</li>
8113 * @param {Object} scope The scope in which to call the callback
8114 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
8116 load : function(params, reader, callback, scope, arg){
8117 if(this.fireEvent("beforeload", this, params) !== false){
8119 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
8122 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
8124 url += "&_dc=" + (new Date().getTime());
8126 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
8129 cb : "stcCallback"+transId,
8130 scriptId : "stcScript"+transId,
8134 callback : callback,
8140 window[trans.cb] = function(o){
8141 conn.handleResponse(o, trans);
8144 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
8146 if(this.autoAbort !== false){
8150 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
8152 var script = document.createElement("script");
8153 script.setAttribute("src", url);
8154 script.setAttribute("type", "text/javascript");
8155 script.setAttribute("id", trans.scriptId);
8156 this.head.appendChild(script);
8160 callback.call(scope||this, null, arg, false);
8165 isLoading : function(){
8166 return this.trans ? true : false;
8170 * Abort the current server request.
8173 if(this.isLoading()){
8174 this.destroyTrans(this.trans);
8179 destroyTrans : function(trans, isLoaded){
8180 this.head.removeChild(document.getElementById(trans.scriptId));
8181 clearTimeout(trans.timeoutId);
8183 window[trans.cb] = undefined;
8185 delete window[trans.cb];
8188 // if hasn't been loaded, wait for load to remove it to prevent script error
8189 window[trans.cb] = function(){
8190 window[trans.cb] = undefined;
8192 delete window[trans.cb];
8199 handleResponse : function(o, trans){
8201 this.destroyTrans(trans, true);
8204 result = trans.reader.readRecords(o);
8206 this.fireEvent("loadexception", this, o, trans.arg, e);
8207 trans.callback.call(trans.scope||window, null, trans.arg, false);
8210 this.fireEvent("load", this, o, trans.arg);
8211 trans.callback.call(trans.scope||window, result, trans.arg, true);
8215 handleFailure : function(trans){
8217 this.destroyTrans(trans, false);
8218 this.fireEvent("loadexception", this, null, trans.arg);
8219 trans.callback.call(trans.scope||window, null, trans.arg, false);
8223 * Ext JS Library 1.1.1
8224 * Copyright(c) 2006-2007, Ext JS, LLC.
8226 * Originally Released Under LGPL - original licence link has changed is not relivant.
8229 * <script type="text/javascript">
8233 * @class Roo.data.JsonReader
8234 * @extends Roo.data.DataReader
8235 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
8236 * based on mappings in a provided Roo.data.Record constructor.
8238 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
8239 * in the reply previously.
8244 var RecordDef = Roo.data.Record.create([
8245 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
8246 {name: 'occupation'} // This field will use "occupation" as the mapping.
8248 var myReader = new Roo.data.JsonReader({
8249 totalProperty: "results", // The property which contains the total dataset size (optional)
8250 root: "rows", // The property which contains an Array of row objects
8251 id: "id" // The property within each row object that provides an ID for the record (optional)
8255 * This would consume a JSON file like this:
8257 { 'results': 2, 'rows': [
8258 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
8259 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
8262 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
8263 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
8264 * paged from the remote server.
8265 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
8266 * @cfg {String} root name of the property which contains the Array of row objects.
8267 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
8269 * Create a new JsonReader
8270 * @param {Object} meta Metadata configuration options
8271 * @param {Object} recordType Either an Array of field definition objects,
8272 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
8274 Roo.data.JsonReader = function(meta, recordType){
8277 // set some defaults:
8279 totalProperty: 'total',
8280 successProperty : 'success',
8285 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
8287 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
8290 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
8291 * Used by Store query builder to append _requestMeta to params.
8294 metaFromRemote : false,
8296 * This method is only used by a DataProxy which has retrieved data from a remote server.
8297 * @param {Object} response The XHR object which contains the JSON data in its responseText.
8298 * @return {Object} data A data block which is used by an Roo.data.Store object as
8299 * a cache of Roo.data.Records.
8301 read : function(response){
8302 var json = response.responseText;
8304 var o = /* eval:var:o */ eval("("+json+")");
8306 throw {message: "JsonReader.read: Json object not found"};
8312 this.metaFromRemote = true;
8313 this.meta = o.metaData;
8314 this.recordType = Roo.data.Record.create(o.metaData.fields);
8315 this.onMetaChange(this.meta, this.recordType, o);
8317 return this.readRecords(o);
8320 // private function a store will implement
8321 onMetaChange : function(meta, recordType, o){
8328 simpleAccess: function(obj, subsc) {
8335 getJsonAccessor: function(){
8337 return function(expr) {
8339 return(re.test(expr))
8340 ? new Function("obj", "return obj." + expr)
8350 * Create a data block containing Roo.data.Records from an XML document.
8351 * @param {Object} o An object which contains an Array of row objects in the property specified
8352 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
8353 * which contains the total size of the dataset.
8354 * @return {Object} data A data block which is used by an Roo.data.Store object as
8355 * a cache of Roo.data.Records.
8357 readRecords : function(o){
8359 * After any data loads, the raw JSON data is available for further custom processing.
8363 var s = this.meta, Record = this.recordType,
8364 f = Record.prototype.fields, fi = f.items, fl = f.length;
8366 // Generate extraction functions for the totalProperty, the root, the id, and for each field
8368 if(s.totalProperty) {
8369 this.getTotal = this.getJsonAccessor(s.totalProperty);
8371 if(s.successProperty) {
8372 this.getSuccess = this.getJsonAccessor(s.successProperty);
8374 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
8376 var g = this.getJsonAccessor(s.id);
8377 this.getId = function(rec) {
8379 return (r === undefined || r === "") ? null : r;
8382 this.getId = function(){return null;};
8385 for(var jj = 0; jj < fl; jj++){
8387 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
8388 this.ef[jj] = this.getJsonAccessor(map);
8392 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
8393 if(s.totalProperty){
8394 var vt = parseInt(this.getTotal(o), 10);
8399 if(s.successProperty){
8400 var vs = this.getSuccess(o);
8401 if(vs === false || vs === 'false'){
8406 for(var i = 0; i < c; i++){
8409 var id = this.getId(n);
8410 for(var j = 0; j < fl; j++){
8412 var v = this.ef[j](n);
8414 Roo.log('missing convert for ' + f.name);
8418 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
8420 var record = new Record(values, id);
8422 records[i] = record;
8428 totalRecords : totalRecords
8433 * Ext JS Library 1.1.1
8434 * Copyright(c) 2006-2007, Ext JS, LLC.
8436 * Originally Released Under LGPL - original licence link has changed is not relivant.
8439 * <script type="text/javascript">
8443 * @class Roo.data.ArrayReader
8444 * @extends Roo.data.DataReader
8445 * Data reader class to create an Array of Roo.data.Record objects from an Array.
8446 * Each element of that Array represents a row of data fields. The
8447 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
8448 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
8452 var RecordDef = Roo.data.Record.create([
8453 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
8454 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
8456 var myReader = new Roo.data.ArrayReader({
8457 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
8461 * This would consume an Array like this:
8463 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
8465 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
8467 * Create a new JsonReader
8468 * @param {Object} meta Metadata configuration options.
8469 * @param {Object} recordType Either an Array of field definition objects
8470 * as specified to {@link Roo.data.Record#create},
8471 * or an {@link Roo.data.Record} object
8472 * created using {@link Roo.data.Record#create}.
8474 Roo.data.ArrayReader = function(meta, recordType){
8475 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
8478 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
8480 * Create a data block containing Roo.data.Records from an XML document.
8481 * @param {Object} o An Array of row objects which represents the dataset.
8482 * @return {Object} data A data block which is used by an Roo.data.Store object as
8483 * a cache of Roo.data.Records.
8485 readRecords : function(o){
8486 var sid = this.meta ? this.meta.id : null;
8487 var recordType = this.recordType, fields = recordType.prototype.fields;
8490 for(var i = 0; i < root.length; i++){
8493 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
8494 for(var j = 0, jlen = fields.length; j < jlen; j++){
8495 var f = fields.items[j];
8496 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
8497 var v = n[k] !== undefined ? n[k] : f.defaultValue;
8501 var record = new recordType(values, id);
8503 records[records.length] = record;
8507 totalRecords : records.length
8516 * @class Roo.bootstrap.ComboBox
8517 * @extends Roo.bootstrap.TriggerField
8518 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
8519 * @cfg {Boolean} append (true|false) default false
8521 * Create a new ComboBox.
8522 * @param {Object} config Configuration options
8524 Roo.bootstrap.ComboBox = function(config){
8525 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
8529 * Fires when the dropdown list is expanded
8530 * @param {Roo.bootstrap.ComboBox} combo This combo box
8535 * Fires when the dropdown list is collapsed
8536 * @param {Roo.bootstrap.ComboBox} combo This combo box
8540 * @event beforeselect
8541 * Fires before a list item is selected. Return false to cancel the selection.
8542 * @param {Roo.bootstrap.ComboBox} combo This combo box
8543 * @param {Roo.data.Record} record The data record returned from the underlying store
8544 * @param {Number} index The index of the selected item in the dropdown list
8546 'beforeselect' : true,
8549 * Fires when a list item is selected
8550 * @param {Roo.bootstrap.ComboBox} combo This combo box
8551 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
8552 * @param {Number} index The index of the selected item in the dropdown list
8556 * @event beforequery
8557 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
8558 * The event object passed has these properties:
8559 * @param {Roo.bootstrap.ComboBox} combo This combo box
8560 * @param {String} query The query
8561 * @param {Boolean} forceAll true to force "all" query
8562 * @param {Boolean} cancel true to cancel the query
8563 * @param {Object} e The query event object
8565 'beforequery': true,
8568 * Fires when the 'add' icon is pressed (add a listener to enable add button)
8569 * @param {Roo.bootstrap.ComboBox} combo This combo box
8574 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
8575 * @param {Roo.bootstrap.ComboBox} combo This combo box
8576 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
8581 * Fires when the remove value from the combobox array
8582 * @param {Roo.bootstrap.ComboBox} combo This combo box
8589 this.selectedIndex = -1;
8590 if(this.mode == 'local'){
8591 if(config.queryDelay === undefined){
8592 this.queryDelay = 10;
8594 if(config.minChars === undefined){
8600 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
8603 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
8604 * rendering into an Roo.Editor, defaults to false)
8607 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
8608 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
8611 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
8614 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
8615 * the dropdown list (defaults to undefined, with no header element)
8619 * @cfg {String/Roo.Template} tpl The template to use to render the output
8623 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
8625 listWidth: undefined,
8627 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
8628 * mode = 'remote' or 'text' if mode = 'local')
8630 displayField: undefined,
8632 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8633 * mode = 'remote' or 'value' if mode = 'local').
8634 * Note: use of a valueField requires the user make a selection
8635 * in order for a value to be mapped.
8637 valueField: undefined,
8641 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8642 * field's data value (defaults to the underlying DOM element's name)
8644 hiddenName: undefined,
8646 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8650 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8652 selectedClass: 'active',
8655 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8659 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8660 * anchor positions (defaults to 'tl-bl')
8662 listAlign: 'tl-bl?',
8664 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8668 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
8669 * query specified by the allQuery config option (defaults to 'query')
8671 triggerAction: 'query',
8673 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8674 * (defaults to 4, does not apply if editable = false)
8678 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8679 * delay (typeAheadDelay) if it matches a known value (defaults to false)
8683 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8684 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8688 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8689 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
8693 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
8694 * when editable = true (defaults to false)
8696 selectOnFocus:false,
8698 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8700 queryParam: 'query',
8702 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
8703 * when mode = 'remote' (defaults to 'Loading...')
8705 loadingText: 'Loading...',
8707 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8711 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8715 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8716 * traditional select (defaults to true)
8720 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8724 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8728 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8729 * listWidth has a higher value)
8733 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8734 * allow the user to set arbitrary text into the field (defaults to false)
8736 forceSelection:false,
8738 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8739 * if typeAhead = true (defaults to 250)
8741 typeAheadDelay : 250,
8743 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8744 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8746 valueNotFoundText : undefined,
8748 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8753 * @cfg {Boolean} disableClear Disable showing of clear button.
8755 disableClear : false,
8757 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
8759 alwaysQuery : false,
8762 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8776 // element that contains real text value.. (when hidden is used..)
8779 initEvents: function(){
8782 throw "can not find store for combo";
8784 this.store = Roo.factory(this.store, Roo.data);
8788 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8791 if(this.hiddenName){
8793 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8795 this.hiddenField.dom.value =
8796 this.hiddenValue !== undefined ? this.hiddenValue :
8797 this.value !== undefined ? this.value : '';
8799 // prevent input submission
8800 this.el.dom.removeAttribute('name');
8801 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8806 // this.el.dom.setAttribute('autocomplete', 'off');
8809 var cls = 'x-combo-list';
8810 this.list = this.el.select('ul.dropdown-menu',true).first();
8812 //this.list = new Roo.Layer({
8813 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8816 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8817 this.list.setWidth(lw);
8819 this.list.on('mouseover', this.onViewOver, this);
8820 this.list.on('mousemove', this.onViewMove, this);
8822 this.list.on('scroll', this.onViewScroll, this);
8825 this.list.swallowEvent('mousewheel');
8826 this.assetHeight = 0;
8829 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8830 this.assetHeight += this.header.getHeight();
8833 this.innerList = this.list.createChild({cls:cls+'-inner'});
8834 this.innerList.on('mouseover', this.onViewOver, this);
8835 this.innerList.on('mousemove', this.onViewMove, this);
8836 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8838 if(this.allowBlank && !this.pageSize && !this.disableClear){
8839 this.footer = this.list.createChild({cls:cls+'-ft'});
8840 this.pageTb = new Roo.Toolbar(this.footer);
8844 this.footer = this.list.createChild({cls:cls+'-ft'});
8845 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8846 {pageSize: this.pageSize});
8850 if (this.pageTb && this.allowBlank && !this.disableClear) {
8852 this.pageTb.add(new Roo.Toolbar.Fill(), {
8853 cls: 'x-btn-icon x-btn-clear',
8859 _this.onSelect(false, -1);
8864 this.assetHeight += this.footer.getHeight();
8869 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8872 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8873 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8875 //this.view.wrapEl.setDisplayed(false);
8876 this.view.on('click', this.onViewClick, this);
8880 this.store.on('beforeload', this.onBeforeLoad, this);
8881 this.store.on('load', this.onLoad, this);
8882 this.store.on('loadexception', this.onLoadException, this);
8885 this.resizer = new Roo.Resizable(this.list, {
8886 pinned:true, handles:'se'
8888 this.resizer.on('resize', function(r, w, h){
8889 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8891 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8892 this.restrictHeight();
8894 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8898 this.editable = true;
8899 this.setEditable(false);
8904 if (typeof(this.events.add.listeners) != 'undefined') {
8906 this.addicon = this.wrap.createChild(
8907 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8909 this.addicon.on('click', function(e) {
8910 this.fireEvent('add', this);
8913 if (typeof(this.events.edit.listeners) != 'undefined') {
8915 this.editicon = this.wrap.createChild(
8916 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8918 this.editicon.setStyle('margin-left', '40px');
8920 this.editicon.on('click', function(e) {
8922 // we fire even if inothing is selected..
8923 this.fireEvent('edit', this, this.lastData );
8929 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8931 this.inKeyMode = true;
8935 "down" : function(e){
8936 if(!this.isExpanded()){
8937 this.onTriggerClick();
8939 this.inKeyMode = true;
8944 "enter" : function(e){
8949 "esc" : function(e){
8953 "tab" : function(e){
8956 if(this.fireEvent("specialkey", this, e)){
8957 this.onViewClick(false);
8965 doRelay : function(foo, bar, hname){
8966 if(hname == 'down' || this.scope.isExpanded()){
8967 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8976 this.queryDelay = Math.max(this.queryDelay || 10,
8977 this.mode == 'local' ? 10 : 250);
8980 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8983 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8985 if(this.editable !== false){
8986 this.inputEl().on("keyup", this.onKeyUp, this);
8988 if(this.forceSelection){
8989 this.on('blur', this.doForce, this);
8993 this.choices = this.el.select('ul.select2-choices', true).first();
8994 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8998 onDestroy : function(){
9000 this.view.setStore(null);
9001 this.view.el.removeAllListeners();
9002 this.view.el.remove();
9003 this.view.purgeListeners();
9006 this.list.dom.innerHTML = '';
9009 this.store.un('beforeload', this.onBeforeLoad, this);
9010 this.store.un('load', this.onLoad, this);
9011 this.store.un('loadexception', this.onLoadException, this);
9013 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
9017 fireKey : function(e){
9018 if(e.isNavKeyPress() && !this.list.isVisible()){
9019 this.fireEvent("specialkey", this, e);
9024 onResize: function(w, h){
9025 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
9027 // if(typeof w != 'number'){
9028 // // we do not handle it!?!?
9031 // var tw = this.trigger.getWidth();
9032 // // tw += this.addicon ? this.addicon.getWidth() : 0;
9033 // // tw += this.editicon ? this.editicon.getWidth() : 0;
9035 // this.inputEl().setWidth( this.adjustWidth('input', x));
9037 // //this.trigger.setStyle('left', x+'px');
9039 // if(this.list && this.listWidth === undefined){
9040 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
9041 // this.list.setWidth(lw);
9042 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
9050 * Allow or prevent the user from directly editing the field text. If false is passed,
9051 * the user will only be able to select from the items defined in the dropdown list. This method
9052 * is the runtime equivalent of setting the 'editable' config option at config time.
9053 * @param {Boolean} value True to allow the user to directly edit the field text
9055 setEditable : function(value){
9056 if(value == this.editable){
9059 this.editable = value;
9061 this.inputEl().dom.setAttribute('readOnly', true);
9062 this.inputEl().on('mousedown', this.onTriggerClick, this);
9063 this.inputEl().addClass('x-combo-noedit');
9065 this.inputEl().dom.setAttribute('readOnly', false);
9066 this.inputEl().un('mousedown', this.onTriggerClick, this);
9067 this.inputEl().removeClass('x-combo-noedit');
9073 onBeforeLoad : function(combo,opts){
9078 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
9080 this.restrictHeight();
9081 this.selectedIndex = -1;
9085 onLoad : function(){
9087 this.hasQuery = false;
9093 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9094 this.loading.hide();
9097 if(this.store.getCount() > 0){
9099 this.restrictHeight();
9100 if(this.lastQuery == this.allQuery){
9102 this.inputEl().dom.select();
9104 if(!this.selectByValue(this.value, true)){
9105 this.select(0, true);
9109 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
9110 this.taTask.delay(this.typeAheadDelay);
9114 this.onEmptyResults();
9120 onLoadException : function()
9122 this.hasQuery = false;
9124 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
9125 this.loading.hide();
9129 Roo.log(this.store.reader.jsonData);
9130 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
9132 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
9138 onTypeAhead : function(){
9139 if(this.store.getCount() > 0){
9140 var r = this.store.getAt(0);
9141 var newValue = r.data[this.displayField];
9142 var len = newValue.length;
9143 var selStart = this.getRawValue().length;
9145 if(selStart != len){
9146 this.setRawValue(newValue);
9147 this.selectText(selStart, newValue.length);
9153 onSelect : function(record, index){
9155 if(this.fireEvent('beforeselect', this, record, index) !== false){
9157 this.setFromData(index > -1 ? record.data : false);
9160 this.fireEvent('select', this, record, index);
9165 * Returns the currently selected field value or empty string if no value is set.
9166 * @return {String} value The selected value
9168 getValue : function(){
9171 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
9174 if(this.valueField){
9175 return typeof this.value != 'undefined' ? this.value : '';
9177 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
9182 * Clears any text/value currently set in the field
9184 clearValue : function(){
9185 if(this.hiddenField){
9186 this.hiddenField.dom.value = '';
9189 this.setRawValue('');
9190 this.lastSelectionText = '';
9195 * Sets the specified value into the field. If the value finds a match, the corresponding record text
9196 * will be displayed in the field. If the value does not match the data value of an existing item,
9197 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
9198 * Otherwise the field will be blank (although the value will still be set).
9199 * @param {String} value The value to match
9201 setValue : function(v){
9208 if(this.valueField){
9209 var r = this.findRecord(this.valueField, v);
9211 text = r.data[this.displayField];
9212 }else if(this.valueNotFoundText !== undefined){
9213 text = this.valueNotFoundText;
9216 this.lastSelectionText = text;
9217 if(this.hiddenField){
9218 this.hiddenField.dom.value = v;
9220 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
9224 * @property {Object} the last set data for the element
9229 * Sets the value of the field based on a object which is related to the record format for the store.
9230 * @param {Object} value the value to set as. or false on reset?
9232 setFromData : function(o){
9239 var dv = ''; // display value
9240 var vv = ''; // value value..
9242 if (this.displayField) {
9243 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9245 // this is an error condition!!!
9246 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
9249 if(this.valueField){
9250 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
9253 if(this.hiddenField){
9254 this.hiddenField.dom.value = vv;
9256 this.lastSelectionText = dv;
9257 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9261 // no hidden field.. - we store the value in 'value', but still display
9262 // display field!!!!
9263 this.lastSelectionText = dv;
9264 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
9271 // overridden so that last data is reset..
9272 this.setValue(this.originalValue);
9273 this.clearInvalid();
9274 this.lastData = false;
9276 this.view.clearSelections();
9280 findRecord : function(prop, value){
9282 if(this.store.getCount() > 0){
9283 this.store.each(function(r){
9284 if(r.data[prop] == value){
9296 // returns hidden if it's set..
9297 if (!this.rendered) {return ''};
9298 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
9302 onViewMove : function(e, t){
9303 this.inKeyMode = false;
9307 onViewOver : function(e, t){
9308 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
9311 var item = this.view.findItemFromChild(t);
9313 var index = this.view.indexOf(item);
9314 this.select(index, false);
9319 onViewClick : function(doFocus)
9321 var index = this.view.getSelectedIndexes()[0];
9322 var r = this.store.getAt(index);
9324 this.onSelect(r, index);
9326 if(doFocus !== false && !this.blockFocus){
9327 this.inputEl().focus();
9332 restrictHeight : function(){
9333 //this.innerList.dom.style.height = '';
9334 //var inner = this.innerList.dom;
9335 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
9336 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
9337 //this.list.beginUpdate();
9338 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
9339 this.list.alignTo(this.inputEl(), this.listAlign);
9340 //this.list.endUpdate();
9344 onEmptyResults : function(){
9349 * Returns true if the dropdown list is expanded, else false.
9351 isExpanded : function(){
9352 return this.list.isVisible();
9356 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
9357 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9358 * @param {String} value The data value of the item to select
9359 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9360 * selected item if it is not currently in view (defaults to true)
9361 * @return {Boolean} True if the value matched an item in the list, else false
9363 selectByValue : function(v, scrollIntoView){
9364 if(v !== undefined && v !== null){
9365 var r = this.findRecord(this.valueField || this.displayField, v);
9367 this.select(this.store.indexOf(r), scrollIntoView);
9375 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
9376 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
9377 * @param {Number} index The zero-based index of the list item to select
9378 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
9379 * selected item if it is not currently in view (defaults to true)
9381 select : function(index, scrollIntoView){
9382 this.selectedIndex = index;
9383 this.view.select(index);
9384 if(scrollIntoView !== false){
9385 var el = this.view.getNode(index);
9387 //this.innerList.scrollChildIntoView(el, false);
9394 selectNext : function(){
9395 var ct = this.store.getCount();
9397 if(this.selectedIndex == -1){
9399 }else if(this.selectedIndex < ct-1){
9400 this.select(this.selectedIndex+1);
9406 selectPrev : function(){
9407 var ct = this.store.getCount();
9409 if(this.selectedIndex == -1){
9411 }else if(this.selectedIndex != 0){
9412 this.select(this.selectedIndex-1);
9418 onKeyUp : function(e){
9419 if(this.editable !== false && !e.isSpecialKey()){
9420 this.lastKey = e.getKey();
9421 this.dqTask.delay(this.queryDelay);
9426 validateBlur : function(){
9427 return !this.list || !this.list.isVisible();
9431 initQuery : function(){
9432 this.doQuery(this.getRawValue());
9436 doForce : function(){
9437 if(this.el.dom.value.length > 0){
9439 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
9445 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
9446 * query allowing the query action to be canceled if needed.
9447 * @param {String} query The SQL query to execute
9448 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
9449 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
9450 * saved in the current store (defaults to false)
9452 doQuery : function(q, forceAll){
9454 if(q === undefined || q === null){
9463 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
9468 forceAll = qe.forceAll;
9469 if(forceAll === true || (q.length >= this.minChars)){
9471 this.hasQuery = true;
9473 if(this.lastQuery != q || this.alwaysQuery){
9475 if(this.mode == 'local'){
9476 this.selectedIndex = -1;
9478 this.store.clearFilter();
9480 this.store.filter(this.displayField, q);
9484 this.store.baseParams[this.queryParam] = q;
9486 var options = {params : this.getParams(q)};
9490 options.params.start = this.page * this.pageSize;
9493 this.store.load(options);
9497 this.selectedIndex = -1;
9502 this.loadNext = false;
9506 getParams : function(q){
9508 //p[this.queryParam] = q;
9512 p.limit = this.pageSize;
9518 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
9520 collapse : function(){
9521 if(!this.isExpanded()){
9526 Roo.get(document).un('mousedown', this.collapseIf, this);
9527 Roo.get(document).un('mousewheel', this.collapseIf, this);
9528 if (!this.editable) {
9529 Roo.get(document).un('keydown', this.listKeyPress, this);
9531 this.fireEvent('collapse', this);
9535 collapseIf : function(e){
9536 var in_combo = e.within(this.el);
9537 var in_list = e.within(this.list);
9539 if (in_combo || in_list) {
9540 //e.stopPropagation();
9549 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
9551 expand : function(){
9553 if(this.isExpanded() || !this.hasFocus){
9557 this.list.alignTo(this.inputEl(), this.listAlign);
9559 Roo.get(document).on('mousedown', this.collapseIf, this);
9560 Roo.get(document).on('mousewheel', this.collapseIf, this);
9561 if (!this.editable) {
9562 Roo.get(document).on('keydown', this.listKeyPress, this);
9565 this.fireEvent('expand', this);
9569 // Implements the default empty TriggerField.onTriggerClick function
9570 onTriggerClick : function()
9572 Roo.log('trigger click');
9579 this.loadNext = false;
9581 if(this.isExpanded()){
9583 if (!this.blockFocus) {
9584 this.inputEl().focus();
9588 this.hasFocus = true;
9589 if(this.triggerAction == 'all') {
9590 this.doQuery(this.allQuery, true);
9592 this.doQuery(this.getRawValue());
9594 if (!this.blockFocus) {
9595 this.inputEl().focus();
9599 listKeyPress : function(e)
9601 //Roo.log('listkeypress');
9602 // scroll to first matching element based on key pres..
9603 if (e.isSpecialKey()) {
9606 var k = String.fromCharCode(e.getKey()).toUpperCase();
9609 var csel = this.view.getSelectedNodes();
9610 var cselitem = false;
9612 var ix = this.view.indexOf(csel[0]);
9613 cselitem = this.store.getAt(ix);
9614 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
9620 this.store.each(function(v) {
9622 // start at existing selection.
9623 if (cselitem.id == v.id) {
9629 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9630 match = this.store.indexOf(v);
9636 if (match === false) {
9637 return true; // no more action?
9640 this.view.select(match);
9641 var sn = Roo.get(this.view.getSelectedNodes()[0])
9642 //sn.scrollIntoView(sn.dom.parentNode, false);
9645 onViewScroll : function(e, t){
9647 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9651 this.hasQuery = true;
9653 this.loading = this.list.select('.loading', true).first();
9655 if(this.loading === null){
9656 this.list.createChild({
9658 cls: 'loading select2-more-results select2-active',
9659 html: 'Loading more results...'
9662 this.loading = this.list.select('.loading', true).first();
9664 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9666 this.loading.hide();
9669 this.loading.show();
9674 this.loadNext = true;
9676 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9681 addItem : function(o)
9683 var dv = ''; // display value
9685 if (this.displayField) {
9686 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9688 // this is an error condition!!!
9689 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
9696 var choice = this.choices.createChild({
9698 cls: 'select2-search-choice',
9707 cls: 'select2-search-choice-close',
9712 }, this.searchField);
9714 var close = choice.select('a.select2-search-choice-close', true).first()
9716 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9723 this.inputEl().dom.value = '';
9727 onRemoveItem : function(e, _self, o)
9729 Roo.log('remove item');
9730 var index = this.item.indexOf(o.data) * 1;
9733 Roo.log('not this item?!');
9737 this.item.splice(index, 1);
9742 this.fireEvent('remove', this);
9746 syncValue : function()
9748 if(!this.item.length){
9755 Roo.each(this.item, function(i){
9756 if(_this.valueField){
9757 value.push(i[_this.valueField]);
9764 this.value = value.join(',');
9766 if(this.hiddenField){
9767 this.hiddenField.dom.value = this.value;
9771 clearItem : function()
9779 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9789 * @cfg {Boolean} grow
9793 * @cfg {Number} growMin
9797 * @cfg {Number} growMax
9807 * Ext JS Library 1.1.1
9808 * Copyright(c) 2006-2007, Ext JS, LLC.
9810 * Originally Released Under LGPL - original licence link has changed is not relivant.
9813 * <script type="text/javascript">
9818 * @extends Roo.util.Observable
9819 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9820 * This class also supports single and multi selection modes. <br>
9821 * Create a data model bound view:
9823 var store = new Roo.data.Store(...);
9825 var view = new Roo.View({
9827 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9830 selectedClass: "ydataview-selected",
9834 // listen for node click?
9835 view.on("click", function(vw, index, node, e){
9836 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9840 dataModel.load("foobar.xml");
9842 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9844 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9845 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9847 * Note: old style constructor is still suported (container, template, config)
9851 * @param {Object} config The config object
9854 Roo.View = function(config, depreciated_tpl, depreciated_config){
9856 if (typeof(depreciated_tpl) == 'undefined') {
9857 // new way.. - universal constructor.
9858 Roo.apply(this, config);
9859 this.el = Roo.get(this.el);
9862 this.el = Roo.get(config);
9863 this.tpl = depreciated_tpl;
9864 Roo.apply(this, depreciated_config);
9866 this.wrapEl = this.el.wrap().wrap();
9867 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9870 if(typeof(this.tpl) == "string"){
9871 this.tpl = new Roo.Template(this.tpl);
9873 // support xtype ctors..
9874 this.tpl = new Roo.factory(this.tpl, Roo);
9886 * @event beforeclick
9887 * Fires before a click is processed. Returns false to cancel the default action.
9888 * @param {Roo.View} this
9889 * @param {Number} index The index of the target node
9890 * @param {HTMLElement} node The target node
9891 * @param {Roo.EventObject} e The raw event object
9893 "beforeclick" : true,
9896 * Fires when a template node is clicked.
9897 * @param {Roo.View} this
9898 * @param {Number} index The index of the target node
9899 * @param {HTMLElement} node The target node
9900 * @param {Roo.EventObject} e The raw event object
9905 * Fires when a template node is double clicked.
9906 * @param {Roo.View} this
9907 * @param {Number} index The index of the target node
9908 * @param {HTMLElement} node The target node
9909 * @param {Roo.EventObject} e The raw event object
9913 * @event contextmenu
9914 * Fires when a template node is right clicked.
9915 * @param {Roo.View} this
9916 * @param {Number} index The index of the target node
9917 * @param {HTMLElement} node The target node
9918 * @param {Roo.EventObject} e The raw event object
9920 "contextmenu" : true,
9922 * @event selectionchange
9923 * Fires when the selected nodes change.
9924 * @param {Roo.View} this
9925 * @param {Array} selections Array of the selected nodes
9927 "selectionchange" : true,
9930 * @event beforeselect
9931 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9932 * @param {Roo.View} this
9933 * @param {HTMLElement} node The node to be selected
9934 * @param {Array} selections Array of currently selected nodes
9936 "beforeselect" : true,
9938 * @event preparedata
9939 * Fires on every row to render, to allow you to change the data.
9940 * @param {Roo.View} this
9941 * @param {Object} data to be rendered (change this)
9943 "preparedata" : true
9951 "click": this.onClick,
9952 "dblclick": this.onDblClick,
9953 "contextmenu": this.onContextMenu,
9957 this.selections = [];
9959 this.cmp = new Roo.CompositeElementLite([]);
9961 this.store = Roo.factory(this.store, Roo.data);
9962 this.setStore(this.store, true);
9965 if ( this.footer && this.footer.xtype) {
9967 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9969 this.footer.dataSource = this.store
9970 this.footer.container = fctr;
9971 this.footer = Roo.factory(this.footer, Roo);
9972 fctr.insertFirst(this.el);
9974 // this is a bit insane - as the paging toolbar seems to detach the el..
9975 // dom.parentNode.parentNode.parentNode
9976 // they get detached?
9980 Roo.View.superclass.constructor.call(this);
9985 Roo.extend(Roo.View, Roo.util.Observable, {
9988 * @cfg {Roo.data.Store} store Data store to load data from.
9993 * @cfg {String|Roo.Element} el The container element.
9998 * @cfg {String|Roo.Template} tpl The template used by this View
10002 * @cfg {String} dataName the named area of the template to use as the data area
10003 * Works with domtemplates roo-name="name"
10007 * @cfg {String} selectedClass The css class to add to selected nodes
10009 selectedClass : "x-view-selected",
10011 * @cfg {String} emptyText The empty text to show when nothing is loaded.
10016 * @cfg {String} text to display on mask (default Loading)
10020 * @cfg {Boolean} multiSelect Allow multiple selection
10022 multiSelect : false,
10024 * @cfg {Boolean} singleSelect Allow single selection
10026 singleSelect: false,
10029 * @cfg {Boolean} toggleSelect - selecting
10031 toggleSelect : false,
10034 * Returns the element this view is bound to.
10035 * @return {Roo.Element}
10037 getEl : function(){
10038 return this.wrapEl;
10044 * Refreshes the view. - called by datachanged on the store. - do not call directly.
10046 refresh : function(){
10047 Roo.log('refresh');
10050 // if we are using something like 'domtemplate', then
10051 // the what gets used is:
10052 // t.applySubtemplate(NAME, data, wrapping data..)
10053 // the outer template then get' applied with
10054 // the store 'extra data'
10055 // and the body get's added to the
10056 // roo-name="data" node?
10057 // <span class='roo-tpl-{name}'></span> ?????
10061 this.clearSelections();
10062 this.el.update("");
10064 var records = this.store.getRange();
10065 if(records.length < 1) {
10067 // is this valid?? = should it render a template??
10069 this.el.update(this.emptyText);
10073 if (this.dataName) {
10074 this.el.update(t.apply(this.store.meta)); //????
10075 el = this.el.child('.roo-tpl-' + this.dataName);
10078 for(var i = 0, len = records.length; i < len; i++){
10079 var data = this.prepareData(records[i].data, i, records[i]);
10080 this.fireEvent("preparedata", this, data, i, records[i]);
10081 html[html.length] = Roo.util.Format.trim(
10083 t.applySubtemplate(this.dataName, data, this.store.meta) :
10090 el.update(html.join(""));
10091 this.nodes = el.dom.childNodes;
10092 this.updateIndexes(0);
10097 * Function to override to reformat the data that is sent to
10098 * the template for each node.
10099 * DEPRICATED - use the preparedata event handler.
10100 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
10101 * a JSON object for an UpdateManager bound view).
10103 prepareData : function(data, index, record)
10105 this.fireEvent("preparedata", this, data, index, record);
10109 onUpdate : function(ds, record){
10110 Roo.log('on update');
10111 this.clearSelections();
10112 var index = this.store.indexOf(record);
10113 var n = this.nodes[index];
10114 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
10115 n.parentNode.removeChild(n);
10116 this.updateIndexes(index, index);
10122 onAdd : function(ds, records, index)
10124 Roo.log(['on Add', ds, records, index] );
10125 this.clearSelections();
10126 if(this.nodes.length == 0){
10130 var n = this.nodes[index];
10131 for(var i = 0, len = records.length; i < len; i++){
10132 var d = this.prepareData(records[i].data, i, records[i]);
10134 this.tpl.insertBefore(n, d);
10137 this.tpl.append(this.el, d);
10140 this.updateIndexes(index);
10143 onRemove : function(ds, record, index){
10144 Roo.log('onRemove');
10145 this.clearSelections();
10146 var el = this.dataName ?
10147 this.el.child('.roo-tpl-' + this.dataName) :
10150 el.dom.removeChild(this.nodes[index]);
10151 this.updateIndexes(index);
10155 * Refresh an individual node.
10156 * @param {Number} index
10158 refreshNode : function(index){
10159 this.onUpdate(this.store, this.store.getAt(index));
10162 updateIndexes : function(startIndex, endIndex){
10163 var ns = this.nodes;
10164 startIndex = startIndex || 0;
10165 endIndex = endIndex || ns.length - 1;
10166 for(var i = startIndex; i <= endIndex; i++){
10167 ns[i].nodeIndex = i;
10172 * Changes the data store this view uses and refresh the view.
10173 * @param {Store} store
10175 setStore : function(store, initial){
10176 if(!initial && this.store){
10177 this.store.un("datachanged", this.refresh);
10178 this.store.un("add", this.onAdd);
10179 this.store.un("remove", this.onRemove);
10180 this.store.un("update", this.onUpdate);
10181 this.store.un("clear", this.refresh);
10182 this.store.un("beforeload", this.onBeforeLoad);
10183 this.store.un("load", this.onLoad);
10184 this.store.un("loadexception", this.onLoad);
10188 store.on("datachanged", this.refresh, this);
10189 store.on("add", this.onAdd, this);
10190 store.on("remove", this.onRemove, this);
10191 store.on("update", this.onUpdate, this);
10192 store.on("clear", this.refresh, this);
10193 store.on("beforeload", this.onBeforeLoad, this);
10194 store.on("load", this.onLoad, this);
10195 store.on("loadexception", this.onLoad, this);
10203 * onbeforeLoad - masks the loading area.
10206 onBeforeLoad : function(store,opts)
10208 Roo.log('onBeforeLoad');
10210 this.el.update("");
10212 this.el.mask(this.mask ? this.mask : "Loading" );
10214 onLoad : function ()
10221 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
10222 * @param {HTMLElement} node
10223 * @return {HTMLElement} The template node
10225 findItemFromChild : function(node){
10226 var el = this.dataName ?
10227 this.el.child('.roo-tpl-' + this.dataName,true) :
10230 if(!node || node.parentNode == el){
10233 var p = node.parentNode;
10234 while(p && p != el){
10235 if(p.parentNode == el){
10244 onClick : function(e){
10245 var item = this.findItemFromChild(e.getTarget());
10247 var index = this.indexOf(item);
10248 if(this.onItemClick(item, index, e) !== false){
10249 this.fireEvent("click", this, index, item, e);
10252 this.clearSelections();
10257 onContextMenu : function(e){
10258 var item = this.findItemFromChild(e.getTarget());
10260 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
10265 onDblClick : function(e){
10266 var item = this.findItemFromChild(e.getTarget());
10268 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
10272 onItemClick : function(item, index, e)
10274 if(this.fireEvent("beforeclick", this, index, item, e) === false){
10277 if (this.toggleSelect) {
10278 var m = this.isSelected(item) ? 'unselect' : 'select';
10281 _t[m](item, true, false);
10284 if(this.multiSelect || this.singleSelect){
10285 if(this.multiSelect && e.shiftKey && this.lastSelection){
10286 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
10288 this.select(item, this.multiSelect && e.ctrlKey);
10289 this.lastSelection = item;
10291 e.preventDefault();
10297 * Get the number of selected nodes.
10300 getSelectionCount : function(){
10301 return this.selections.length;
10305 * Get the currently selected nodes.
10306 * @return {Array} An array of HTMLElements
10308 getSelectedNodes : function(){
10309 return this.selections;
10313 * Get the indexes of the selected nodes.
10316 getSelectedIndexes : function(){
10317 var indexes = [], s = this.selections;
10318 for(var i = 0, len = s.length; i < len; i++){
10319 indexes.push(s[i].nodeIndex);
10325 * Clear all selections
10326 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
10328 clearSelections : function(suppressEvent){
10329 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
10330 this.cmp.elements = this.selections;
10331 this.cmp.removeClass(this.selectedClass);
10332 this.selections = [];
10333 if(!suppressEvent){
10334 this.fireEvent("selectionchange", this, this.selections);
10340 * Returns true if the passed node is selected
10341 * @param {HTMLElement/Number} node The node or node index
10342 * @return {Boolean}
10344 isSelected : function(node){
10345 var s = this.selections;
10349 node = this.getNode(node);
10350 return s.indexOf(node) !== -1;
10355 * @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
10356 * @param {Boolean} keepExisting (optional) true to keep existing selections
10357 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10359 select : function(nodeInfo, keepExisting, suppressEvent){
10360 if(nodeInfo instanceof Array){
10362 this.clearSelections(true);
10364 for(var i = 0, len = nodeInfo.length; i < len; i++){
10365 this.select(nodeInfo[i], true, true);
10369 var node = this.getNode(nodeInfo);
10370 if(!node || this.isSelected(node)){
10371 return; // already selected.
10374 this.clearSelections(true);
10376 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
10377 Roo.fly(node).addClass(this.selectedClass);
10378 this.selections.push(node);
10379 if(!suppressEvent){
10380 this.fireEvent("selectionchange", this, this.selections);
10388 * @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
10389 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
10390 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
10392 unselect : function(nodeInfo, keepExisting, suppressEvent)
10394 if(nodeInfo instanceof Array){
10395 Roo.each(this.selections, function(s) {
10396 this.unselect(s, nodeInfo);
10400 var node = this.getNode(nodeInfo);
10401 if(!node || !this.isSelected(node)){
10402 Roo.log("not selected");
10403 return; // not selected.
10407 Roo.each(this.selections, function(s) {
10409 Roo.fly(node).removeClass(this.selectedClass);
10416 this.selections= ns;
10417 this.fireEvent("selectionchange", this, this.selections);
10421 * Gets a template node.
10422 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10423 * @return {HTMLElement} The node or null if it wasn't found
10425 getNode : function(nodeInfo){
10426 if(typeof nodeInfo == "string"){
10427 return document.getElementById(nodeInfo);
10428 }else if(typeof nodeInfo == "number"){
10429 return this.nodes[nodeInfo];
10435 * Gets a range template nodes.
10436 * @param {Number} startIndex
10437 * @param {Number} endIndex
10438 * @return {Array} An array of nodes
10440 getNodes : function(start, end){
10441 var ns = this.nodes;
10442 start = start || 0;
10443 end = typeof end == "undefined" ? ns.length - 1 : end;
10446 for(var i = start; i <= end; i++){
10450 for(var i = start; i >= end; i--){
10458 * Finds the index of the passed node
10459 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
10460 * @return {Number} The index of the node or -1
10462 indexOf : function(node){
10463 node = this.getNode(node);
10464 if(typeof node.nodeIndex == "number"){
10465 return node.nodeIndex;
10467 var ns = this.nodes;
10468 for(var i = 0, len = ns.length; i < len; i++){
10479 * based on jquery fullcalendar
10483 Roo.bootstrap = Roo.bootstrap || {};
10485 * @class Roo.bootstrap.Calendar
10486 * @extends Roo.bootstrap.Component
10487 * Bootstrap Calendar class
10488 * @cfg {Boolean} loadMask (true|false) default false
10489 * @cfg {Object} header generate the user specific header of the calendar, default false
10492 * Create a new Container
10493 * @param {Object} config The config object
10498 Roo.bootstrap.Calendar = function(config){
10499 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
10503 * Fires when a date is selected
10504 * @param {DatePicker} this
10505 * @param {Date} date The selected date
10509 * @event monthchange
10510 * Fires when the displayed month changes
10511 * @param {DatePicker} this
10512 * @param {Date} date The selected month
10514 'monthchange': true,
10516 * @event evententer
10517 * Fires when mouse over an event
10518 * @param {Calendar} this
10519 * @param {event} Event
10521 'evententer': true,
10523 * @event eventleave
10524 * Fires when the mouse leaves an
10525 * @param {Calendar} this
10528 'eventleave': true,
10530 * @event eventclick
10531 * Fires when the mouse click an
10532 * @param {Calendar} this
10541 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
10544 * @cfg {Number} startDay
10545 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
10553 getAutoCreate : function(){
10556 var fc_button = function(name, corner, style, content ) {
10557 return Roo.apply({},{
10559 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
10561 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
10564 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
10575 style : 'width:100%',
10582 cls : 'fc-header-left',
10584 fc_button('prev', 'left', 'arrow', '‹' ),
10585 fc_button('next', 'right', 'arrow', '›' ),
10586 { tag: 'span', cls: 'fc-header-space' },
10587 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
10595 cls : 'fc-header-center',
10599 cls: 'fc-header-title',
10602 html : 'month / year'
10610 cls : 'fc-header-right',
10612 /* fc_button('month', 'left', '', 'month' ),
10613 fc_button('week', '', '', 'week' ),
10614 fc_button('day', 'right', '', 'day' )
10626 header = this.header;
10629 var cal_heads = function() {
10631 // fixme - handle this.
10633 for (var i =0; i < Date.dayNames.length; i++) {
10634 var d = Date.dayNames[i];
10637 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
10638 html : d.substring(0,3)
10642 ret[0].cls += ' fc-first';
10643 ret[6].cls += ' fc-last';
10646 var cal_cell = function(n) {
10649 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10654 cls: 'fc-day-number',
10658 cls: 'fc-day-content',
10662 style: 'position: relative;' // height: 17px;
10674 var cal_rows = function() {
10677 for (var r = 0; r < 6; r++) {
10684 for (var i =0; i < Date.dayNames.length; i++) {
10685 var d = Date.dayNames[i];
10686 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10689 row.cn[0].cls+=' fc-first';
10690 row.cn[0].cn[0].style = 'min-height:90px';
10691 row.cn[6].cls+=' fc-last';
10695 ret[0].cls += ' fc-first';
10696 ret[4].cls += ' fc-prev-last';
10697 ret[5].cls += ' fc-last';
10704 cls: 'fc-border-separate',
10705 style : 'width:100%',
10713 cls : 'fc-first fc-last',
10731 cls : 'fc-content',
10732 style : "position: relative;",
10735 cls : 'fc-view fc-view-month fc-grid',
10736 style : 'position: relative',
10737 unselectable : 'on',
10740 cls : 'fc-event-container',
10741 style : 'position:absolute;z-index:8;top:0;left:0;'
10759 initEvents : function()
10762 throw "can not find store for calendar";
10768 style: "text-align:center",
10772 style: "background-color:white;width:50%;margin:250 auto",
10776 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10787 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10789 var size = this.el.select('.fc-content', true).first().getSize();
10790 this.maskEl.setSize(size.width, size.height);
10791 this.maskEl.enableDisplayMode("block");
10792 if(!this.loadMask){
10793 this.maskEl.hide();
10796 this.store = Roo.factory(this.store, Roo.data);
10797 this.store.on('load', this.onLoad, this);
10798 this.store.on('beforeload', this.onBeforeLoad, this);
10802 this.cells = this.el.select('.fc-day',true);
10803 //Roo.log(this.cells);
10804 this.textNodes = this.el.query('.fc-day-number');
10805 this.cells.addClassOnOver('fc-state-hover');
10807 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10808 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10809 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10810 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10812 this.on('monthchange', this.onMonthChange, this);
10814 this.update(new Date().clearTime());
10817 resize : function() {
10818 var sz = this.el.getSize();
10820 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10821 this.el.select('.fc-day-content div',true).setHeight(34);
10826 showPrevMonth : function(e){
10827 this.update(this.activeDate.add("mo", -1));
10829 showToday : function(e){
10830 this.update(new Date().clearTime());
10833 showNextMonth : function(e){
10834 this.update(this.activeDate.add("mo", 1));
10838 showPrevYear : function(){
10839 this.update(this.activeDate.add("y", -1));
10843 showNextYear : function(){
10844 this.update(this.activeDate.add("y", 1));
10849 update : function(date)
10851 var vd = this.activeDate;
10852 this.activeDate = date;
10853 // if(vd && this.el){
10854 // var t = date.getTime();
10855 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10856 // Roo.log('using add remove');
10858 // this.fireEvent('monthchange', this, date);
10860 // this.cells.removeClass("fc-state-highlight");
10861 // this.cells.each(function(c){
10862 // if(c.dateValue == t){
10863 // c.addClass("fc-state-highlight");
10864 // setTimeout(function(){
10865 // try{c.dom.firstChild.focus();}catch(e){}
10875 var days = date.getDaysInMonth();
10877 var firstOfMonth = date.getFirstDateOfMonth();
10878 var startingPos = firstOfMonth.getDay()-this.startDay;
10880 if(startingPos < this.startDay){
10884 var pm = date.add(Date.MONTH, -1);
10885 var prevStart = pm.getDaysInMonth()-startingPos;
10887 this.cells = this.el.select('.fc-day',true);
10888 this.textNodes = this.el.query('.fc-day-number');
10889 this.cells.addClassOnOver('fc-state-hover');
10891 var cells = this.cells.elements;
10892 var textEls = this.textNodes;
10894 Roo.each(cells, function(cell){
10895 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10898 days += startingPos;
10900 // convert everything to numbers so it's fast
10901 var day = 86400000;
10902 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10905 //Roo.log(prevStart);
10907 var today = new Date().clearTime().getTime();
10908 var sel = date.clearTime().getTime();
10909 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10910 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10911 var ddMatch = this.disabledDatesRE;
10912 var ddText = this.disabledDatesText;
10913 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10914 var ddaysText = this.disabledDaysText;
10915 var format = this.format;
10917 var setCellClass = function(cal, cell){
10919 //Roo.log('set Cell Class');
10921 var t = d.getTime();
10925 cell.dateValue = t;
10927 cell.className += " fc-today";
10928 cell.className += " fc-state-highlight";
10929 cell.title = cal.todayText;
10932 // disable highlight in other month..
10933 //cell.className += " fc-state-highlight";
10938 cell.className = " fc-state-disabled";
10939 cell.title = cal.minText;
10943 cell.className = " fc-state-disabled";
10944 cell.title = cal.maxText;
10948 if(ddays.indexOf(d.getDay()) != -1){
10949 cell.title = ddaysText;
10950 cell.className = " fc-state-disabled";
10953 if(ddMatch && format){
10954 var fvalue = d.dateFormat(format);
10955 if(ddMatch.test(fvalue)){
10956 cell.title = ddText.replace("%0", fvalue);
10957 cell.className = " fc-state-disabled";
10961 if (!cell.initialClassName) {
10962 cell.initialClassName = cell.dom.className;
10965 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10970 for(; i < startingPos; i++) {
10971 textEls[i].innerHTML = (++prevStart);
10972 d.setDate(d.getDate()+1);
10974 cells[i].className = "fc-past fc-other-month";
10975 setCellClass(this, cells[i]);
10980 for(; i < days; i++){
10981 intDay = i - startingPos + 1;
10982 textEls[i].innerHTML = (intDay);
10983 d.setDate(d.getDate()+1);
10985 cells[i].className = ''; // "x-date-active";
10986 setCellClass(this, cells[i]);
10990 for(; i < 42; i++) {
10991 textEls[i].innerHTML = (++extraDays);
10992 d.setDate(d.getDate()+1);
10994 cells[i].className = "fc-future fc-other-month";
10995 setCellClass(this, cells[i]);
10998 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
11000 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
11002 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
11003 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
11005 if(totalRows != 6){
11006 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
11007 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
11010 this.fireEvent('monthchange', this, date);
11014 if(!this.internalRender){
11015 var main = this.el.dom.firstChild;
11016 var w = main.offsetWidth;
11017 this.el.setWidth(w + this.el.getBorderWidth("lr"));
11018 Roo.fly(main).setWidth(w);
11019 this.internalRender = true;
11020 // opera does not respect the auto grow header center column
11021 // then, after it gets a width opera refuses to recalculate
11022 // without a second pass
11023 if(Roo.isOpera && !this.secondPass){
11024 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
11025 this.secondPass = true;
11026 this.update.defer(10, this, [date]);
11033 findCell : function(dt) {
11034 dt = dt.clearTime().getTime();
11036 this.cells.each(function(c){
11037 //Roo.log("check " +c.dateValue + '?=' + dt);
11038 if(c.dateValue == dt){
11048 findCells : function(ev) {
11049 var s = ev.start.clone().clearTime().getTime();
11051 var e= ev.end.clone().clearTime().getTime();
11054 this.cells.each(function(c){
11055 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
11057 if(c.dateValue > e){
11060 if(c.dateValue < s){
11069 findBestRow: function(cells)
11073 for (var i =0 ; i < cells.length;i++) {
11074 ret = Math.max(cells[i].rows || 0,ret);
11081 addItem : function(ev)
11083 // look for vertical location slot in
11084 var cells = this.findCells(ev);
11086 ev.row = this.findBestRow(cells);
11088 // work out the location.
11092 for(var i =0; i < cells.length; i++) {
11100 if (crow.start.getY() == cells[i].getY()) {
11102 crow.end = cells[i];
11118 for (var i = 0; i < cells.length;i++) {
11119 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
11123 this.calevents.push(ev);
11126 clearEvents: function() {
11128 if(!this.calevents){
11132 Roo.each(this.cells.elements, function(c){
11136 Roo.each(this.calevents, function(e) {
11137 Roo.each(e.els, function(el) {
11138 el.un('mouseenter' ,this.onEventEnter, this);
11139 el.un('mouseleave' ,this.onEventLeave, this);
11146 renderEvents: function()
11148 // first make sure there is enough space..
11150 this.cells.each(function(c) {
11152 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
11155 for (var e = 0; e < this.calevents.length; e++) {
11156 var ev = this.calevents[e];
11157 var cells = ev.cells;
11158 var rows = ev.rows;
11160 for(var i =0; i < rows.length; i++) {
11163 // how many rows should it span..
11166 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
11167 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
11169 unselectable : "on",
11172 cls: 'fc-event-inner',
11176 // cls: 'fc-event-time',
11177 // html : cells.length > 1 ? '' : ev.time
11181 cls: 'fc-event-title',
11182 html : String.format('{0}', ev.title)
11189 cls: 'ui-resizable-handle ui-resizable-e',
11190 html : '  '
11196 cfg.cls += ' fc-event-start';
11198 if ((i+1) == rows.length) {
11199 cfg.cls += ' fc-event-end';
11202 var ctr = this.el.select('.fc-event-container',true).first();
11203 var cg = ctr.createChild(cfg);
11205 cg.on('mouseenter' ,this.onEventEnter, this, ev);
11206 cg.on('mouseleave' ,this.onEventLeave, this, ev);
11207 cg.on('click', this.onEventClick, this, ev);
11211 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
11212 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
11214 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
11215 cg.setWidth(ebox.right - sbox.x -2);
11223 onEventEnter: function (e, el,event,d) {
11224 this.fireEvent('evententer', this, el, event);
11227 onEventLeave: function (e, el,event,d) {
11228 this.fireEvent('eventleave', this, el, event);
11231 onEventClick: function (e, el,event,d) {
11232 this.fireEvent('eventclick', this, el, event);
11235 onMonthChange: function () {
11239 onLoad: function ()
11241 this.calevents = [];
11244 if(this.store.getCount() > 0){
11245 this.store.data.each(function(d){
11248 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
11249 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
11250 time : d.data.start_time,
11251 title : d.data.title,
11252 description : d.data.description,
11253 venue : d.data.venue
11258 this.renderEvents();
11261 this.maskEl.hide();
11265 onBeforeLoad: function()
11267 this.clearEvents();
11270 this.maskEl.show();
11284 * @class Roo.bootstrap.Popover
11285 * @extends Roo.bootstrap.Component
11286 * Bootstrap Popover class
11287 * @cfg {String} html contents of the popover (or false to use children..)
11288 * @cfg {String} title of popover (or false to hide)
11289 * @cfg {String} placement how it is placed
11290 * @cfg {String} trigger click || hover (or false to trigger manually)
11291 * @cfg {String} over what (parent or false to trigger manually.)
11294 * Create a new Popover
11295 * @param {Object} config The config object
11298 Roo.bootstrap.Popover = function(config){
11299 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
11302 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
11304 title: 'Fill in a title',
11307 placement : 'right',
11308 trigger : 'hover', // hover
11312 can_build_overlaid : false,
11314 getChildContainer : function()
11316 return this.el.select('.popover-content',true).first();
11319 getAutoCreate : function(){
11320 Roo.log('make popover?');
11322 cls : 'popover roo-dynamic',
11323 style: 'display:block',
11329 cls : 'popover-inner',
11333 cls: 'popover-title',
11337 cls : 'popover-content',
11348 setTitle: function(str)
11350 this.el.select('.popover-title',true).first().dom.innerHTML = str;
11352 setContent: function(str)
11354 this.el.select('.popover-content',true).first().dom.innerHTML = str;
11356 // as it get's added to the bottom of the page.
11357 onRender : function(ct, position)
11359 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
11361 var cfg = Roo.apply({}, this.getAutoCreate());
11365 cfg.cls += ' ' + this.cls;
11368 cfg.style = this.style;
11370 Roo.log("adding to ")
11371 this.el = Roo.get(document.body).createChild(cfg, position);
11377 initEvents : function()
11379 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
11380 this.el.enableDisplayMode('block');
11382 if (this.over === false) {
11385 if (this.triggers === false) {
11388 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11389 var triggers = this.trigger ? this.trigger.split(' ') : [];
11390 Roo.each(triggers, function(trigger) {
11392 if (trigger == 'click') {
11393 on_el.on('click', this.toggle, this);
11394 } else if (trigger != 'manual') {
11395 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
11396 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
11398 on_el.on(eventIn ,this.enter, this);
11399 on_el.on(eventOut, this.leave, this);
11410 toggle : function () {
11411 this.hoverState == 'in' ? this.leave() : this.enter();
11414 enter : function () {
11417 clearTimeout(this.timeout);
11419 this.hoverState = 'in'
11421 if (!this.delay || !this.delay.show) {
11426 this.timeout = setTimeout(function () {
11427 if (_t.hoverState == 'in') {
11430 }, this.delay.show)
11432 leave : function() {
11433 clearTimeout(this.timeout);
11435 this.hoverState = 'out'
11437 if (!this.delay || !this.delay.hide) {
11442 this.timeout = setTimeout(function () {
11443 if (_t.hoverState == 'out') {
11446 }, this.delay.hide)
11449 show : function (on_el)
11452 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
11455 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
11456 if (this.html !== false) {
11457 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
11459 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
11460 if (!this.title.length) {
11461 this.el.select('.popover-title',true).hide();
11464 var placement = typeof this.placement == 'function' ?
11465 this.placement.call(this, this.el, on_el) :
11468 var autoToken = /\s?auto?\s?/i;
11469 var autoPlace = autoToken.test(placement);
11471 placement = placement.replace(autoToken, '') || 'top';
11475 //this.el.setXY([0,0]);
11477 this.el.dom.style.display='block';
11478 this.el.addClass(placement);
11480 //this.el.appendTo(on_el);
11482 var p = this.getPosition();
11483 var box = this.el.getBox();
11488 var align = Roo.bootstrap.Popover.alignment[placement]
11489 this.el.alignTo(on_el, align[0],align[1]);
11490 //var arrow = this.el.select('.arrow',true).first();
11491 //arrow.set(align[2],
11493 this.el.addClass('in');
11494 this.hoverState = null;
11496 if (this.el.hasClass('fade')) {
11503 this.el.setXY([0,0]);
11504 this.el.removeClass('in');
11511 Roo.bootstrap.Popover.alignment = {
11512 'left' : ['r-l', [-10,0], 'right'],
11513 'right' : ['l-r', [10,0], 'left'],
11514 'bottom' : ['t-b', [0,10], 'top'],
11515 'top' : [ 'b-t', [0,-10], 'bottom']
11526 * @class Roo.bootstrap.Progress
11527 * @extends Roo.bootstrap.Component
11528 * Bootstrap Progress class
11529 * @cfg {Boolean} striped striped of the progress bar
11530 * @cfg {Boolean} active animated of the progress bar
11534 * Create a new Progress
11535 * @param {Object} config The config object
11538 Roo.bootstrap.Progress = function(config){
11539 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
11542 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
11547 getAutoCreate : function(){
11555 cfg.cls += ' progress-striped';
11559 cfg.cls += ' active';
11578 * @class Roo.bootstrap.ProgressBar
11579 * @extends Roo.bootstrap.Component
11580 * Bootstrap ProgressBar class
11581 * @cfg {Number} aria_valuenow aria-value now
11582 * @cfg {Number} aria_valuemin aria-value min
11583 * @cfg {Number} aria_valuemax aria-value max
11584 * @cfg {String} label label for the progress bar
11585 * @cfg {String} panel (success | info | warning | danger )
11586 * @cfg {String} role role of the progress bar
11587 * @cfg {String} sr_only text
11591 * Create a new ProgressBar
11592 * @param {Object} config The config object
11595 Roo.bootstrap.ProgressBar = function(config){
11596 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
11599 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
11603 aria_valuemax : 100,
11609 getAutoCreate : function()
11614 cls: 'progress-bar',
11615 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
11627 cfg.role = this.role;
11630 if(this.aria_valuenow){
11631 cfg['aria-valuenow'] = this.aria_valuenow;
11634 if(this.aria_valuemin){
11635 cfg['aria-valuemin'] = this.aria_valuemin;
11638 if(this.aria_valuemax){
11639 cfg['aria-valuemax'] = this.aria_valuemax;
11642 if(this.label && !this.sr_only){
11643 cfg.html = this.label;
11647 cfg.cls += ' progress-bar-' + this.panel;
11653 update : function(aria_valuenow)
11655 this.aria_valuenow = aria_valuenow;
11657 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11672 * @class Roo.bootstrap.TabPanel
11673 * @extends Roo.bootstrap.Component
11674 * Bootstrap TabPanel class
11675 * @cfg {Boolean} active panel active
11676 * @cfg {String} html panel content
11677 * @cfg {String} tabId tab relate id
11678 * @cfg {String} navId The navbar which triggers show hide
11682 * Create a new TabPanel
11683 * @param {Object} config The config object
11686 Roo.bootstrap.TabPanel = function(config){
11687 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11691 * Fires when the active status changes
11692 * @param {Roo.bootstrap.TabPanel} this
11693 * @param {Boolean} state the new state
11700 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
11707 getAutoCreate : function(){
11711 html: this.html || ''
11715 cfg.cls += ' active';
11719 cfg.tabId = this.tabId;
11724 onRender : function(ct, position)
11726 // Roo.log("Call onRender: " + this.xtype);
11728 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11730 if (this.navId && this.tabId) {
11731 var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11733 Roo.log("could not find navID:" + this.navId + ", tabId: " + this.tabId);
11735 item.on('changed', function(item, state) {
11736 this.setActive(state);
11742 setActive: function(state)
11744 Roo.log("panel - set active " + this.tabId + "=" + state);
11746 this.active = state;
11748 this.el.removeClass('active');
11750 } else if (!this.el.hasClass('active')) {
11751 this.el.addClass('active');
11753 this.fireEvent('changed', this, state);
11770 * @class Roo.bootstrap.DateField
11771 * @extends Roo.bootstrap.Input
11772 * Bootstrap DateField class
11773 * @cfg {Number} weekStart default 0
11774 * @cfg {Number} weekStart default 0
11775 * @cfg {Number} viewMode default empty, (months|years)
11776 * @cfg {Number} minViewMode default empty, (months|years)
11777 * @cfg {Number} startDate default -Infinity
11778 * @cfg {Number} endDate default Infinity
11779 * @cfg {Boolean} todayHighlight default false
11780 * @cfg {Boolean} todayBtn default false
11781 * @cfg {Boolean} calendarWeeks default false
11782 * @cfg {Object} daysOfWeekDisabled default empty
11784 * @cfg {Boolean} keyboardNavigation default true
11785 * @cfg {String} language default en
11788 * Create a new DateField
11789 * @param {Object} config The config object
11792 Roo.bootstrap.DateField = function(config){
11793 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11797 * Fires when this field show.
11798 * @param {Roo.bootstrap.DateField} this
11799 * @param {Mixed} date The date value
11804 * Fires when this field hide.
11805 * @param {Roo.bootstrap.DateField} this
11806 * @param {Mixed} date The date value
11811 * Fires when select a date.
11812 * @param {Roo.bootstrap.DateField} this
11813 * @param {Mixed} date The date value
11819 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
11822 * @cfg {String} format
11823 * The default date format string which can be overriden for localization support. The format must be
11824 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11828 * @cfg {String} altFormats
11829 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11830 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11832 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11840 todayHighlight : false,
11846 keyboardNavigation: true,
11848 calendarWeeks: false,
11850 startDate: -Infinity,
11854 daysOfWeekDisabled: [],
11858 UTCDate: function()
11860 return new Date(Date.UTC.apply(Date, arguments));
11863 UTCToday: function()
11865 var today = new Date();
11866 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11869 getDate: function() {
11870 var d = this.getUTCDate();
11871 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11874 getUTCDate: function() {
11878 setDate: function(d) {
11879 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11882 setUTCDate: function(d) {
11884 this.setValue(this.formatDate(this.date));
11887 onRender: function(ct, position)
11890 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11892 this.language = this.language || 'en';
11893 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11894 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11896 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11897 this.format = this.format || 'm/d/y';
11898 this.isInline = false;
11899 this.isInput = true;
11900 this.component = this.el.select('.add-on', true).first() || false;
11901 this.component = (this.component && this.component.length === 0) ? false : this.component;
11902 this.hasInput = this.component && this.inputEL().length;
11904 if (typeof(this.minViewMode === 'string')) {
11905 switch (this.minViewMode) {
11907 this.minViewMode = 1;
11910 this.minViewMode = 2;
11913 this.minViewMode = 0;
11918 if (typeof(this.viewMode === 'string')) {
11919 switch (this.viewMode) {
11932 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11934 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11936 this.picker().on('mousedown', this.onMousedown, this);
11937 this.picker().on('click', this.onClick, this);
11939 this.picker().addClass('datepicker-dropdown');
11941 this.startViewMode = this.viewMode;
11944 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11945 if(!this.calendarWeeks){
11950 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11951 v.attr('colspan', function(i, val){
11952 return parseInt(val) + 1;
11957 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11959 this.setStartDate(this.startDate);
11960 this.setEndDate(this.endDate);
11962 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11969 if(this.isInline) {
11974 picker : function()
11976 return this.el.select('.datepicker', true).first();
11979 fillDow: function()
11981 var dowCnt = this.weekStart;
11990 if(this.calendarWeeks){
11998 while (dowCnt < this.weekStart + 7) {
12002 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
12006 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
12009 fillMonths: function()
12012 var months = this.picker().select('>.datepicker-months td', true).first();
12014 months.dom.innerHTML = '';
12020 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
12023 months.createChild(month);
12028 update: function(){
12030 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
12032 if (this.date < this.startDate) {
12033 this.viewDate = new Date(this.startDate);
12034 } else if (this.date > this.endDate) {
12035 this.viewDate = new Date(this.endDate);
12037 this.viewDate = new Date(this.date);
12044 var d = new Date(this.viewDate),
12045 year = d.getUTCFullYear(),
12046 month = d.getUTCMonth(),
12047 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
12048 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
12049 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
12050 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
12051 currentDate = this.date && this.date.valueOf(),
12052 today = this.UTCToday();
12054 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
12056 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
12058 // this.picker.select('>tfoot th.today').
12059 // .text(dates[this.language].today)
12060 // .toggle(this.todayBtn !== false);
12062 this.updateNavArrows();
12065 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
12067 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
12069 prevMonth.setUTCDate(day);
12071 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
12073 var nextMonth = new Date(prevMonth);
12075 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
12077 nextMonth = nextMonth.valueOf();
12079 var fillMonths = false;
12081 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
12083 while(prevMonth.valueOf() < nextMonth) {
12086 if (prevMonth.getUTCDay() === this.weekStart) {
12088 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
12096 if(this.calendarWeeks){
12097 // ISO 8601: First week contains first thursday.
12098 // ISO also states week starts on Monday, but we can be more abstract here.
12100 // Start of current week: based on weekstart/current date
12101 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
12102 // Thursday of this week
12103 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
12104 // First Thursday of year, year from thursday
12105 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
12106 // Calendar week: ms between thursdays, div ms per day, div 7 days
12107 calWeek = (th - yth) / 864e5 / 7 + 1;
12109 fillMonths.cn.push({
12117 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
12119 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
12122 if (this.todayHighlight &&
12123 prevMonth.getUTCFullYear() == today.getFullYear() &&
12124 prevMonth.getUTCMonth() == today.getMonth() &&
12125 prevMonth.getUTCDate() == today.getDate()) {
12126 clsName += ' today';
12129 if (currentDate && prevMonth.valueOf() === currentDate) {
12130 clsName += ' active';
12133 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
12134 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
12135 clsName += ' disabled';
12138 fillMonths.cn.push({
12140 cls: 'day ' + clsName,
12141 html: prevMonth.getDate()
12144 prevMonth.setDate(prevMonth.getDate()+1);
12147 var currentYear = this.date && this.date.getUTCFullYear();
12148 var currentMonth = this.date && this.date.getUTCMonth();
12150 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
12152 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
12153 v.removeClass('active');
12155 if(currentYear === year && k === currentMonth){
12156 v.addClass('active');
12159 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
12160 v.addClass('disabled');
12166 year = parseInt(year/10, 10) * 10;
12168 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
12170 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
12173 for (var i = -1; i < 11; i++) {
12174 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
12176 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
12184 showMode: function(dir) {
12186 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
12188 Roo.each(this.picker().select('>div',true).elements, function(v){
12189 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12192 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
12197 if(this.isInline) return;
12199 this.picker().removeClass(['bottom', 'top']);
12201 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12203 * place to the top of element!
12207 this.picker().addClass('top');
12208 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12213 this.picker().addClass('bottom');
12215 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12218 parseDate : function(value){
12219 if(!value || value instanceof Date){
12222 var v = Date.parseDate(value, this.format);
12223 if (!v && this.useIso) {
12224 v = Date.parseDate(value, 'Y-m-d');
12226 if(!v && this.altFormats){
12227 if(!this.altFormatsArray){
12228 this.altFormatsArray = this.altFormats.split("|");
12230 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
12231 v = Date.parseDate(value, this.altFormatsArray[i]);
12237 formatDate : function(date, fmt){
12238 return (!date || !(date instanceof Date)) ?
12239 date : date.dateFormat(fmt || this.format);
12242 onFocus : function()
12244 Roo.bootstrap.DateField.superclass.onFocus.call(this);
12248 onBlur : function()
12250 Roo.bootstrap.DateField.superclass.onBlur.call(this);
12256 this.picker().show();
12260 this.fireEvent('show', this, this.date);
12265 if(this.isInline) return;
12266 this.picker().hide();
12267 this.viewMode = this.startViewMode;
12270 this.fireEvent('hide', this, this.date);
12274 onMousedown: function(e){
12275 e.stopPropagation();
12276 e.preventDefault();
12279 keyup: function(e){
12280 Roo.bootstrap.DateField.superclass.keyup.call(this);
12285 setValue: function(v){
12286 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
12288 this.fireEvent('select', this, this.date);
12292 fireKey: function(e){
12293 if (!this.picker().isVisible()){
12294 if (e.keyCode == 27) // allow escape to hide and re-show picker
12298 var dateChanged = false,
12300 newDate, newViewDate;
12304 e.preventDefault();
12308 if (!this.keyboardNavigation) break;
12309 dir = e.keyCode == 37 ? -1 : 1;
12312 newDate = this.moveYear(this.date, dir);
12313 newViewDate = this.moveYear(this.viewDate, dir);
12314 } else if (e.shiftKey){
12315 newDate = this.moveMonth(this.date, dir);
12316 newViewDate = this.moveMonth(this.viewDate, dir);
12318 newDate = new Date(this.date);
12319 newDate.setUTCDate(this.date.getUTCDate() + dir);
12320 newViewDate = new Date(this.viewDate);
12321 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
12323 if (this.dateWithinRange(newDate)){
12324 this.date = newDate;
12325 this.viewDate = newViewDate;
12326 this.setValue(this.formatDate(this.date));
12328 e.preventDefault();
12329 dateChanged = true;
12334 if (!this.keyboardNavigation) break;
12335 dir = e.keyCode == 38 ? -1 : 1;
12337 newDate = this.moveYear(this.date, dir);
12338 newViewDate = this.moveYear(this.viewDate, dir);
12339 } else if (e.shiftKey){
12340 newDate = this.moveMonth(this.date, dir);
12341 newViewDate = this.moveMonth(this.viewDate, dir);
12343 newDate = new Date(this.date);
12344 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
12345 newViewDate = new Date(this.viewDate);
12346 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
12348 if (this.dateWithinRange(newDate)){
12349 this.date = newDate;
12350 this.viewDate = newViewDate;
12351 this.setValue(this.formatDate(this.date));
12353 e.preventDefault();
12354 dateChanged = true;
12358 this.setValue(this.formatDate(this.date));
12360 e.preventDefault();
12363 this.setValue(this.formatDate(this.date));
12370 onClick: function(e) {
12371 e.stopPropagation();
12372 e.preventDefault();
12374 var target = e.getTarget();
12376 if(target.nodeName.toLowerCase() === 'i'){
12377 target = Roo.get(target).dom.parentNode;
12380 var nodeName = target.nodeName;
12381 var className = target.className;
12382 var html = target.innerHTML;
12384 switch(nodeName.toLowerCase()) {
12386 switch(className) {
12392 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
12393 switch(this.viewMode){
12395 this.viewDate = this.moveMonth(this.viewDate, dir);
12399 this.viewDate = this.moveYear(this.viewDate, dir);
12405 var date = new Date();
12406 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
12408 this.setValue(this.formatDate(this.date));
12414 if (className.indexOf('disabled') === -1) {
12415 this.viewDate.setUTCDate(1);
12416 if (className.indexOf('month') !== -1) {
12417 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
12419 var year = parseInt(html, 10) || 0;
12420 this.viewDate.setUTCFullYear(year);
12429 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
12430 var day = parseInt(html, 10) || 1;
12431 var year = this.viewDate.getUTCFullYear(),
12432 month = this.viewDate.getUTCMonth();
12434 if (className.indexOf('old') !== -1) {
12441 } else if (className.indexOf('new') !== -1) {
12449 this.date = this.UTCDate(year, month, day,0,0,0,0);
12450 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
12452 this.setValue(this.formatDate(this.date));
12459 setStartDate: function(startDate){
12460 this.startDate = startDate || -Infinity;
12461 if (this.startDate !== -Infinity) {
12462 this.startDate = this.parseDate(this.startDate);
12465 this.updateNavArrows();
12468 setEndDate: function(endDate){
12469 this.endDate = endDate || Infinity;
12470 if (this.endDate !== Infinity) {
12471 this.endDate = this.parseDate(this.endDate);
12474 this.updateNavArrows();
12477 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
12478 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
12479 if (typeof(this.daysOfWeekDisabled) !== 'object') {
12480 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
12482 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
12483 return parseInt(d, 10);
12486 this.updateNavArrows();
12489 updateNavArrows: function() {
12490 var d = new Date(this.viewDate),
12491 year = d.getUTCFullYear(),
12492 month = d.getUTCMonth();
12494 Roo.each(this.picker().select('.prev', true).elements, function(v){
12496 switch (this.viewMode) {
12499 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
12505 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
12512 Roo.each(this.picker().select('.next', true).elements, function(v){
12514 switch (this.viewMode) {
12517 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
12523 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
12531 moveMonth: function(date, dir){
12532 if (!dir) return date;
12533 var new_date = new Date(date.valueOf()),
12534 day = new_date.getUTCDate(),
12535 month = new_date.getUTCMonth(),
12536 mag = Math.abs(dir),
12538 dir = dir > 0 ? 1 : -1;
12541 // If going back one month, make sure month is not current month
12542 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
12544 return new_date.getUTCMonth() == month;
12546 // If going forward one month, make sure month is as expected
12547 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
12549 return new_date.getUTCMonth() != new_month;
12551 new_month = month + dir;
12552 new_date.setUTCMonth(new_month);
12553 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
12554 if (new_month < 0 || new_month > 11)
12555 new_month = (new_month + 12) % 12;
12557 // For magnitudes >1, move one month at a time...
12558 for (var i=0; i<mag; i++)
12559 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
12560 new_date = this.moveMonth(new_date, dir);
12561 // ...then reset the day, keeping it in the new month
12562 new_month = new_date.getUTCMonth();
12563 new_date.setUTCDate(day);
12565 return new_month != new_date.getUTCMonth();
12568 // Common date-resetting loop -- if date is beyond end of month, make it
12571 new_date.setUTCDate(--day);
12572 new_date.setUTCMonth(new_month);
12577 moveYear: function(date, dir){
12578 return this.moveMonth(date, dir*12);
12581 dateWithinRange: function(date){
12582 return date >= this.startDate && date <= this.endDate;
12586 remove: function() {
12587 this.picker().remove();
12592 Roo.apply(Roo.bootstrap.DateField, {
12603 html: '<i class="icon-arrow-left"/>'
12613 html: '<i class="icon-arrow-right"/>'
12655 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12656 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12657 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12658 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12659 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12672 navFnc: 'FullYear',
12677 navFnc: 'FullYear',
12682 Roo.apply(Roo.bootstrap.DateField, {
12686 cls: 'datepicker dropdown-menu',
12690 cls: 'datepicker-days',
12694 cls: 'table-condensed',
12696 Roo.bootstrap.DateField.head,
12700 Roo.bootstrap.DateField.footer
12707 cls: 'datepicker-months',
12711 cls: 'table-condensed',
12713 Roo.bootstrap.DateField.head,
12714 Roo.bootstrap.DateField.content,
12715 Roo.bootstrap.DateField.footer
12722 cls: 'datepicker-years',
12726 cls: 'table-condensed',
12728 Roo.bootstrap.DateField.head,
12729 Roo.bootstrap.DateField.content,
12730 Roo.bootstrap.DateField.footer
12749 * @class Roo.bootstrap.TimeField
12750 * @extends Roo.bootstrap.Input
12751 * Bootstrap DateField class
12755 * Create a new TimeField
12756 * @param {Object} config The config object
12759 Roo.bootstrap.TimeField = function(config){
12760 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12764 * Fires when this field show.
12765 * @param {Roo.bootstrap.DateField} this
12766 * @param {Mixed} date The date value
12771 * Fires when this field hide.
12772 * @param {Roo.bootstrap.DateField} this
12773 * @param {Mixed} date The date value
12778 * Fires when select a date.
12779 * @param {Roo.bootstrap.DateField} this
12780 * @param {Mixed} date The date value
12786 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
12789 * @cfg {String} format
12790 * The default time format string which can be overriden for localization support. The format must be
12791 * valid according to {@link Date#parseDate} (defaults to 'H:i').
12795 onRender: function(ct, position)
12798 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12800 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12802 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12804 this.pop = this.picker().select('>.datepicker-time',true).first();
12805 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
12807 this.picker().on('mousedown', this.onMousedown, this);
12808 this.picker().on('click', this.onClick, this);
12810 this.picker().addClass('datepicker-dropdown');
12815 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12816 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12817 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12818 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12819 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12820 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12824 fireKey: function(e){
12825 if (!this.picker().isVisible()){
12826 if (e.keyCode == 27) // allow escape to hide and re-show picker
12831 e.preventDefault();
12839 this.onTogglePeriod();
12842 this.onIncrementMinutes();
12845 this.onDecrementMinutes();
12854 onClick: function(e) {
12855 e.stopPropagation();
12856 e.preventDefault();
12859 picker : function()
12861 return this.el.select('.datepicker', true).first();
12864 fillTime: function()
12866 var time = this.pop.select('tbody', true).first();
12868 time.dom.innerHTML = '';
12883 cls: 'hours-up glyphicon glyphicon-chevron-up'
12903 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12924 cls: 'timepicker-hour',
12939 cls: 'timepicker-minute',
12954 cls: 'btn btn-primary period',
12976 cls: 'hours-down glyphicon glyphicon-chevron-down'
12996 cls: 'minutes-down glyphicon glyphicon-chevron-down'
13014 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
13021 var hours = this.time.getHours();
13022 var minutes = this.time.getMinutes();
13035 hours = hours - 12;
13039 hours = '0' + hours;
13043 minutes = '0' + minutes;
13046 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
13047 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
13048 this.pop.select('button', true).first().dom.innerHTML = period;
13054 this.picker().removeClass(['bottom', 'top']);
13056 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
13058 * place to the top of element!
13062 this.picker().addClass('top');
13063 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13068 this.picker().addClass('bottom');
13070 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
13073 onFocus : function()
13075 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
13079 onBlur : function()
13081 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
13087 this.picker().show();
13092 this.fireEvent('show', this, this.date);
13097 this.picker().hide();
13100 this.fireEvent('hide', this, this.date);
13103 setTime : function()
13106 this.setValue(this.time.format(this.format));
13108 this.fireEvent('select', this, this.date);
13113 onMousedown: function(e){
13114 e.stopPropagation();
13115 e.preventDefault();
13118 onIncrementHours: function()
13120 Roo.log('onIncrementHours');
13121 this.time = this.time.add(Date.HOUR, 1);
13126 onDecrementHours: function()
13128 Roo.log('onDecrementHours');
13129 this.time = this.time.add(Date.HOUR, -1);
13133 onIncrementMinutes: function()
13135 Roo.log('onIncrementMinutes');
13136 this.time = this.time.add(Date.MINUTE, 1);
13140 onDecrementMinutes: function()
13142 Roo.log('onDecrementMinutes');
13143 this.time = this.time.add(Date.MINUTE, -1);
13147 onTogglePeriod: function()
13149 Roo.log('onTogglePeriod');
13150 this.time = this.time.add(Date.HOUR, 12);
13157 Roo.apply(Roo.bootstrap.TimeField, {
13187 cls: 'btn btn-info ok',
13199 Roo.apply(Roo.bootstrap.TimeField, {
13203 cls: 'datepicker dropdown-menu',
13207 cls: 'datepicker-time',
13211 cls: 'table-condensed',
13213 Roo.bootstrap.TimeField.content,
13214 Roo.bootstrap.TimeField.footer
13233 * @class Roo.bootstrap.CheckBox
13234 * @extends Roo.bootstrap.Input
13235 * Bootstrap CheckBox class
13237 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
13238 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
13239 * @cfg {String} boxLabel The text that appears beside the checkbox
13240 * @cfg {Boolean} checked initnal the element
13243 * Create a new CheckBox
13244 * @param {Object} config The config object
13247 Roo.bootstrap.CheckBox = function(config){
13248 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
13253 * Fires when the element is checked or unchecked.
13254 * @param {Roo.bootstrap.CheckBox} this This input
13255 * @param {Boolean} checked The new checked value
13261 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
13263 inputType: 'checkbox',
13269 getAutoCreate : function()
13271 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13277 cfg.cls = 'form-group' //input-group
13282 type : this.inputType,
13283 value : (!this.checked) ? this.valueOff : this.inputValue,
13285 placeholder : this.placeholder || ''
13289 if (this.disabled) {
13290 input.disabled=true;
13294 input.checked = this.checked;
13298 input.name = this.name;
13302 input.cls += ' input-' + this.size;
13306 ['xs','sm','md','lg'].map(function(size){
13307 if (settings[size]) {
13308 cfg.cls += ' col-' + size + '-' + settings[size];
13312 var inputblock = input;
13314 if (this.before || this.after) {
13317 cls : 'input-group',
13321 inputblock.cn.push({
13323 cls : 'input-group-addon',
13327 inputblock.cn.push(input);
13329 inputblock.cn.push({
13331 cls : 'input-group-addon',
13338 if (align ==='left' && this.fieldLabel.length) {
13339 Roo.log("left and has label");
13345 cls : 'control-label col-md-' + this.labelWidth,
13346 html : this.fieldLabel
13350 cls : "col-md-" + (12 - this.labelWidth),
13357 } else if ( this.fieldLabel.length) {
13362 tag: this.boxLabel ? 'span' : 'label',
13364 cls: 'control-label box-input-label',
13365 //cls : 'input-group-addon',
13366 html : this.fieldLabel
13376 Roo.log(" no label && no align");
13391 html: this.boxLabel
13400 * return the real input element.
13402 inputEl: function ()
13404 return this.el.select('input.form-box',true).first();
13409 return this.el.select('label.control-label',true).first();
13412 initEvents : function()
13414 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
13416 this.inputEl().on('click', this.onClick, this);
13420 onClick : function()
13422 this.setChecked(!this.checked);
13425 setChecked : function(state,suppressEvent)
13427 this.checked = state;
13429 this.inputEl().dom.checked = state;
13431 if(suppressEvent !== true){
13432 this.fireEvent('check', this, state);
13435 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13439 setValue : function(v,suppressEvent)
13441 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
13455 * @class Roo.bootstrap.Radio
13456 * @extends Roo.bootstrap.CheckBox
13457 * Bootstrap Radio class
13460 * Create a new Radio
13461 * @param {Object} config The config object
13464 Roo.bootstrap.Radio = function(config){
13465 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
13469 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
13471 inputType: 'radio',
13475 getAutoCreate : function()
13477 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
13483 cfg.cls = 'form-group' //input-group
13488 type : this.inputType,
13489 value : (!this.checked) ? this.valueOff : this.inputValue,
13491 placeholder : this.placeholder || ''
13495 if (this.disabled) {
13496 input.disabled=true;
13500 input.checked = this.checked;
13504 input.name = this.name;
13508 input.cls += ' input-' + this.size;
13512 ['xs','sm','md','lg'].map(function(size){
13513 if (settings[size]) {
13514 cfg.cls += ' col-' + size + '-' + settings[size];
13518 var inputblock = input;
13520 if (this.before || this.after) {
13523 cls : 'input-group',
13527 inputblock.cn.push({
13529 cls : 'input-group-addon',
13533 inputblock.cn.push(input);
13535 inputblock.cn.push({
13537 cls : 'input-group-addon',
13544 if (align ==='left' && this.fieldLabel.length) {
13545 Roo.log("left and has label");
13551 cls : 'control-label col-md-' + this.labelWidth,
13552 html : this.fieldLabel
13556 cls : "col-md-" + (12 - this.labelWidth),
13563 } else if ( this.fieldLabel.length) {
13570 cls: 'control-label box-input-label',
13571 //cls : 'input-group-addon',
13572 html : this.fieldLabel
13582 Roo.log(" no label && no align");
13597 html: this.boxLabel
13605 onClick : function()
13607 this.setChecked(true);
13610 setChecked : function(state,suppressEvent)
13613 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13614 v.dom.checked = false;
13618 this.checked = state;
13619 this.inputEl().dom.checked = state;
13621 if(suppressEvent !== true){
13622 this.fireEvent('check', this, state);
13625 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
13629 getGroupValue : function()
13632 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
13633 if(v.dom.checked == true){
13634 value = v.dom.value;
13642 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
13643 * @return {Mixed} value The field value
13645 getValue : function(){
13646 return this.getGroupValue();
13652 //<script type="text/javascript">
13655 * Based Ext JS Library 1.1.1
13656 * Copyright(c) 2006-2007, Ext JS, LLC.
13662 * @class Roo.HtmlEditorCore
13663 * @extends Roo.Component
13664 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13666 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13669 Roo.HtmlEditorCore = function(config){
13672 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13675 * @event initialize
13676 * Fires when the editor is fully initialized (including the iframe)
13677 * @param {Roo.HtmlEditorCore} this
13682 * Fires when the editor is first receives the focus. Any insertion must wait
13683 * until after this event.
13684 * @param {Roo.HtmlEditorCore} this
13688 * @event beforesync
13689 * Fires before the textarea is updated with content from the editor iframe. Return false
13690 * to cancel the sync.
13691 * @param {Roo.HtmlEditorCore} this
13692 * @param {String} html
13696 * @event beforepush
13697 * Fires before the iframe editor is updated with content from the textarea. Return false
13698 * to cancel the push.
13699 * @param {Roo.HtmlEditorCore} this
13700 * @param {String} html
13705 * Fires when the textarea is updated with content from the editor iframe.
13706 * @param {Roo.HtmlEditorCore} this
13707 * @param {String} html
13712 * Fires when the iframe editor is updated with content from the textarea.
13713 * @param {Roo.HtmlEditorCore} this
13714 * @param {String} html
13719 * @event editorevent
13720 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13721 * @param {Roo.HtmlEditorCore} this
13729 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
13733 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
13739 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
13744 * @cfg {Number} height (in pixels)
13748 * @cfg {Number} width (in pixels)
13753 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13756 stylesheets: false,
13761 // private properties
13762 validationEvent : false,
13764 initialized : false,
13766 sourceEditMode : false,
13767 onFocus : Roo.emptyFn,
13769 hideMode:'offsets',
13777 * Protected method that will not generally be called directly. It
13778 * is called when the editor initializes the iframe with HTML contents. Override this method if you
13779 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13781 getDocMarkup : function(){
13784 Roo.log(this.stylesheets);
13786 // inherit styels from page...??
13787 if (this.stylesheets === false) {
13789 Roo.get(document.head).select('style').each(function(node) {
13790 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13793 Roo.get(document.head).select('link').each(function(node) {
13794 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13797 } else if (!this.stylesheets.length) {
13799 st = '<style type="text/css">' +
13800 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13803 Roo.each(this.stylesheets, function(s) {
13804 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13809 st += '<style type="text/css">' +
13810 'IMG { cursor: pointer } ' +
13814 return '<html><head>' + st +
13815 //<style type="text/css">' +
13816 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13818 ' </head><body class="roo-htmleditor-body"></body></html>';
13822 onRender : function(ct, position)
13825 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13826 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13829 this.el.dom.style.border = '0 none';
13830 this.el.dom.setAttribute('tabIndex', -1);
13831 this.el.addClass('x-hidden hide');
13835 if(Roo.isIE){ // fix IE 1px bogus margin
13836 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13840 this.frameId = Roo.id();
13844 var iframe = this.owner.wrap.createChild({
13846 cls: 'form-control', // bootstrap..
13848 name: this.frameId,
13849 frameBorder : 'no',
13850 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13855 this.iframe = iframe.dom;
13857 this.assignDocWin();
13859 this.doc.designMode = 'on';
13862 this.doc.write(this.getDocMarkup());
13866 var task = { // must defer to wait for browser to be ready
13868 //console.log("run task?" + this.doc.readyState);
13869 this.assignDocWin();
13870 if(this.doc.body || this.doc.readyState == 'complete'){
13872 this.doc.designMode="on";
13876 Roo.TaskMgr.stop(task);
13877 this.initEditor.defer(10, this);
13884 Roo.TaskMgr.start(task);
13891 onResize : function(w, h)
13893 Roo.log('resize: ' +w + ',' + h );
13894 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13898 if(typeof w == 'number'){
13900 this.iframe.style.width = w + 'px';
13902 if(typeof h == 'number'){
13904 this.iframe.style.height = h + 'px';
13906 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13913 * Toggles the editor between standard and source edit mode.
13914 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13916 toggleSourceEdit : function(sourceEditMode){
13918 this.sourceEditMode = sourceEditMode === true;
13920 if(this.sourceEditMode){
13922 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13925 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13926 //this.iframe.className = '';
13929 //this.setSize(this.owner.wrap.getSize());
13930 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13937 * Protected method that will not generally be called directly. If you need/want
13938 * custom HTML cleanup, this is the method you should override.
13939 * @param {String} html The HTML to be cleaned
13940 * return {String} The cleaned HTML
13942 cleanHtml : function(html){
13943 html = String(html);
13944 if(html.length > 5){
13945 if(Roo.isSafari){ // strip safari nonsense
13946 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13949 if(html == ' '){
13956 * HTML Editor -> Textarea
13957 * Protected method that will not generally be called directly. Syncs the contents
13958 * of the editor iframe with the textarea.
13960 syncValue : function(){
13961 if(this.initialized){
13962 var bd = (this.doc.body || this.doc.documentElement);
13963 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13964 var html = bd.innerHTML;
13966 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13967 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13969 html = '<div style="'+m[0]+'">' + html + '</div>';
13972 html = this.cleanHtml(html);
13973 // fix up the special chars.. normaly like back quotes in word...
13974 // however we do not want to do this with chinese..
13975 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13976 var cc = b.charCodeAt();
13978 (cc >= 0x4E00 && cc < 0xA000 ) ||
13979 (cc >= 0x3400 && cc < 0x4E00 ) ||
13980 (cc >= 0xf900 && cc < 0xfb00 )
13986 if(this.owner.fireEvent('beforesync', this, html) !== false){
13987 this.el.dom.value = html;
13988 this.owner.fireEvent('sync', this, html);
13994 * Protected method that will not generally be called directly. Pushes the value of the textarea
13995 * into the iframe editor.
13997 pushValue : function(){
13998 if(this.initialized){
13999 var v = this.el.dom.value.trim();
14001 // if(v.length < 1){
14005 if(this.owner.fireEvent('beforepush', this, v) !== false){
14006 var d = (this.doc.body || this.doc.documentElement);
14008 this.cleanUpPaste();
14009 this.el.dom.value = d.innerHTML;
14010 this.owner.fireEvent('push', this, v);
14016 deferFocus : function(){
14017 this.focus.defer(10, this);
14021 focus : function(){
14022 if(this.win && !this.sourceEditMode){
14029 assignDocWin: function()
14031 var iframe = this.iframe;
14034 this.doc = iframe.contentWindow.document;
14035 this.win = iframe.contentWindow;
14037 if (!Roo.get(this.frameId)) {
14040 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
14041 this.win = Roo.get(this.frameId).dom.contentWindow;
14046 initEditor : function(){
14047 //console.log("INIT EDITOR");
14048 this.assignDocWin();
14052 this.doc.designMode="on";
14054 this.doc.write(this.getDocMarkup());
14057 var dbody = (this.doc.body || this.doc.documentElement);
14058 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
14059 // this copies styles from the containing element into thsi one..
14060 // not sure why we need all of this..
14061 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
14062 ss['background-attachment'] = 'fixed'; // w3c
14063 dbody.bgProperties = 'fixed'; // ie
14064 Roo.DomHelper.applyStyles(dbody, ss);
14065 Roo.EventManager.on(this.doc, {
14066 //'mousedown': this.onEditorEvent,
14067 'mouseup': this.onEditorEvent,
14068 'dblclick': this.onEditorEvent,
14069 'click': this.onEditorEvent,
14070 'keyup': this.onEditorEvent,
14075 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
14077 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
14078 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
14080 this.initialized = true;
14082 this.owner.fireEvent('initialize', this);
14087 onDestroy : function(){
14093 //for (var i =0; i < this.toolbars.length;i++) {
14094 // // fixme - ask toolbars for heights?
14095 // this.toolbars[i].onDestroy();
14098 //this.wrap.dom.innerHTML = '';
14099 //this.wrap.remove();
14104 onFirstFocus : function(){
14106 this.assignDocWin();
14109 this.activated = true;
14112 if(Roo.isGecko){ // prevent silly gecko errors
14114 var s = this.win.getSelection();
14115 if(!s.focusNode || s.focusNode.nodeType != 3){
14116 var r = s.getRangeAt(0);
14117 r.selectNodeContents((this.doc.body || this.doc.documentElement));
14122 this.execCmd('useCSS', true);
14123 this.execCmd('styleWithCSS', false);
14126 this.owner.fireEvent('activate', this);
14130 adjustFont: function(btn){
14131 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
14132 //if(Roo.isSafari){ // safari
14135 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
14136 if(Roo.isSafari){ // safari
14137 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
14138 v = (v < 10) ? 10 : v;
14139 v = (v > 48) ? 48 : v;
14140 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
14145 v = Math.max(1, v+adjust);
14147 this.execCmd('FontSize', v );
14150 onEditorEvent : function(e){
14151 this.owner.fireEvent('editorevent', this, e);
14152 // this.updateToolbar();
14153 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
14156 insertTag : function(tg)
14158 // could be a bit smarter... -> wrap the current selected tRoo..
14159 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
14161 range = this.createRange(this.getSelection());
14162 var wrappingNode = this.doc.createElement(tg.toLowerCase());
14163 wrappingNode.appendChild(range.extractContents());
14164 range.insertNode(wrappingNode);
14171 this.execCmd("formatblock", tg);
14175 insertText : function(txt)
14179 var range = this.createRange();
14180 range.deleteContents();
14181 //alert(Sender.getAttribute('label'));
14183 range.insertNode(this.doc.createTextNode(txt));
14189 * Executes a Midas editor command on the editor document and performs necessary focus and
14190 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
14191 * @param {String} cmd The Midas command
14192 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14194 relayCmd : function(cmd, value){
14196 this.execCmd(cmd, value);
14197 this.owner.fireEvent('editorevent', this);
14198 //this.updateToolbar();
14199 this.owner.deferFocus();
14203 * Executes a Midas editor command directly on the editor document.
14204 * For visual commands, you should use {@link #relayCmd} instead.
14205 * <b>This should only be called after the editor is initialized.</b>
14206 * @param {String} cmd The Midas command
14207 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
14209 execCmd : function(cmd, value){
14210 this.doc.execCommand(cmd, false, value === undefined ? null : value);
14217 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
14219 * @param {String} text | dom node..
14221 insertAtCursor : function(text)
14226 if(!this.activated){
14232 var r = this.doc.selection.createRange();
14243 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
14247 // from jquery ui (MIT licenced)
14249 var win = this.win;
14251 if (win.getSelection && win.getSelection().getRangeAt) {
14252 range = win.getSelection().getRangeAt(0);
14253 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
14254 range.insertNode(node);
14255 } else if (win.document.selection && win.document.selection.createRange) {
14256 // no firefox support
14257 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14258 win.document.selection.createRange().pasteHTML(txt);
14260 // no firefox support
14261 var txt = typeof(text) == 'string' ? text : text.outerHTML;
14262 this.execCmd('InsertHTML', txt);
14271 mozKeyPress : function(e){
14273 var c = e.getCharCode(), cmd;
14276 c = String.fromCharCode(c).toLowerCase();
14290 this.cleanUpPaste.defer(100, this);
14298 e.preventDefault();
14306 fixKeys : function(){ // load time branching for fastest keydown performance
14308 return function(e){
14309 var k = e.getKey(), r;
14312 r = this.doc.selection.createRange();
14315 r.pasteHTML('    ');
14322 r = this.doc.selection.createRange();
14324 var target = r.parentElement();
14325 if(!target || target.tagName.toLowerCase() != 'li'){
14327 r.pasteHTML('<br />');
14333 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14334 this.cleanUpPaste.defer(100, this);
14340 }else if(Roo.isOpera){
14341 return function(e){
14342 var k = e.getKey();
14346 this.execCmd('InsertHTML','    ');
14349 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14350 this.cleanUpPaste.defer(100, this);
14355 }else if(Roo.isSafari){
14356 return function(e){
14357 var k = e.getKey();
14361 this.execCmd('InsertText','\t');
14365 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
14366 this.cleanUpPaste.defer(100, this);
14374 getAllAncestors: function()
14376 var p = this.getSelectedNode();
14379 a.push(p); // push blank onto stack..
14380 p = this.getParentElement();
14384 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
14388 a.push(this.doc.body);
14392 lastSelNode : false,
14395 getSelection : function()
14397 this.assignDocWin();
14398 return Roo.isIE ? this.doc.selection : this.win.getSelection();
14401 getSelectedNode: function()
14403 // this may only work on Gecko!!!
14405 // should we cache this!!!!
14410 var range = this.createRange(this.getSelection()).cloneRange();
14413 var parent = range.parentElement();
14415 var testRange = range.duplicate();
14416 testRange.moveToElementText(parent);
14417 if (testRange.inRange(range)) {
14420 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
14423 parent = parent.parentElement;
14428 // is ancestor a text element.
14429 var ac = range.commonAncestorContainer;
14430 if (ac.nodeType == 3) {
14431 ac = ac.parentNode;
14434 var ar = ac.childNodes;
14437 var other_nodes = [];
14438 var has_other_nodes = false;
14439 for (var i=0;i<ar.length;i++) {
14440 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
14443 // fullly contained node.
14445 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
14450 // probably selected..
14451 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
14452 other_nodes.push(ar[i]);
14456 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
14461 has_other_nodes = true;
14463 if (!nodes.length && other_nodes.length) {
14464 nodes= other_nodes;
14466 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
14472 createRange: function(sel)
14474 // this has strange effects when using with
14475 // top toolbar - not sure if it's a great idea.
14476 //this.editor.contentWindow.focus();
14477 if (typeof sel != "undefined") {
14479 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
14481 return this.doc.createRange();
14484 return this.doc.createRange();
14487 getParentElement: function()
14490 this.assignDocWin();
14491 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
14493 var range = this.createRange(sel);
14496 var p = range.commonAncestorContainer;
14497 while (p.nodeType == 3) { // text node
14508 * Range intersection.. the hard stuff...
14512 * [ -- selected range --- ]
14516 * if end is before start or hits it. fail.
14517 * if start is after end or hits it fail.
14519 * if either hits (but other is outside. - then it's not
14525 // @see http://www.thismuchiknow.co.uk/?p=64.
14526 rangeIntersectsNode : function(range, node)
14528 var nodeRange = node.ownerDocument.createRange();
14530 nodeRange.selectNode(node);
14532 nodeRange.selectNodeContents(node);
14535 var rangeStartRange = range.cloneRange();
14536 rangeStartRange.collapse(true);
14538 var rangeEndRange = range.cloneRange();
14539 rangeEndRange.collapse(false);
14541 var nodeStartRange = nodeRange.cloneRange();
14542 nodeStartRange.collapse(true);
14544 var nodeEndRange = nodeRange.cloneRange();
14545 nodeEndRange.collapse(false);
14547 return rangeStartRange.compareBoundaryPoints(
14548 Range.START_TO_START, nodeEndRange) == -1 &&
14549 rangeEndRange.compareBoundaryPoints(
14550 Range.START_TO_START, nodeStartRange) == 1;
14554 rangeCompareNode : function(range, node)
14556 var nodeRange = node.ownerDocument.createRange();
14558 nodeRange.selectNode(node);
14560 nodeRange.selectNodeContents(node);
14564 range.collapse(true);
14566 nodeRange.collapse(true);
14568 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
14569 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
14571 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
14573 var nodeIsBefore = ss == 1;
14574 var nodeIsAfter = ee == -1;
14576 if (nodeIsBefore && nodeIsAfter)
14578 if (!nodeIsBefore && nodeIsAfter)
14579 return 1; //right trailed.
14581 if (nodeIsBefore && !nodeIsAfter)
14582 return 2; // left trailed.
14587 // private? - in a new class?
14588 cleanUpPaste : function()
14590 // cleans up the whole document..
14591 Roo.log('cleanuppaste');
14593 this.cleanUpChildren(this.doc.body);
14594 var clean = this.cleanWordChars(this.doc.body.innerHTML);
14595 if (clean != this.doc.body.innerHTML) {
14596 this.doc.body.innerHTML = clean;
14601 cleanWordChars : function(input) {// change the chars to hex code
14602 var he = Roo.HtmlEditorCore;
14604 var output = input;
14605 Roo.each(he.swapCodes, function(sw) {
14606 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
14608 output = output.replace(swapper, sw[1]);
14615 cleanUpChildren : function (n)
14617 if (!n.childNodes.length) {
14620 for (var i = n.childNodes.length-1; i > -1 ; i--) {
14621 this.cleanUpChild(n.childNodes[i]);
14628 cleanUpChild : function (node)
14631 //console.log(node);
14632 if (node.nodeName == "#text") {
14633 // clean up silly Windows -- stuff?
14636 if (node.nodeName == "#comment") {
14637 node.parentNode.removeChild(node);
14638 // clean up silly Windows -- stuff?
14642 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
14644 node.parentNode.removeChild(node);
14649 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
14651 // remove <a name=....> as rendering on yahoo mailer is borked with this.
14652 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14654 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14655 // remove_keep_children = true;
14658 if (remove_keep_children) {
14659 this.cleanUpChildren(node);
14660 // inserts everything just before this node...
14661 while (node.childNodes.length) {
14662 var cn = node.childNodes[0];
14663 node.removeChild(cn);
14664 node.parentNode.insertBefore(cn, node);
14666 node.parentNode.removeChild(node);
14670 if (!node.attributes || !node.attributes.length) {
14671 this.cleanUpChildren(node);
14675 function cleanAttr(n,v)
14678 if (v.match(/^\./) || v.match(/^\//)) {
14681 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14684 if (v.match(/^#/)) {
14687 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14688 node.removeAttribute(n);
14692 function cleanStyle(n,v)
14694 if (v.match(/expression/)) { //XSS?? should we even bother..
14695 node.removeAttribute(n);
14698 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14699 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14702 var parts = v.split(/;/);
14705 Roo.each(parts, function(p) {
14706 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14710 var l = p.split(':').shift().replace(/\s+/g,'');
14711 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14713 if ( cblack.indexOf(l) > -1) {
14714 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14715 //node.removeAttribute(n);
14719 // only allow 'c whitelisted system attributes'
14720 if ( cwhite.length && cwhite.indexOf(l) < 0) {
14721 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14722 //node.removeAttribute(n);
14732 if (clean.length) {
14733 node.setAttribute(n, clean.join(';'));
14735 node.removeAttribute(n);
14741 for (var i = node.attributes.length-1; i > -1 ; i--) {
14742 var a = node.attributes[i];
14745 if (a.name.toLowerCase().substr(0,2)=='on') {
14746 node.removeAttribute(a.name);
14749 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14750 node.removeAttribute(a.name);
14753 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14754 cleanAttr(a.name,a.value); // fixme..
14757 if (a.name == 'style') {
14758 cleanStyle(a.name,a.value);
14761 /// clean up MS crap..
14762 // tecnically this should be a list of valid class'es..
14765 if (a.name == 'class') {
14766 if (a.value.match(/^Mso/)) {
14767 node.className = '';
14770 if (a.value.match(/body/)) {
14771 node.className = '';
14782 this.cleanUpChildren(node);
14788 // hide stuff that is not compatible
14802 * @event specialkey
14806 * @cfg {String} fieldClass @hide
14809 * @cfg {String} focusClass @hide
14812 * @cfg {String} autoCreate @hide
14815 * @cfg {String} inputType @hide
14818 * @cfg {String} invalidClass @hide
14821 * @cfg {String} invalidText @hide
14824 * @cfg {String} msgFx @hide
14827 * @cfg {String} validateOnBlur @hide
14831 Roo.HtmlEditorCore.white = [
14832 'area', 'br', 'img', 'input', 'hr', 'wbr',
14834 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14835 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14836 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14837 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14838 'table', 'ul', 'xmp',
14840 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14843 'dir', 'menu', 'ol', 'ul', 'dl',
14849 Roo.HtmlEditorCore.black = [
14850 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14852 'base', 'basefont', 'bgsound', 'blink', 'body',
14853 'frame', 'frameset', 'head', 'html', 'ilayer',
14854 'iframe', 'layer', 'link', 'meta', 'object',
14855 'script', 'style' ,'title', 'xml' // clean later..
14857 Roo.HtmlEditorCore.clean = [
14858 'script', 'style', 'title', 'xml'
14860 Roo.HtmlEditorCore.remove = [
14865 Roo.HtmlEditorCore.ablack = [
14869 Roo.HtmlEditorCore.aclean = [
14870 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14874 Roo.HtmlEditorCore.pwhite= [
14875 'http', 'https', 'mailto'
14878 // white listed style attributes.
14879 Roo.HtmlEditorCore.cwhite= [
14880 // 'text-align', /// default is to allow most things..
14886 // black listed style attributes.
14887 Roo.HtmlEditorCore.cblack= [
14888 // 'font-size' -- this can be set by the project
14892 Roo.HtmlEditorCore.swapCodes =[
14911 * @class Roo.bootstrap.HtmlEditor
14912 * @extends Roo.bootstrap.TextArea
14913 * Bootstrap HtmlEditor class
14916 * Create a new HtmlEditor
14917 * @param {Object} config The config object
14920 Roo.bootstrap.HtmlEditor = function(config){
14921 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14922 if (!this.toolbars) {
14923 this.toolbars = [];
14925 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14928 * @event initialize
14929 * Fires when the editor is fully initialized (including the iframe)
14930 * @param {HtmlEditor} this
14935 * Fires when the editor is first receives the focus. Any insertion must wait
14936 * until after this event.
14937 * @param {HtmlEditor} this
14941 * @event beforesync
14942 * Fires before the textarea is updated with content from the editor iframe. Return false
14943 * to cancel the sync.
14944 * @param {HtmlEditor} this
14945 * @param {String} html
14949 * @event beforepush
14950 * Fires before the iframe editor is updated with content from the textarea. Return false
14951 * to cancel the push.
14952 * @param {HtmlEditor} this
14953 * @param {String} html
14958 * Fires when the textarea is updated with content from the editor iframe.
14959 * @param {HtmlEditor} this
14960 * @param {String} html
14965 * Fires when the iframe editor is updated with content from the textarea.
14966 * @param {HtmlEditor} this
14967 * @param {String} html
14971 * @event editmodechange
14972 * Fires when the editor switches edit modes
14973 * @param {HtmlEditor} this
14974 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14976 editmodechange: true,
14978 * @event editorevent
14979 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14980 * @param {HtmlEditor} this
14984 * @event firstfocus
14985 * Fires when on first focus - needed by toolbars..
14986 * @param {HtmlEditor} this
14991 * Auto save the htmlEditor value as a file into Events
14992 * @param {HtmlEditor} this
14996 * @event savedpreview
14997 * preview the saved version of htmlEditor
14998 * @param {HtmlEditor} this
15005 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
15009 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
15014 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
15019 * @cfg {Number} height (in pixels)
15023 * @cfg {Number} width (in pixels)
15028 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
15031 stylesheets: false,
15036 // private properties
15037 validationEvent : false,
15039 initialized : false,
15042 onFocus : Roo.emptyFn,
15044 hideMode:'offsets',
15047 tbContainer : false,
15049 toolbarContainer :function() {
15050 return this.wrap.select('.x-html-editor-tb',true).first();
15054 * Protected method that will not generally be called directly. It
15055 * is called when the editor creates its toolbar. Override this method if you need to
15056 * add custom toolbar buttons.
15057 * @param {HtmlEditor} editor
15059 createToolbar : function(){
15061 Roo.log("create toolbars");
15063 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
15064 this.toolbars[0].render(this.toolbarContainer());
15068 // if (!editor.toolbars || !editor.toolbars.length) {
15069 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
15072 // for (var i =0 ; i < editor.toolbars.length;i++) {
15073 // editor.toolbars[i] = Roo.factory(
15074 // typeof(editor.toolbars[i]) == 'string' ?
15075 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
15076 // Roo.bootstrap.HtmlEditor);
15077 // editor.toolbars[i].init(editor);
15083 onRender : function(ct, position)
15085 // Roo.log("Call onRender: " + this.xtype);
15087 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
15089 this.wrap = this.inputEl().wrap({
15090 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
15093 this.editorcore.onRender(ct, position);
15095 if (this.resizable) {
15096 this.resizeEl = new Roo.Resizable(this.wrap, {
15100 minHeight : this.height,
15101 height: this.height,
15102 handles : this.resizable,
15105 resize : function(r, w, h) {
15106 _t.onResize(w,h); // -something
15112 this.createToolbar(this);
15115 if(!this.width && this.resizable){
15116 this.setSize(this.wrap.getSize());
15118 if (this.resizeEl) {
15119 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
15120 // should trigger onReize..
15126 onResize : function(w, h)
15128 Roo.log('resize: ' +w + ',' + h );
15129 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
15133 if(this.inputEl() ){
15134 if(typeof w == 'number'){
15135 var aw = w - this.wrap.getFrameWidth('lr');
15136 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
15139 if(typeof h == 'number'){
15140 var tbh = -11; // fixme it needs to tool bar size!
15141 for (var i =0; i < this.toolbars.length;i++) {
15142 // fixme - ask toolbars for heights?
15143 tbh += this.toolbars[i].el.getHeight();
15144 //if (this.toolbars[i].footer) {
15145 // tbh += this.toolbars[i].footer.el.getHeight();
15153 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
15154 ah -= 5; // knock a few pixes off for look..
15155 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
15159 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
15160 this.editorcore.onResize(ew,eh);
15165 * Toggles the editor between standard and source edit mode.
15166 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
15168 toggleSourceEdit : function(sourceEditMode)
15170 this.editorcore.toggleSourceEdit(sourceEditMode);
15172 if(this.editorcore.sourceEditMode){
15173 Roo.log('editor - showing textarea');
15176 // Roo.log(this.syncValue());
15178 this.inputEl().removeClass('hide');
15179 this.inputEl().dom.removeAttribute('tabIndex');
15180 this.inputEl().focus();
15182 Roo.log('editor - hiding textarea');
15184 // Roo.log(this.pushValue());
15187 this.inputEl().addClass('hide');
15188 this.inputEl().dom.setAttribute('tabIndex', -1);
15189 //this.deferFocus();
15192 if(this.resizable){
15193 this.setSize(this.wrap.getSize());
15196 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
15199 // private (for BoxComponent)
15200 adjustSize : Roo.BoxComponent.prototype.adjustSize,
15202 // private (for BoxComponent)
15203 getResizeEl : function(){
15207 // private (for BoxComponent)
15208 getPositionEl : function(){
15213 initEvents : function(){
15214 this.originalValue = this.getValue();
15218 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15221 // markInvalid : Roo.emptyFn,
15223 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
15226 // clearInvalid : Roo.emptyFn,
15228 setValue : function(v){
15229 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
15230 this.editorcore.pushValue();
15235 deferFocus : function(){
15236 this.focus.defer(10, this);
15240 focus : function(){
15241 this.editorcore.focus();
15247 onDestroy : function(){
15253 for (var i =0; i < this.toolbars.length;i++) {
15254 // fixme - ask toolbars for heights?
15255 this.toolbars[i].onDestroy();
15258 this.wrap.dom.innerHTML = '';
15259 this.wrap.remove();
15264 onFirstFocus : function(){
15265 //Roo.log("onFirstFocus");
15266 this.editorcore.onFirstFocus();
15267 for (var i =0; i < this.toolbars.length;i++) {
15268 this.toolbars[i].onFirstFocus();
15274 syncValue : function()
15276 this.editorcore.syncValue();
15279 pushValue : function()
15281 this.editorcore.pushValue();
15285 // hide stuff that is not compatible
15299 * @event specialkey
15303 * @cfg {String} fieldClass @hide
15306 * @cfg {String} focusClass @hide
15309 * @cfg {String} autoCreate @hide
15312 * @cfg {String} inputType @hide
15315 * @cfg {String} invalidClass @hide
15318 * @cfg {String} invalidText @hide
15321 * @cfg {String} msgFx @hide
15324 * @cfg {String} validateOnBlur @hide
15335 * @class Roo.bootstrap.HtmlEditorToolbar1
15340 new Roo.bootstrap.HtmlEditor({
15343 new Roo.bootstrap.HtmlEditorToolbar1({
15344 disable : { fonts: 1 , format: 1, ..., ... , ...],
15350 * @cfg {Object} disable List of elements to disable..
15351 * @cfg {Array} btns List of additional buttons.
15355 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
15358 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
15361 Roo.apply(this, config);
15363 // default disabled, based on 'good practice'..
15364 this.disable = this.disable || {};
15365 Roo.applyIf(this.disable, {
15368 specialElements : true
15370 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
15372 this.editor = config.editor;
15373 this.editorcore = config.editor.editorcore;
15375 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
15377 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
15378 // dont call parent... till later.
15380 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
15386 editorcore : false,
15391 "h1","h2","h3","h4","h5","h6",
15393 "abbr", "acronym", "address", "cite", "samp", "var",
15397 onRender : function(ct, position)
15399 // Roo.log("Call onRender: " + this.xtype);
15401 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
15403 this.el.dom.style.marginBottom = '0';
15405 var editorcore = this.editorcore;
15406 var editor= this.editor;
15409 var btn = function(id,cmd , toggle, handler){
15411 var event = toggle ? 'toggle' : 'click';
15416 xns: Roo.bootstrap,
15419 enableToggle:toggle !== false,
15421 pressed : toggle ? false : null,
15424 a.listeners[toggle ? 'toggle' : 'click'] = function() {
15425 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
15434 xns: Roo.bootstrap,
15435 glyphicon : 'font',
15439 xns: Roo.bootstrap,
15443 Roo.each(this.formats, function(f) {
15444 style.menu.items.push({
15446 xns: Roo.bootstrap,
15447 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
15452 editorcore.insertTag(this.tagname);
15459 children.push(style);
15462 btn('bold',false,true);
15463 btn('italic',false,true);
15464 btn('align-left', 'justifyleft',true);
15465 btn('align-center', 'justifycenter',true);
15466 btn('align-right' , 'justifyright',true);
15467 btn('link', false, false, function(btn) {
15468 //Roo.log("create link?");
15469 var url = prompt(this.createLinkText, this.defaultLinkValue);
15470 if(url && url != 'http:/'+'/'){
15471 this.editorcore.relayCmd('createlink', url);
15474 btn('list','insertunorderedlist',true);
15475 btn('pencil', false,true, function(btn){
15478 this.toggleSourceEdit(btn.pressed);
15484 xns: Roo.bootstrap,
15489 xns: Roo.bootstrap,
15494 cog.menu.items.push({
15496 xns: Roo.bootstrap,
15497 html : Clean styles,
15502 editorcore.insertTag(this.tagname);
15511 this.xtype = 'Navbar';
15513 for(var i=0;i< children.length;i++) {
15515 this.buttons.add(this.addxtypeChild(children[i]));
15519 editor.on('editorevent', this.updateToolbar, this);
15521 onBtnClick : function(id)
15523 this.editorcore.relayCmd(id);
15524 this.editorcore.focus();
15528 * Protected method that will not generally be called directly. It triggers
15529 * a toolbar update by reading the markup state of the current selection in the editor.
15531 updateToolbar: function(){
15533 if(!this.editorcore.activated){
15534 this.editor.onFirstFocus(); // is this neeed?
15538 var btns = this.buttons;
15539 var doc = this.editorcore.doc;
15540 btns.get('bold').setActive(doc.queryCommandState('bold'));
15541 btns.get('italic').setActive(doc.queryCommandState('italic'));
15542 //btns.get('underline').setActive(doc.queryCommandState('underline'));
15544 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
15545 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
15546 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
15548 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
15549 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
15552 var ans = this.editorcore.getAllAncestors();
15553 if (this.formatCombo) {
15556 var store = this.formatCombo.store;
15557 this.formatCombo.setValue("");
15558 for (var i =0; i < ans.length;i++) {
15559 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
15561 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
15569 // hides menus... - so this cant be on a menu...
15570 Roo.bootstrap.MenuMgr.hideAll();
15572 Roo.bootstrap.MenuMgr.hideAll();
15573 //this.editorsyncValue();
15575 onFirstFocus: function() {
15576 this.buttons.each(function(item){
15580 toggleSourceEdit : function(sourceEditMode){
15583 if(sourceEditMode){
15584 Roo.log("disabling buttons");
15585 this.buttons.each( function(item){
15586 if(item.cmd != 'pencil'){
15592 Roo.log("enabling buttons");
15593 if(this.editorcore.initialized){
15594 this.buttons.each( function(item){
15600 Roo.log("calling toggole on editor");
15601 // tell the editor that it's been pressed..
15602 this.editor.toggleSourceEdit(sourceEditMode);
15612 * @class Roo.bootstrap.Table.AbstractSelectionModel
15613 * @extends Roo.util.Observable
15614 * Abstract base class for grid SelectionModels. It provides the interface that should be
15615 * implemented by descendant classes. This class should not be directly instantiated.
15618 Roo.bootstrap.Table.AbstractSelectionModel = function(){
15619 this.locked = false;
15620 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
15624 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
15625 /** @ignore Called by the grid automatically. Do not call directly. */
15626 init : function(grid){
15632 * Locks the selections.
15635 this.locked = true;
15639 * Unlocks the selections.
15641 unlock : function(){
15642 this.locked = false;
15646 * Returns true if the selections are locked.
15647 * @return {Boolean}
15649 isLocked : function(){
15650 return this.locked;
15654 * @class Roo.bootstrap.Table.ColumnModel
15655 * @extends Roo.util.Observable
15656 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15657 * the columns in the table.
15660 * @param {Object} config An Array of column config objects. See this class's
15661 * config objects for details.
15663 Roo.bootstrap.Table.ColumnModel = function(config){
15665 * The config passed into the constructor
15667 this.config = config;
15670 // if no id, create one
15671 // if the column does not have a dataIndex mapping,
15672 // map it to the order it is in the config
15673 for(var i = 0, len = config.length; i < len; i++){
15675 if(typeof c.dataIndex == "undefined"){
15678 if(typeof c.renderer == "string"){
15679 c.renderer = Roo.util.Format[c.renderer];
15681 if(typeof c.id == "undefined"){
15684 // if(c.editor && c.editor.xtype){
15685 // c.editor = Roo.factory(c.editor, Roo.grid);
15687 // if(c.editor && c.editor.isFormField){
15688 // c.editor = new Roo.grid.GridEditor(c.editor);
15691 this.lookup[c.id] = c;
15695 * The width of columns which have no width specified (defaults to 100)
15698 this.defaultWidth = 100;
15701 * Default sortable of columns which have no sortable specified (defaults to false)
15704 this.defaultSortable = false;
15708 * @event widthchange
15709 * Fires when the width of a column changes.
15710 * @param {ColumnModel} this
15711 * @param {Number} columnIndex The column index
15712 * @param {Number} newWidth The new width
15714 "widthchange": true,
15716 * @event headerchange
15717 * Fires when the text of a header changes.
15718 * @param {ColumnModel} this
15719 * @param {Number} columnIndex The column index
15720 * @param {Number} newText The new header text
15722 "headerchange": true,
15724 * @event hiddenchange
15725 * Fires when a column is hidden or "unhidden".
15726 * @param {ColumnModel} this
15727 * @param {Number} columnIndex The column index
15728 * @param {Boolean} hidden true if hidden, false otherwise
15730 "hiddenchange": true,
15732 * @event columnmoved
15733 * Fires when a column is moved.
15734 * @param {ColumnModel} this
15735 * @param {Number} oldIndex
15736 * @param {Number} newIndex
15738 "columnmoved" : true,
15740 * @event columlockchange
15741 * Fires when a column's locked state is changed
15742 * @param {ColumnModel} this
15743 * @param {Number} colIndex
15744 * @param {Boolean} locked true if locked
15746 "columnlockchange" : true
15748 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15750 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15752 * @cfg {String} header The header text to display in the Grid view.
15755 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15756 * {@link Roo.data.Record} definition from which to draw the column's value. If not
15757 * specified, the column's index is used as an index into the Record's data Array.
15760 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15761 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15764 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15765 * Defaults to the value of the {@link #defaultSortable} property.
15766 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15769 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
15772 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
15775 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15778 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15781 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15782 * given the cell's data value. See {@link #setRenderer}. If not specified, the
15783 * default renderer uses the raw data value.
15786 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
15790 * Returns the id of the column at the specified index.
15791 * @param {Number} index The column index
15792 * @return {String} the id
15794 getColumnId : function(index){
15795 return this.config[index].id;
15799 * Returns the column for a specified id.
15800 * @param {String} id The column id
15801 * @return {Object} the column
15803 getColumnById : function(id){
15804 return this.lookup[id];
15809 * Returns the column for a specified dataIndex.
15810 * @param {String} dataIndex The column dataIndex
15811 * @return {Object|Boolean} the column or false if not found
15813 getColumnByDataIndex: function(dataIndex){
15814 var index = this.findColumnIndex(dataIndex);
15815 return index > -1 ? this.config[index] : false;
15819 * Returns the index for a specified column id.
15820 * @param {String} id The column id
15821 * @return {Number} the index, or -1 if not found
15823 getIndexById : function(id){
15824 for(var i = 0, len = this.config.length; i < len; i++){
15825 if(this.config[i].id == id){
15833 * Returns the index for a specified column dataIndex.
15834 * @param {String} dataIndex The column dataIndex
15835 * @return {Number} the index, or -1 if not found
15838 findColumnIndex : function(dataIndex){
15839 for(var i = 0, len = this.config.length; i < len; i++){
15840 if(this.config[i].dataIndex == dataIndex){
15848 moveColumn : function(oldIndex, newIndex){
15849 var c = this.config[oldIndex];
15850 this.config.splice(oldIndex, 1);
15851 this.config.splice(newIndex, 0, c);
15852 this.dataMap = null;
15853 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15856 isLocked : function(colIndex){
15857 return this.config[colIndex].locked === true;
15860 setLocked : function(colIndex, value, suppressEvent){
15861 if(this.isLocked(colIndex) == value){
15864 this.config[colIndex].locked = value;
15865 if(!suppressEvent){
15866 this.fireEvent("columnlockchange", this, colIndex, value);
15870 getTotalLockedWidth : function(){
15871 var totalWidth = 0;
15872 for(var i = 0; i < this.config.length; i++){
15873 if(this.isLocked(i) && !this.isHidden(i)){
15874 this.totalWidth += this.getColumnWidth(i);
15880 getLockedCount : function(){
15881 for(var i = 0, len = this.config.length; i < len; i++){
15882 if(!this.isLocked(i)){
15889 * Returns the number of columns.
15892 getColumnCount : function(visibleOnly){
15893 if(visibleOnly === true){
15895 for(var i = 0, len = this.config.length; i < len; i++){
15896 if(!this.isHidden(i)){
15902 return this.config.length;
15906 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15907 * @param {Function} fn
15908 * @param {Object} scope (optional)
15909 * @return {Array} result
15911 getColumnsBy : function(fn, scope){
15913 for(var i = 0, len = this.config.length; i < len; i++){
15914 var c = this.config[i];
15915 if(fn.call(scope||this, c, i) === true){
15923 * Returns true if the specified column is sortable.
15924 * @param {Number} col The column index
15925 * @return {Boolean}
15927 isSortable : function(col){
15928 if(typeof this.config[col].sortable == "undefined"){
15929 return this.defaultSortable;
15931 return this.config[col].sortable;
15935 * Returns the rendering (formatting) function defined for the column.
15936 * @param {Number} col The column index.
15937 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15939 getRenderer : function(col){
15940 if(!this.config[col].renderer){
15941 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15943 return this.config[col].renderer;
15947 * Sets the rendering (formatting) function for a column.
15948 * @param {Number} col The column index
15949 * @param {Function} fn The function to use to process the cell's raw data
15950 * to return HTML markup for the grid view. The render function is called with
15951 * the following parameters:<ul>
15952 * <li>Data value.</li>
15953 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15954 * <li>css A CSS style string to apply to the table cell.</li>
15955 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15956 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15957 * <li>Row index</li>
15958 * <li>Column index</li>
15959 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15961 setRenderer : function(col, fn){
15962 this.config[col].renderer = fn;
15966 * Returns the width for the specified column.
15967 * @param {Number} col The column index
15970 getColumnWidth : function(col){
15971 return this.config[col].width * 1 || this.defaultWidth;
15975 * Sets the width for a column.
15976 * @param {Number} col The column index
15977 * @param {Number} width The new width
15979 setColumnWidth : function(col, width, suppressEvent){
15980 this.config[col].width = width;
15981 this.totalWidth = null;
15982 if(!suppressEvent){
15983 this.fireEvent("widthchange", this, col, width);
15988 * Returns the total width of all columns.
15989 * @param {Boolean} includeHidden True to include hidden column widths
15992 getTotalWidth : function(includeHidden){
15993 if(!this.totalWidth){
15994 this.totalWidth = 0;
15995 for(var i = 0, len = this.config.length; i < len; i++){
15996 if(includeHidden || !this.isHidden(i)){
15997 this.totalWidth += this.getColumnWidth(i);
16001 return this.totalWidth;
16005 * Returns the header for the specified column.
16006 * @param {Number} col The column index
16009 getColumnHeader : function(col){
16010 return this.config[col].header;
16014 * Sets the header for a column.
16015 * @param {Number} col The column index
16016 * @param {String} header The new header
16018 setColumnHeader : function(col, header){
16019 this.config[col].header = header;
16020 this.fireEvent("headerchange", this, col, header);
16024 * Returns the tooltip for the specified column.
16025 * @param {Number} col The column index
16028 getColumnTooltip : function(col){
16029 return this.config[col].tooltip;
16032 * Sets the tooltip for a column.
16033 * @param {Number} col The column index
16034 * @param {String} tooltip The new tooltip
16036 setColumnTooltip : function(col, tooltip){
16037 this.config[col].tooltip = tooltip;
16041 * Returns the dataIndex for the specified column.
16042 * @param {Number} col The column index
16045 getDataIndex : function(col){
16046 return this.config[col].dataIndex;
16050 * Sets the dataIndex for a column.
16051 * @param {Number} col The column index
16052 * @param {Number} dataIndex The new dataIndex
16054 setDataIndex : function(col, dataIndex){
16055 this.config[col].dataIndex = dataIndex;
16061 * Returns true if the cell is editable.
16062 * @param {Number} colIndex The column index
16063 * @param {Number} rowIndex The row index
16064 * @return {Boolean}
16066 isCellEditable : function(colIndex, rowIndex){
16067 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
16071 * Returns the editor defined for the cell/column.
16072 * return false or null to disable editing.
16073 * @param {Number} colIndex The column index
16074 * @param {Number} rowIndex The row index
16077 getCellEditor : function(colIndex, rowIndex){
16078 return this.config[colIndex].editor;
16082 * Sets if a column is editable.
16083 * @param {Number} col The column index
16084 * @param {Boolean} editable True if the column is editable
16086 setEditable : function(col, editable){
16087 this.config[col].editable = editable;
16092 * Returns true if the column is hidden.
16093 * @param {Number} colIndex The column index
16094 * @return {Boolean}
16096 isHidden : function(colIndex){
16097 return this.config[colIndex].hidden;
16102 * Returns true if the column width cannot be changed
16104 isFixed : function(colIndex){
16105 return this.config[colIndex].fixed;
16109 * Returns true if the column can be resized
16110 * @return {Boolean}
16112 isResizable : function(colIndex){
16113 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
16116 * Sets if a column is hidden.
16117 * @param {Number} colIndex The column index
16118 * @param {Boolean} hidden True if the column is hidden
16120 setHidden : function(colIndex, hidden){
16121 this.config[colIndex].hidden = hidden;
16122 this.totalWidth = null;
16123 this.fireEvent("hiddenchange", this, colIndex, hidden);
16127 * Sets the editor for a column.
16128 * @param {Number} col The column index
16129 * @param {Object} editor The editor object
16131 setEditor : function(col, editor){
16132 this.config[col].editor = editor;
16136 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
16137 if(typeof value == "string" && value.length < 1){
16143 // Alias for backwards compatibility
16144 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
16147 * @extends Roo.bootstrap.Table.AbstractSelectionModel
16148 * @class Roo.bootstrap.Table.RowSelectionModel
16149 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
16150 * It supports multiple selections and keyboard selection/navigation.
16152 * @param {Object} config
16155 Roo.bootstrap.Table.RowSelectionModel = function(config){
16156 Roo.apply(this, config);
16157 this.selections = new Roo.util.MixedCollection(false, function(o){
16162 this.lastActive = false;
16166 * @event selectionchange
16167 * Fires when the selection changes
16168 * @param {SelectionModel} this
16170 "selectionchange" : true,
16172 * @event afterselectionchange
16173 * Fires after the selection changes (eg. by key press or clicking)
16174 * @param {SelectionModel} this
16176 "afterselectionchange" : true,
16178 * @event beforerowselect
16179 * Fires when a row is selected being selected, return false to cancel.
16180 * @param {SelectionModel} this
16181 * @param {Number} rowIndex The selected index
16182 * @param {Boolean} keepExisting False if other selections will be cleared
16184 "beforerowselect" : true,
16187 * Fires when a row is selected.
16188 * @param {SelectionModel} this
16189 * @param {Number} rowIndex The selected index
16190 * @param {Roo.data.Record} r The record
16192 "rowselect" : true,
16194 * @event rowdeselect
16195 * Fires when a row is deselected.
16196 * @param {SelectionModel} this
16197 * @param {Number} rowIndex The selected index
16199 "rowdeselect" : true
16201 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
16202 this.locked = false;
16205 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
16207 * @cfg {Boolean} singleSelect
16208 * True to allow selection of only one row at a time (defaults to false)
16210 singleSelect : false,
16213 initEvents : function(){
16215 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
16216 this.grid.on("mousedown", this.handleMouseDown, this);
16217 }else{ // allow click to work like normal
16218 this.grid.on("rowclick", this.handleDragableRowClick, this);
16221 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
16222 "up" : function(e){
16224 this.selectPrevious(e.shiftKey);
16225 }else if(this.last !== false && this.lastActive !== false){
16226 var last = this.last;
16227 this.selectRange(this.last, this.lastActive-1);
16228 this.grid.getView().focusRow(this.lastActive);
16229 if(last !== false){
16233 this.selectFirstRow();
16235 this.fireEvent("afterselectionchange", this);
16237 "down" : function(e){
16239 this.selectNext(e.shiftKey);
16240 }else if(this.last !== false && this.lastActive !== false){
16241 var last = this.last;
16242 this.selectRange(this.last, this.lastActive+1);
16243 this.grid.getView().focusRow(this.lastActive);
16244 if(last !== false){
16248 this.selectFirstRow();
16250 this.fireEvent("afterselectionchange", this);
16255 var view = this.grid.view;
16256 view.on("refresh", this.onRefresh, this);
16257 view.on("rowupdated", this.onRowUpdated, this);
16258 view.on("rowremoved", this.onRemove, this);
16262 onRefresh : function(){
16263 var ds = this.grid.dataSource, i, v = this.grid.view;
16264 var s = this.selections;
16265 s.each(function(r){
16266 if((i = ds.indexOfId(r.id)) != -1){
16275 onRemove : function(v, index, r){
16276 this.selections.remove(r);
16280 onRowUpdated : function(v, index, r){
16281 if(this.isSelected(r)){
16282 v.onRowSelect(index);
16288 * @param {Array} records The records to select
16289 * @param {Boolean} keepExisting (optional) True to keep existing selections
16291 selectRecords : function(records, keepExisting){
16293 this.clearSelections();
16295 var ds = this.grid.dataSource;
16296 for(var i = 0, len = records.length; i < len; i++){
16297 this.selectRow(ds.indexOf(records[i]), true);
16302 * Gets the number of selected rows.
16305 getCount : function(){
16306 return this.selections.length;
16310 * Selects the first row in the grid.
16312 selectFirstRow : function(){
16317 * Select the last row.
16318 * @param {Boolean} keepExisting (optional) True to keep existing selections
16320 selectLastRow : function(keepExisting){
16321 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
16325 * Selects the row immediately following the last selected row.
16326 * @param {Boolean} keepExisting (optional) True to keep existing selections
16328 selectNext : function(keepExisting){
16329 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
16330 this.selectRow(this.last+1, keepExisting);
16331 this.grid.getView().focusRow(this.last);
16336 * Selects the row that precedes the last selected row.
16337 * @param {Boolean} keepExisting (optional) True to keep existing selections
16339 selectPrevious : function(keepExisting){
16341 this.selectRow(this.last-1, keepExisting);
16342 this.grid.getView().focusRow(this.last);
16347 * Returns the selected records
16348 * @return {Array} Array of selected records
16350 getSelections : function(){
16351 return [].concat(this.selections.items);
16355 * Returns the first selected record.
16358 getSelected : function(){
16359 return this.selections.itemAt(0);
16364 * Clears all selections.
16366 clearSelections : function(fast){
16367 if(this.locked) return;
16369 var ds = this.grid.dataSource;
16370 var s = this.selections;
16371 s.each(function(r){
16372 this.deselectRow(ds.indexOfId(r.id));
16376 this.selections.clear();
16383 * Selects all rows.
16385 selectAll : function(){
16386 if(this.locked) return;
16387 this.selections.clear();
16388 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
16389 this.selectRow(i, true);
16394 * Returns True if there is a selection.
16395 * @return {Boolean}
16397 hasSelection : function(){
16398 return this.selections.length > 0;
16402 * Returns True if the specified row is selected.
16403 * @param {Number/Record} record The record or index of the record to check
16404 * @return {Boolean}
16406 isSelected : function(index){
16407 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
16408 return (r && this.selections.key(r.id) ? true : false);
16412 * Returns True if the specified record id is selected.
16413 * @param {String} id The id of record to check
16414 * @return {Boolean}
16416 isIdSelected : function(id){
16417 return (this.selections.key(id) ? true : false);
16421 handleMouseDown : function(e, t){
16422 var view = this.grid.getView(), rowIndex;
16423 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
16426 if(e.shiftKey && this.last !== false){
16427 var last = this.last;
16428 this.selectRange(last, rowIndex, e.ctrlKey);
16429 this.last = last; // reset the last
16430 view.focusRow(rowIndex);
16432 var isSelected = this.isSelected(rowIndex);
16433 if(e.button !== 0 && isSelected){
16434 view.focusRow(rowIndex);
16435 }else if(e.ctrlKey && isSelected){
16436 this.deselectRow(rowIndex);
16437 }else if(!isSelected){
16438 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
16439 view.focusRow(rowIndex);
16442 this.fireEvent("afterselectionchange", this);
16445 handleDragableRowClick : function(grid, rowIndex, e)
16447 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
16448 this.selectRow(rowIndex, false);
16449 grid.view.focusRow(rowIndex);
16450 this.fireEvent("afterselectionchange", this);
16455 * Selects multiple rows.
16456 * @param {Array} rows Array of the indexes of the row to select
16457 * @param {Boolean} keepExisting (optional) True to keep existing selections
16459 selectRows : function(rows, keepExisting){
16461 this.clearSelections();
16463 for(var i = 0, len = rows.length; i < len; i++){
16464 this.selectRow(rows[i], true);
16469 * Selects a range of rows. All rows in between startRow and endRow are also selected.
16470 * @param {Number} startRow The index of the first row in the range
16471 * @param {Number} endRow The index of the last row in the range
16472 * @param {Boolean} keepExisting (optional) True to retain existing selections
16474 selectRange : function(startRow, endRow, keepExisting){
16475 if(this.locked) return;
16477 this.clearSelections();
16479 if(startRow <= endRow){
16480 for(var i = startRow; i <= endRow; i++){
16481 this.selectRow(i, true);
16484 for(var i = startRow; i >= endRow; i--){
16485 this.selectRow(i, true);
16491 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
16492 * @param {Number} startRow The index of the first row in the range
16493 * @param {Number} endRow The index of the last row in the range
16495 deselectRange : function(startRow, endRow, preventViewNotify){
16496 if(this.locked) return;
16497 for(var i = startRow; i <= endRow; i++){
16498 this.deselectRow(i, preventViewNotify);
16504 * @param {Number} row The index of the row to select
16505 * @param {Boolean} keepExisting (optional) True to keep existing selections
16507 selectRow : function(index, keepExisting, preventViewNotify){
16508 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
16509 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
16510 if(!keepExisting || this.singleSelect){
16511 this.clearSelections();
16513 var r = this.grid.dataSource.getAt(index);
16514 this.selections.add(r);
16515 this.last = this.lastActive = index;
16516 if(!preventViewNotify){
16517 this.grid.getView().onRowSelect(index);
16519 this.fireEvent("rowselect", this, index, r);
16520 this.fireEvent("selectionchange", this);
16526 * @param {Number} row The index of the row to deselect
16528 deselectRow : function(index, preventViewNotify){
16529 if(this.locked) return;
16530 if(this.last == index){
16533 if(this.lastActive == index){
16534 this.lastActive = false;
16536 var r = this.grid.dataSource.getAt(index);
16537 this.selections.remove(r);
16538 if(!preventViewNotify){
16539 this.grid.getView().onRowDeselect(index);
16541 this.fireEvent("rowdeselect", this, index);
16542 this.fireEvent("selectionchange", this);
16546 restoreLast : function(){
16548 this.last = this._last;
16553 acceptsNav : function(row, col, cm){
16554 return !cm.isHidden(col) && cm.isCellEditable(col, row);
16558 onEditorKey : function(field, e){
16559 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
16564 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
16566 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
16568 }else if(k == e.ENTER && !e.ctrlKey){
16572 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
16574 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
16576 }else if(k == e.ESC){
16580 g.startEditing(newCell[0], newCell[1]);
16591 * @class Roo.bootstrap.MessageBar
16592 * @extends Roo.bootstrap.Component
16593 * Bootstrap MessageBar class
16594 * @cfg {String} html contents of the MessageBar
16595 * @cfg {String} weight (info | success | warning | danger) default info
16596 * @cfg {String} beforeClass insert the bar before the given class
16597 * @cfg {Boolean} closable (true | false) default false
16598 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
16601 * Create a new Element
16602 * @param {Object} config The config object
16605 Roo.bootstrap.MessageBar = function(config){
16606 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
16609 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
16615 beforeClass: 'bootstrap-sticky-wrap',
16617 getAutoCreate : function(){
16621 cls: 'alert alert-dismissable alert-' + this.weight,
16626 html: this.html || ''
16632 cfg.cls += ' alert-messages-fixed';
16646 onRender : function(ct, position)
16648 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16651 var cfg = Roo.apply({}, this.getAutoCreate());
16655 cfg.cls += ' ' + this.cls;
16658 cfg.style = this.style;
16660 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16662 this.el.setVisibilityMode(Roo.Element.DISPLAY);
16665 this.el.select('>button.close').on('click', this.hide, this);
16671 if (!this.rendered) {
16677 this.fireEvent('show', this);
16683 if (!this.rendered) {
16689 this.fireEvent('hide', this);
16692 update : function()
16694 // var e = this.el.dom.firstChild;
16696 // if(this.closable){
16697 // e = e.nextSibling;
16700 // e.data = this.html || '';
16702 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';