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 = this.html || cfg.html;
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);
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);
734 * @class Roo.bootstrap.Column
735 * @extends Roo.bootstrap.Component
736 * Bootstrap Column class
737 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739 * @cfg {Number} md colspan out of 12 for computer-sized screens
740 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741 * @cfg {String} html content of column.
744 * Create a new Column
745 * @param {Object} config The config object
748 Roo.bootstrap.Column = function(config){
749 Roo.bootstrap.Column.superclass.constructor.call(this, config);
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
761 getAutoCreate : function(){
762 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
770 ['xs','sm','md','lg'].map(function(size){
771 if (settings[size]) {
772 cfg.cls += ' col-' + size + '-' + settings[size];
775 if (this.html.length) {
776 cfg.html = this.html;
795 * @class Roo.bootstrap.Container
796 * @extends Roo.bootstrap.Component
797 * Bootstrap Container class
798 * @cfg {Boolean} jumbotron is it a jumbotron element
799 * @cfg {String} html content of element
800 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802 * @cfg {String} header content of header (for panel)
803 * @cfg {String} footer content of footer (for panel)
804 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
807 * Create a new Container
808 * @param {Object} config The config object
811 Roo.bootstrap.Container = function(config){
812 Roo.bootstrap.Container.superclass.constructor.call(this, config);
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
825 getChildContainer : function() {
831 if (this.panel.length) {
832 return this.el.select('.panel-body',true).first();
839 getAutoCreate : function(){
845 if (this.jumbotron) {
846 cfg.cls = 'jumbotron';
849 cfg.cls = this.cls + '';
852 if (this.sticky.length) {
854 var bd = Roo.get(document.body);
855 if (!bd.hasClass('bootstrap-sticky')) {
856 bd.addClass('bootstrap-sticky');
857 Roo.select('html',true).setStyle('height', '100%');
860 cfg.cls += 'bootstrap-sticky-' + this.sticky;
864 if (this.well.length) {
868 cfg.cls +=' well well-' +this.well;
878 if (this.panel.length) {
879 cfg.cls += ' panel panel-' + this.panel;
881 if (this.header.length) {
884 cls : 'panel-heading',
900 if (this.footer.length) {
902 cls : 'panel-footer',
910 body.html = this.html || cfg.html;
912 if (!cfg.cls.length) {
913 cfg.cls = 'container';
930 * @class Roo.bootstrap.Img
931 * @extends Roo.bootstrap.Component
932 * Bootstrap Img class
933 * @cfg {Boolean} imgResponsive false | true
934 * @cfg {String} border rounded | circle | thumbnail
935 * @cfg {String} src image source
936 * @cfg {String} alt image alternative text
937 * @cfg {String} href a tag href
938 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
942 * @param {Object} config The config object
945 Roo.bootstrap.Img = function(config){
946 Roo.bootstrap.Img.superclass.constructor.call(this, config);
952 * The img click event for the img.
953 * @param {Roo.EventObject} e
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
967 getAutoCreate : function(){
971 cls: 'img-responsive',
975 cfg.html = this.html || cfg.html;
977 cfg.src = this.src || cfg.src;
979 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980 cfg.cls += ' img-' + this.border;
997 a.target = this.target;
1003 return (this.href) ? a : cfg;
1006 initEvents: function() {
1009 this.el.on('click', this.onClick, this);
1013 onClick : function(e)
1015 Roo.log('img onclick');
1016 this.fireEvent('click', this, e);
1029 * @class Roo.bootstrap.Header
1030 * @extends Roo.bootstrap.Component
1031 * Bootstrap Header class
1032 * @cfg {String} html content of header
1033 * @cfg {Number} level (1|2|3|4|5|6) default 1
1036 * Create a new Header
1037 * @param {Object} config The config object
1041 Roo.bootstrap.Header = function(config){
1042 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1053 getAutoCreate : function(){
1056 tag: 'h' + (1 *this.level),
1057 html: this.html || 'fill in html'
1069 * Ext JS Library 1.1.1
1070 * Copyright(c) 2006-2007, Ext JS, LLC.
1072 * Originally Released Under LGPL - original licence link has changed is not relivant.
1075 * <script type="text/javascript">
1079 * @class Roo.bootstrap.MenuMgr
1080 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1083 Roo.bootstrap.MenuMgr = function(){
1084 var menus, active, groups = {}, attached = false, lastShow = new Date();
1086 // private - called when first menu is created
1089 active = new Roo.util.MixedCollection();
1090 Roo.get(document).addKeyListener(27, function(){
1091 if(active.length > 0){
1099 if(active && active.length > 0){
1100 var c = active.clone();
1110 if(active.length < 1){
1111 Roo.get(document).un("mouseup", onMouseDown);
1119 var last = active.last();
1120 lastShow = new Date();
1123 Roo.get(document).on("mouseup", onMouseDown);
1128 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129 m.parentMenu.activeChild = m;
1130 }else if(last && last.isVisible()){
1131 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1136 function onBeforeHide(m){
1138 m.activeChild.hide();
1140 if(m.autoHideTimer){
1141 clearTimeout(m.autoHideTimer);
1142 delete m.autoHideTimer;
1147 function onBeforeShow(m){
1148 var pm = m.parentMenu;
1149 if(!pm && !m.allowOtherMenus){
1151 }else if(pm && pm.activeChild && active != m){
1152 pm.activeChild.hide();
1157 function onMouseDown(e){
1158 Roo.log("on MouseDown");
1159 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1167 function onBeforeCheck(mi, state){
1169 var g = groups[mi.group];
1170 for(var i = 0, l = g.length; i < l; i++){
1172 g[i].setChecked(false);
1181 * Hides all menus that are currently visible
1183 hideAll : function(){
1188 register : function(menu){
1192 menus[menu.id] = menu;
1193 menu.on("beforehide", onBeforeHide);
1194 menu.on("hide", onHide);
1195 menu.on("beforeshow", onBeforeShow);
1196 menu.on("show", onShow);
1198 if(g && menu.events["checkchange"]){
1202 groups[g].push(menu);
1203 menu.on("checkchange", onCheck);
1208 * Returns a {@link Roo.menu.Menu} object
1209 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210 * be used to generate and return a new Menu instance.
1212 get : function(menu){
1213 if(typeof menu == "string"){ // menu id
1215 }else if(menu.events){ // menu instance
1218 /*else if(typeof menu.length == 'number'){ // array of menu items?
1219 return new Roo.bootstrap.Menu({items:menu});
1220 }else{ // otherwise, must be a config
1221 return new Roo.bootstrap.Menu(menu);
1228 unregister : function(menu){
1229 delete menus[menu.id];
1230 menu.un("beforehide", onBeforeHide);
1231 menu.un("hide", onHide);
1232 menu.un("beforeshow", onBeforeShow);
1233 menu.un("show", onShow);
1235 if(g && menu.events["checkchange"]){
1236 groups[g].remove(menu);
1237 menu.un("checkchange", onCheck);
1242 registerCheckable : function(menuItem){
1243 var g = menuItem.group;
1248 groups[g].push(menuItem);
1249 menuItem.on("beforecheckchange", onBeforeCheck);
1254 unregisterCheckable : function(menuItem){
1255 var g = menuItem.group;
1257 groups[g].remove(menuItem);
1258 menuItem.un("beforecheckchange", onBeforeCheck);
1270 * @class Roo.bootstrap.Menu
1271 * @extends Roo.bootstrap.Component
1272 * Bootstrap Menu class - container for MenuItems
1273 * @cfg {String} type type of menu
1277 * @param {Object} config The config object
1281 Roo.bootstrap.Menu = function(config){
1282 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283 if (this.registerMenu) {
1284 Roo.bootstrap.MenuMgr.register(this);
1289 * Fires before this menu is displayed
1290 * @param {Roo.menu.Menu} this
1295 * Fires before this menu is hidden
1296 * @param {Roo.menu.Menu} this
1301 * Fires after this menu is displayed
1302 * @param {Roo.menu.Menu} this
1307 * Fires after this menu is hidden
1308 * @param {Roo.menu.Menu} this
1313 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314 * @param {Roo.menu.Menu} this
1315 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316 * @param {Roo.EventObject} e
1321 * Fires when the mouse is hovering over this menu
1322 * @param {Roo.menu.Menu} this
1323 * @param {Roo.EventObject} e
1324 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1329 * Fires when the mouse exits this menu
1330 * @param {Roo.menu.Menu} this
1331 * @param {Roo.EventObject} e
1332 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1337 * Fires when a menu item contained in this menu is clicked
1338 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339 * @param {Roo.EventObject} e
1343 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1350 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1353 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1355 registerMenu : true,
1357 menuItems :false, // stores the menu items..
1363 getChildContainer : function() {
1367 getAutoCreate : function(){
1369 //if (['right'].indexOf(this.align)!==-1) {
1370 // cfg.cn[1].cls += ' pull-right'
1374 cls : 'dropdown-menu' ,
1375 style : 'z-index:1000'
1379 if (this.type === 'submenu') {
1380 cfg.cls = 'submenu active'
1385 initEvents : function() {
1387 // Roo.log("ADD event");
1388 // Roo.log(this.triggerEl.dom);
1389 this.triggerEl.on('click', this.onTriggerPress, this);
1390 this.triggerEl.addClass('dropdown-toggle');
1391 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1393 this.el.on("mouseover", this.onMouseOver, this);
1394 this.el.on("mouseout", this.onMouseOut, this);
1398 findTargetItem : function(e){
1399 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1403 //Roo.log(t); Roo.log(t.id);
1405 //Roo.log(this.menuitems);
1406 return this.menuitems.get(t.id);
1408 //return this.items.get(t.menuItemId);
1413 onClick : function(e){
1414 Roo.log("menu.onClick");
1415 var t = this.findTargetItem(e);
1421 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1422 if(t == this.activeItem && t.shouldDeactivate(e)){
1423 this.activeItem.deactivate();
1424 delete this.activeItem;
1428 this.setActiveItem(t, true);
1435 Roo.log('pass click event');
1439 this.fireEvent("click", this, t, e);
1443 onMouseOver : function(e){
1444 var t = this.findTargetItem(e);
1447 // if(t.canActivate && !t.disabled){
1448 // this.setActiveItem(t, true);
1452 this.fireEvent("mouseover", this, e, t);
1454 isVisible : function(){
1455 return !this.hidden;
1457 onMouseOut : function(e){
1458 var t = this.findTargetItem(e);
1461 // if(t == this.activeItem && t.shouldDeactivate(e)){
1462 // this.activeItem.deactivate();
1463 // delete this.activeItem;
1466 this.fireEvent("mouseout", this, e, t);
1471 * Displays this menu relative to another element
1472 * @param {String/HTMLElement/Roo.Element} element The element to align to
1473 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474 * the element (defaults to this.defaultAlign)
1475 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1477 show : function(el, pos, parentMenu){
1478 this.parentMenu = parentMenu;
1482 this.fireEvent("beforeshow", this);
1483 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1486 * Displays this menu at a specific xy position
1487 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1490 showAt : function(xy, parentMenu, /* private: */_e){
1491 this.parentMenu = parentMenu;
1496 this.fireEvent("beforeshow", this);
1498 //xy = this.el.adjustForConstraints(xy);
1500 //this.el.setXY(xy);
1502 this.hideMenuItems();
1503 this.hidden = false;
1504 this.triggerEl.addClass('open');
1506 this.fireEvent("show", this);
1512 this.doFocus.defer(50, this);
1516 doFocus : function(){
1518 this.focusEl.focus();
1523 * Hides this menu and optionally all parent menus
1524 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1526 hide : function(deep){
1528 this.hideMenuItems();
1529 if(this.el && this.isVisible()){
1530 this.fireEvent("beforehide", this);
1531 if(this.activeItem){
1532 this.activeItem.deactivate();
1533 this.activeItem = null;
1535 this.triggerEl.removeClass('open');;
1537 this.fireEvent("hide", this);
1539 if(deep === true && this.parentMenu){
1540 this.parentMenu.hide(true);
1544 onTriggerPress : function(e)
1547 Roo.log('trigger press');
1548 //Roo.log(e.getTarget());
1549 // Roo.log(this.triggerEl.dom);
1550 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1553 if (this.isVisible()) {
1557 this.show(this.triggerEl, false, false);
1566 hideMenuItems : function()
1568 //$(backdrop).remove()
1569 Roo.select('.open',true).each(function(aa) {
1571 aa.removeClass('open');
1572 //var parent = getParent($(this))
1573 //var relatedTarget = { relatedTarget: this }
1575 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576 //if (e.isDefaultPrevented()) return
1577 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1580 addxtypeChild : function (tree, cntr) {
1581 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1583 this.menuitems.add(comp);
1604 * @class Roo.bootstrap.MenuItem
1605 * @extends Roo.bootstrap.Component
1606 * Bootstrap MenuItem class
1607 * @cfg {String} html the menu label
1608 * @cfg {String} href the link
1609 * @cfg {Boolean} preventDefault (true | false) default true
1613 * Create a new MenuItem
1614 * @param {Object} config The config object
1618 Roo.bootstrap.MenuItem = function(config){
1619 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1624 * The raw click event for the entire grid.
1625 * @param {Roo.EventObject} e
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1635 preventDefault: true,
1637 getAutoCreate : function(){
1640 cls: 'dropdown-menu-item',
1650 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1655 initEvents: function() {
1657 //this.el.select('a').on('click', this.onClick, this);
1660 onClick : function(e)
1662 Roo.log('item on click ');
1663 //if(this.preventDefault){
1664 // e.preventDefault();
1666 //this.parent().hideMenuItems();
1668 this.fireEvent('click', this, e);
1687 * @class Roo.bootstrap.MenuSeparator
1688 * @extends Roo.bootstrap.Component
1689 * Bootstrap MenuSeparator class
1692 * Create a new MenuItem
1693 * @param {Object} config The config object
1697 Roo.bootstrap.MenuSeparator = function(config){
1698 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1703 getAutoCreate : function(){
1718 <div class="modal fade">
1719 <div class="modal-dialog">
1720 <div class="modal-content">
1721 <div class="modal-header">
1722 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1723 <h4 class="modal-title">Modal title</h4>
1725 <div class="modal-body">
1726 <p>One fine body…</p>
1728 <div class="modal-footer">
1729 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730 <button type="button" class="btn btn-primary">Save changes</button>
1732 </div><!-- /.modal-content -->
1733 </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1744 * @class Roo.bootstrap.Modal
1745 * @extends Roo.bootstrap.Component
1746 * Bootstrap Modal class
1747 * @cfg {String} title Title of dialog
1748 * @cfg {Array} buttons Array of buttons or standard button set..
1751 * Create a new Modal Dialog
1752 * @param {Object} config The config object
1755 Roo.bootstrap.Modal = function(config){
1756 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1761 * The raw btnclick event for the button
1762 * @param {Roo.EventObject} e
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1770 title : 'test dialog',
1774 onRender : function(ct, position)
1776 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1779 var cfg = Roo.apply({}, this.getAutoCreate());
1782 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1784 //if (!cfg.name.length) {
1788 cfg.cls += ' ' + this.cls;
1791 cfg.style = this.style;
1793 this.el = Roo.get(document.body).createChild(cfg, position);
1795 //var type = this.el.dom.type;
1797 if(this.tabIndex !== undefined){
1798 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1803 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804 this.maskEl.enableDisplayMode("block");
1806 //this.el.addClass("x-dlg-modal");
1809 Roo.each(this.buttons, function(bb) {
1810 b = Roo.apply({}, bb);
1811 b.xns = b.xns || Roo.bootstrap;
1812 b.xtype = b.xtype || 'Button';
1813 if (typeof(b.listeners) == 'undefined') {
1814 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1817 var btn = Roo.factory(b);
1819 btn.onRender(this.el.select('.modal-footer').first());
1823 // render the children.
1826 if(typeof(this.items) != 'undefined'){
1827 var items = this.items;
1830 for(var i =0;i < items.length;i++) {
1831 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1835 this.items = nitems;
1837 //this.el.addClass([this.fieldClass, this.cls]);
1840 getAutoCreate : function(){
1845 html : this.html || ''
1853 cls: "modal-dialog",
1856 cls : "modal-content",
1859 cls : 'modal-header',
1868 cls : 'modal-title',
1876 cls : 'modal-footer'
1892 getChildContainer : function() {
1894 return this.el.select('.modal-body',true).first();
1897 getButtonContainer : function() {
1898 return this.el.select('.modal-footer',true).first();
1901 initEvents : function()
1903 this.el.select('.modal-header .close').on('click', this.hide, this);
1905 // this.addxtype(this);
1909 if (!this.rendered) {
1913 this.el.addClass('on');
1914 this.el.removeClass('fade');
1915 this.el.setStyle('display', 'block');
1916 Roo.get(document.body).addClass("x-body-masked");
1917 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1919 this.el.setStyle('zIndex', '10001');
1920 this.fireEvent('show', this);
1926 Roo.log('Modal hide?!');
1928 Roo.get(document.body).removeClass("x-body-masked");
1929 this.el.removeClass('on');
1930 this.el.addClass('fade');
1931 this.el.setStyle('display', 'none');
1932 this.fireEvent('hide', this);
1934 onButtonClick: function(btn,e)
1937 this.fireEvent('btnclick', btn.name, e);
1942 Roo.apply(Roo.bootstrap.Modal, {
1944 * Button config that displays a single OK button
1953 * Button config that displays Yes and No buttons
1969 * Button config that displays OK and Cancel buttons
1984 * Button config that displays Yes, No and Cancel buttons
2011 * @class Roo.bootstrap.Navbar
2012 * @extends Roo.bootstrap.Component
2013 * Bootstrap Navbar class
2014 * @cfg {Boolean} sidebar has side bar
2015 * @cfg {Boolean} bar is a bar?
2016 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017 * @cfg {String} brand what is brand
2018 * @cfg {Boolean} inverse is inverted color
2019 * @cfg {String} type (nav | pills | tabs)
2020 * @cfg {Boolean} arrangement stacked | justified
2021 * @cfg {String} align (left | right) alignment
2022 * @cfg {String} brand_href href of the brand
2023 * @cfg {Boolean} main (true|false) main nav bar? default false
2024 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2028 * Create a new Navbar
2029 * @param {Object} config The config object
2033 Roo.bootstrap.Navbar = function(config){
2034 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2052 getAutoCreate : function(){
2057 if (this.sidebar === true) {
2065 if (this.bar === true) {
2073 cls: 'navbar-header',
2078 cls: 'navbar-toggle',
2079 'data-toggle': 'collapse',
2084 html: 'Toggle navigation'
2104 cls: 'collapse navbar-collapse'
2109 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2111 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112 cfg.cls += ' navbar-' + this.position;
2113 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2116 if (this.brand !== '') {
2119 href: this.brand_href ? this.brand_href : '#',
2120 cls: 'navbar-brand',
2128 cfg.cls += ' main-nav';
2134 } else if (this.bar === false) {
2137 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2147 if (['tabs','pills'].indexOf(this.type)!==-1) {
2148 cfg.cn[0].cls += ' nav-' + this.type
2150 if (this.type!=='nav') {
2151 Roo.log('nav type must be nav/tabs/pills')
2153 cfg.cn[0].cls += ' navbar-nav'
2156 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157 cfg.cn[0].cls += ' nav-' + this.arrangement;
2160 if (this.align === 'right') {
2161 cfg.cn[0].cls += ' navbar-right';
2164 cfg.cls += ' navbar-inverse';
2172 initEvents :function ()
2174 //Roo.log(this.el.select('.navbar-toggle',true));
2175 this.el.select('.navbar-toggle',true).on('click', function() {
2176 // Roo.log('click');
2177 this.el.select('.navbar-collapse',true).toggleClass('in');
2185 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2187 var size = this.el.getSize();
2188 this.maskEl.setSize(size.width, size.height);
2189 this.maskEl.enableDisplayMode("block");
2198 getChildContainer : function()
2200 if (this.bar === true) {
2201 return this.el.select('.collapse',true).first();
2229 * @class Roo.bootstrap.NavGroup
2230 * @extends Roo.bootstrap.Component
2231 * Bootstrap NavGroup class
2232 * @cfg {String} align left | right
2233 * @cfg {Boolean} inverse false | true
2234 * @cfg {String} type (nav|pills|tab) default nav
2237 * Create a new nav group
2238 * @param {Object} config The config object
2241 Roo.bootstrap.NavGroup = function(config){
2242 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2252 getAutoCreate : function(){
2253 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2260 if (['tabs','pills'].indexOf(this.type)!==-1) {
2261 cfg.cls += ' nav-' + this.type
2263 if (this.type!=='nav') {
2264 Roo.log('nav type must be nav/tabs/pills')
2266 cfg.cls += ' navbar-nav'
2269 if (this.parent().sidebar === true) {
2272 cls: 'dashboard-menu'
2278 if (this.form === true) {
2284 if (this.align === 'right') {
2285 cfg.cls += ' navbar-right';
2287 cfg.cls += ' navbar-left';
2291 if (this.align === 'right') {
2292 cfg.cls += ' navbar-right';
2296 cfg.cls += ' navbar-inverse';
2316 * @class Roo.bootstrap.Navbar.Item
2317 * @extends Roo.bootstrap.Component
2318 * Bootstrap Navbar.Button class
2319 * @cfg {String} href link to
2320 * @cfg {String} html content of button
2321 * @cfg {String} badge text inside badge
2322 * @cfg {String} glyphicon name of glyphicon
2323 * @cfg {String} icon name of font awesome icon
2324 * @cfg {Boolena} active Is item active
2325 * @cfg {Boolean} preventDefault (true | false) default false
2328 * Create a new Navbar Button
2329 * @param {Object} config The config object
2331 Roo.bootstrap.Navbar.Item = function(config){
2332 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2337 * The raw click event for the entire grid.
2338 * @param {Roo.EventObject} e
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
2353 preventDefault : false,
2355 getAutoCreate : function(){
2357 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2359 if (this.parent().parent().sidebar === true) {
2372 cfg.cn[0].html = this.html;
2376 this.cls += ' active';
2380 cfg.cn[0].cls += ' dropdown-toggle';
2381 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2385 cfg.cn[0].tag = 'a',
2386 cfg.cn[0].href = this.href;
2389 if (this.glyphicon) {
2390 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2394 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2406 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2416 if (this.glyphicon) {
2417 if(cfg.html){cfg.html = ' ' + this.html};
2421 cls: 'glyphicon glyphicon-' + this.glyphicon
2426 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2431 cfg.cn[0].html += " <span class='caret'></span>";
2432 //}else if (!this.href) {
2433 // cfg.cn[0].tag='p';
2434 // cfg.cn[0].cls='navbar-text';
2437 cfg.cn[0].href=this.href||'#';
2438 cfg.cn[0].html=this.html;
2441 if (this.badge !== '') {
2444 cfg.cn[0].html + ' ',
2455 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2460 initEvents: function() {
2461 // Roo.log('init events?');
2462 // Roo.log(this.el.dom);
2463 this.el.select('a',true).on('click', this.onClick, this);
2466 onClick : function(e)
2468 if(this.preventDefault){
2472 if(this.fireEvent('click', this, e) === false){
2476 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477 this.onTabsClick(e);
2481 onTabsClick : function(e)
2483 Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484 v.removeClass('active');
2487 this.el.addClass('active');
2489 if(this.href && this.href.substring(0,1) == '#'){
2490 var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2492 Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493 v.removeClass('active');
2496 tab.addClass('active');
2511 * @class Roo.bootstrap.Row
2512 * @extends Roo.bootstrap.Component
2513 * Bootstrap Row class (contains columns...)
2517 * @param {Object} config The config object
2520 Roo.bootstrap.Row = function(config){
2521 Roo.bootstrap.Row.superclass.constructor.call(this, config);
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
2526 getAutoCreate : function(){
2545 * @class Roo.bootstrap.Element
2546 * @extends Roo.bootstrap.Component
2547 * Bootstrap Element class
2548 * @cfg {String} html contents of the element
2549 * @cfg {String} tag tag of the element
2550 * @cfg {String} cls class of the element
2553 * Create a new Element
2554 * @param {Object} config The config object
2557 Roo.bootstrap.Element = function(config){
2558 Roo.bootstrap.Element.superclass.constructor.call(this, config);
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
2568 getAutoCreate : function(){
2593 * @class Roo.bootstrap.Pagination
2594 * @extends Roo.bootstrap.Component
2595 * Bootstrap Pagination class
2596 * @cfg {String} size xs | sm | md | lg
2597 * @cfg {Boolean} inverse false | true
2600 * Create a new Pagination
2601 * @param {Object} config The config object
2604 Roo.bootstrap.Pagination = function(config){
2605 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
2614 getAutoCreate : function(){
2620 cfg.cls += ' inverse';
2626 cfg.cls += " " + this.cls;
2644 * @class Roo.bootstrap.PaginationItem
2645 * @extends Roo.bootstrap.Component
2646 * Bootstrap PaginationItem class
2647 * @cfg {String} html text
2648 * @cfg {String} href the link
2649 * @cfg {Boolean} preventDefault (true | false) default true
2650 * @cfg {Boolean} active (true | false) default false
2654 * Create a new PaginationItem
2655 * @param {Object} config The config object
2659 Roo.bootstrap.PaginationItem = function(config){
2660 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2665 * The raw click event for the entire grid.
2666 * @param {Roo.EventObject} e
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
2676 preventDefault: true,
2680 getAutoCreate : function(){
2686 href : this.href ? this.href : '#',
2687 html : this.html ? this.html : ''
2697 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2703 initEvents: function() {
2705 this.el.on('click', this.onClick, this);
2708 onClick : function(e)
2710 Roo.log('PaginationItem on click ');
2711 if(this.preventDefault){
2715 this.fireEvent('click', this, e);
2731 * @class Roo.bootstrap.Slider
2732 * @extends Roo.bootstrap.Component
2733 * Bootstrap Slider class
2736 * Create a new Slider
2737 * @param {Object} config The config object
2740 Roo.bootstrap.Slider = function(config){
2741 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
2746 getAutoCreate : function(){
2750 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2754 cls: 'ui-slider-handle ui-state-default ui-corner-all'
2772 * @class Roo.bootstrap.Table
2773 * @extends Roo.bootstrap.Component
2774 * Bootstrap Table class
2775 * @cfg {String} cls table class
2776 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777 * @cfg {String} bgcolor Specifies the background color for a table
2778 * @cfg {Number} border Specifies whether the table cells should have borders or not
2779 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780 * @cfg {Number} cellspacing Specifies the space between cells
2781 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783 * @cfg {String} sortable Specifies that the table should be sortable
2784 * @cfg {String} summary Specifies a summary of the content of a table
2785 * @cfg {Number} width Specifies the width of a table
2787 * @cfg {boolean} striped Should the rows be alternative striped
2788 * @cfg {boolean} bordered Add borders to the table
2789 * @cfg {boolean} hover Add hover highlighting
2790 * @cfg {boolean} condensed Format condensed
2791 * @cfg {boolean} responsive Format condensed
2797 * Create a new Table
2798 * @param {Object} config The config object
2801 Roo.bootstrap.Table = function(config){
2802 Roo.bootstrap.Table.superclass.constructor.call(this, config);
2805 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806 this.sm = this.selModel;
2807 this.sm.xmodule = this.xmodule || false;
2809 if (this.cm && typeof(this.cm.config) == 'undefined') {
2810 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811 this.cm = this.colModel;
2812 this.cm.xmodule = this.xmodule || false;
2815 this.store= Roo.factory(this.store, Roo.data);
2816 this.ds = this.store;
2817 this.ds.xmodule = this.xmodule || false;
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
2844 getAutoCreate : function(){
2845 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2854 cfg.cls += ' table-striped';
2857 cfg.cls += ' table-hover';
2859 if (this.bordered) {
2860 cfg.cls += ' table-bordered';
2862 if (this.condensed) {
2863 cfg.cls += ' table-condensed';
2865 if (this.responsive) {
2866 cfg.cls += ' table-responsive';
2873 cfg.cls+= ' ' +this.cls;
2876 // this lot should be simplifed...
2879 cfg.align=this.align;
2882 cfg.bgcolor=this.bgcolor;
2885 cfg.border=this.border;
2887 if (this.cellpadding) {
2888 cfg.cellpadding=this.cellpadding;
2890 if (this.cellspacing) {
2891 cfg.cellspacing=this.cellspacing;
2894 cfg.frame=this.frame;
2897 cfg.rules=this.rules;
2899 if (this.sortable) {
2900 cfg.sortable=this.sortable;
2903 cfg.summary=this.summary;
2906 cfg.width=this.width;
2909 if(this.store || this.cm){
2910 cfg.cn.push(this.renderHeader());
2911 cfg.cn.push(this.renderBody());
2912 cfg.cn.push(this.renderFooter());
2914 cfg.cls+= ' TableGrid';
2920 // initTableGrid : function()
2929 // var cm = this.cm;
2931 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2934 // html: cm.getColumnHeader(i)
2938 // cfg.push(header);
2945 initEvents : function()
2947 if(!this.store || !this.cm){
2951 Roo.log('initEvents with ds!!!!');
2953 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2954 // this.maskEl.enableDisplayMode("block");
2955 // this.maskEl.show();
2957 this.store.on('load', this.onLoad, this);
2958 this.store.on('beforeload', this.onBeforeLoad, this);
2966 renderHeader : function()
2975 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2978 html: cm.getColumnHeader(i)
2985 renderBody : function()
2995 renderFooter : function()
3007 Roo.log('ds onload');
3011 var tbody = this.el.select('tbody', true).first();
3015 if(this.store.getCount() > 0){
3016 this.store.data.each(function(d){
3022 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3023 var renderer = cm.getRenderer(i);
3024 Roo.log('config!!!!!!!!!!!!!!!!!!!');
3025 Roo.log(cm.config(i));
3029 if(typeof(renderer) !== 'undefined'){
3030 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3033 if(typeof(value) === 'object'){
3043 html: (typeof(value) === 'object') ? '' : value
3048 tbody.createChild(row);
3056 Roo.each(renders, function(r){
3057 _this.renderColumn(r);
3061 // if(this.loadMask){
3062 // this.maskEl.hide();
3066 onBeforeLoad : function()
3068 Roo.log('ds onBeforeLoad');
3072 // if(this.loadMask){
3073 // this.maskEl.show();
3079 this.el.select('tbody', true).first().dom.innerHTML = '';
3082 getSelectionModel : function(){
3084 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3086 return this.selModel;
3089 renderColumn : function(r)
3092 r.cfg.render(Roo.get(r.id));
3095 Roo.each(r.cfg.cn, function(c){
3100 _this.renderColumn(child);
3117 * @class Roo.bootstrap.TableCell
3118 * @extends Roo.bootstrap.Component
3119 * Bootstrap TableCell class
3120 * @cfg {String} html cell contain text
3121 * @cfg {String} cls cell class
3122 * @cfg {String} tag cell tag (td|th) default td
3123 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3124 * @cfg {String} align Aligns the content in a cell
3125 * @cfg {String} axis Categorizes cells
3126 * @cfg {String} bgcolor Specifies the background color of a cell
3127 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3128 * @cfg {Number} colspan Specifies the number of columns a cell should span
3129 * @cfg {String} headers Specifies one or more header cells a cell is related to
3130 * @cfg {Number} height Sets the height of a cell
3131 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3132 * @cfg {Number} rowspan Sets the number of rows a cell should span
3133 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3134 * @cfg {String} valign Vertical aligns the content in a cell
3135 * @cfg {Number} width Specifies the width of a cell
3138 * Create a new TableCell
3139 * @param {Object} config The config object
3142 Roo.bootstrap.TableCell = function(config){
3143 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3146 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3166 getAutoCreate : function(){
3167 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3187 cfg.align=this.align
3193 cfg.bgcolor=this.bgcolor
3196 cfg.charoff=this.charoff
3199 cfg.colspan=this.colspan
3202 cfg.headers=this.headers
3205 cfg.height=this.height
3208 cfg.nowrap=this.nowrap
3211 cfg.rowspan=this.rowspan
3214 cfg.scope=this.scope
3217 cfg.valign=this.valign
3220 cfg.width=this.width
3239 * @class Roo.bootstrap.TableRow
3240 * @extends Roo.bootstrap.Component
3241 * Bootstrap TableRow class
3242 * @cfg {String} cls row class
3243 * @cfg {String} align Aligns the content in a table row
3244 * @cfg {String} bgcolor Specifies a background color for a table row
3245 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3246 * @cfg {String} valign Vertical aligns the content in a table row
3249 * Create a new TableRow
3250 * @param {Object} config The config object
3253 Roo.bootstrap.TableRow = function(config){
3254 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3257 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3265 getAutoCreate : function(){
3266 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3276 cfg.align = this.align;
3279 cfg.bgcolor = this.bgcolor;
3282 cfg.charoff = this.charoff;
3285 cfg.valign = this.valign;
3303 * @class Roo.bootstrap.TableBody
3304 * @extends Roo.bootstrap.Component
3305 * Bootstrap TableBody class
3306 * @cfg {String} cls element class
3307 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3308 * @cfg {String} align Aligns the content inside the element
3309 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3310 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3313 * Create a new TableBody
3314 * @param {Object} config The config object
3317 Roo.bootstrap.TableBody = function(config){
3318 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3321 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3329 getAutoCreate : function(){
3330 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3344 cfg.align = this.align;
3347 cfg.charoff = this.charoff;
3350 cfg.valign = this.valign;
3357 // initEvents : function()
3364 // this.store = Roo.factory(this.store, Roo.data);
3365 // this.store.on('load', this.onLoad, this);
3367 // this.store.load();
3371 // onLoad: function ()
3373 // this.fireEvent('load', this);
3383 * Ext JS Library 1.1.1
3384 * Copyright(c) 2006-2007, Ext JS, LLC.
3386 * Originally Released Under LGPL - original licence link has changed is not relivant.
3389 * <script type="text/javascript">
3392 // as we use this in bootstrap.
3393 Roo.namespace('Roo.form');
3395 * @class Roo.form.Action
3396 * Internal Class used to handle form actions
3398 * @param {Roo.form.BasicForm} el The form element or its id
3399 * @param {Object} config Configuration options
3404 // define the action interface
3405 Roo.form.Action = function(form, options){
3407 this.options = options || {};
3410 * Client Validation Failed
3413 Roo.form.Action.CLIENT_INVALID = 'client';
3415 * Server Validation Failed
3418 Roo.form.Action.SERVER_INVALID = 'server';
3420 * Connect to Server Failed
3423 Roo.form.Action.CONNECT_FAILURE = 'connect';
3425 * Reading Data from Server Failed
3428 Roo.form.Action.LOAD_FAILURE = 'load';
3430 Roo.form.Action.prototype = {
3432 failureType : undefined,
3433 response : undefined,
3437 run : function(options){
3442 success : function(response){
3447 handleResponse : function(response){
3451 // default connection failure
3452 failure : function(response){
3454 this.response = response;
3455 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3456 this.form.afterAction(this, false);
3459 processResponse : function(response){
3460 this.response = response;
3461 if(!response.responseText){
3464 this.result = this.handleResponse(response);
3468 // utility functions used internally
3469 getUrl : function(appendParams){
3470 var url = this.options.url || this.form.url || this.form.el.dom.action;
3472 var p = this.getParams();
3474 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3480 getMethod : function(){
3481 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3484 getParams : function(){
3485 var bp = this.form.baseParams;
3486 var p = this.options.params;
3488 if(typeof p == "object"){
3489 p = Roo.urlEncode(Roo.applyIf(p, bp));
3490 }else if(typeof p == 'string' && bp){
3491 p += '&' + Roo.urlEncode(bp);
3494 p = Roo.urlEncode(bp);
3499 createCallback : function(){
3501 success: this.success,
3502 failure: this.failure,
3504 timeout: (this.form.timeout*1000),
3505 upload: this.form.fileUpload ? this.success : undefined
3510 Roo.form.Action.Submit = function(form, options){
3511 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3514 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3517 haveProgress : false,
3518 uploadComplete : false,
3520 // uploadProgress indicator.
3521 uploadProgress : function()
3523 if (!this.form.progressUrl) {
3527 if (!this.haveProgress) {
3528 Roo.MessageBox.progress("Uploading", "Uploading");
3530 if (this.uploadComplete) {
3531 Roo.MessageBox.hide();
3535 this.haveProgress = true;
3537 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3539 var c = new Roo.data.Connection();
3541 url : this.form.progressUrl,
3546 success : function(req){
3547 //console.log(data);
3551 rdata = Roo.decode(req.responseText)
3553 Roo.log("Invalid data from server..");
3557 if (!rdata || !rdata.success) {
3559 Roo.MessageBox.alert(Roo.encode(rdata));
3562 var data = rdata.data;
3564 if (this.uploadComplete) {
3565 Roo.MessageBox.hide();
3570 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3571 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3574 this.uploadProgress.defer(2000,this);
3577 failure: function(data) {
3578 Roo.log('progress url failed ');
3589 // run get Values on the form, so it syncs any secondary forms.
3590 this.form.getValues();
3592 var o = this.options;
3593 var method = this.getMethod();
3594 var isPost = method == 'POST';
3595 if(o.clientValidation === false || this.form.isValid()){
3597 if (this.form.progressUrl) {
3598 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3599 (new Date() * 1) + '' + Math.random());
3604 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3605 form:this.form.el.dom,
3606 url:this.getUrl(!isPost),
3608 params:isPost ? this.getParams() : null,
3609 isUpload: this.form.fileUpload
3612 this.uploadProgress();
3614 }else if (o.clientValidation !== false){ // client validation failed
3615 this.failureType = Roo.form.Action.CLIENT_INVALID;
3616 this.form.afterAction(this, false);
3620 success : function(response)
3622 this.uploadComplete= true;
3623 if (this.haveProgress) {
3624 Roo.MessageBox.hide();
3628 var result = this.processResponse(response);
3629 if(result === true || result.success){
3630 this.form.afterAction(this, true);
3634 this.form.markInvalid(result.errors);
3635 this.failureType = Roo.form.Action.SERVER_INVALID;
3637 this.form.afterAction(this, false);
3639 failure : function(response)
3641 this.uploadComplete= true;
3642 if (this.haveProgress) {
3643 Roo.MessageBox.hide();
3646 this.response = response;
3647 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3648 this.form.afterAction(this, false);
3651 handleResponse : function(response){
3652 if(this.form.errorReader){
3653 var rs = this.form.errorReader.read(response);
3656 for(var i = 0, len = rs.records.length; i < len; i++) {
3657 var r = rs.records[i];
3661 if(errors.length < 1){
3665 success : rs.success,
3671 ret = Roo.decode(response.responseText);
3675 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3685 Roo.form.Action.Load = function(form, options){
3686 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3687 this.reader = this.form.reader;
3690 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3695 Roo.Ajax.request(Roo.apply(
3696 this.createCallback(), {
3697 method:this.getMethod(),
3698 url:this.getUrl(false),
3699 params:this.getParams()
3703 success : function(response){
3705 var result = this.processResponse(response);
3706 if(result === true || !result.success || !result.data){
3707 this.failureType = Roo.form.Action.LOAD_FAILURE;
3708 this.form.afterAction(this, false);
3711 this.form.clearInvalid();
3712 this.form.setValues(result.data);
3713 this.form.afterAction(this, true);
3716 handleResponse : function(response){
3717 if(this.form.reader){
3718 var rs = this.form.reader.read(response);
3719 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3721 success : rs.success,
3725 return Roo.decode(response.responseText);
3729 Roo.form.Action.ACTION_TYPES = {
3730 'load' : Roo.form.Action.Load,
3731 'submit' : Roo.form.Action.Submit
3740 * @class Roo.bootstrap.Form
3741 * @extends Roo.bootstrap.Component
3742 * Bootstrap Form class
3743 * @cfg {String} method GET | POST (default POST)
3744 * @cfg {String} labelAlign top | left (default top)
3745 * @cfg {String} align left | right - for navbars
3750 * @param {Object} config The config object
3754 Roo.bootstrap.Form = function(config){
3755 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3758 * @event clientvalidation
3759 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3760 * @param {Form} this
3761 * @param {Boolean} valid true if the form has passed client-side validation
3763 clientvalidation: true,
3765 * @event beforeaction
3766 * Fires before any action is performed. Return false to cancel the action.
3767 * @param {Form} this
3768 * @param {Action} action The action to be performed
3772 * @event actionfailed
3773 * Fires when an action fails.
3774 * @param {Form} this
3775 * @param {Action} action The action that failed
3777 actionfailed : true,
3779 * @event actioncomplete
3780 * Fires when an action is completed.
3781 * @param {Form} this
3782 * @param {Action} action The action that completed
3784 actioncomplete : true
3789 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3792 * @cfg {String} method
3793 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3798 * The URL to use for form actions if one isn't supplied in the action options.
3801 * @cfg {Boolean} fileUpload
3802 * Set to true if this form is a file upload.
3806 * @cfg {Object} baseParams
3807 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3811 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3815 * @cfg {Sting} align (left|right) for navbar forms
3820 activeAction : null,
3823 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3824 * element by passing it or its id or mask the form itself by passing in true.
3827 waitMsgTarget : false,
3832 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3833 * element by passing it or its id or mask the form itself by passing in true.
3837 getAutoCreate : function(){
3841 method : this.method || 'POST',
3842 id : this.id || Roo.id(),
3845 if (this.parent().xtype.match(/^Nav/)) {
3846 cfg.cls = 'navbar-form navbar-' + this.align;
3850 if (this.labelAlign == 'left' ) {
3851 cfg.cls += ' form-horizontal';
3857 initEvents : function()
3859 this.el.on('submit', this.onSubmit, this);
3864 onSubmit : function(e){
3869 * Returns true if client-side validation on the form is successful.
3872 isValid : function(){
3873 var items = this.getItems();
3875 items.each(function(f){
3884 * Returns true if any fields in this form have changed since their original load.
3887 isDirty : function(){
3889 var items = this.getItems();
3890 items.each(function(f){
3900 * Performs a predefined action (submit or load) or custom actions you define on this form.
3901 * @param {String} actionName The name of the action type
3902 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3903 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3904 * accept other config options):
3906 Property Type Description
3907 ---------------- --------------- ----------------------------------------------------------------------------------
3908 url String The url for the action (defaults to the form's url)
3909 method String The form method to use (defaults to the form's method, or POST if not defined)
3910 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3911 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3912 validate the form on the client (defaults to false)
3914 * @return {BasicForm} this
3916 doAction : function(action, options){
3917 if(typeof action == 'string'){
3918 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3920 if(this.fireEvent('beforeaction', this, action) !== false){
3921 this.beforeAction(action);
3922 action.run.defer(100, action);
3928 beforeAction : function(action){
3929 var o = action.options;
3931 // not really supported yet.. ??
3933 //if(this.waitMsgTarget === true){
3934 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3935 //}else if(this.waitMsgTarget){
3936 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3937 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3939 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3945 afterAction : function(action, success){
3946 this.activeAction = null;
3947 var o = action.options;
3949 //if(this.waitMsgTarget === true){
3951 //}else if(this.waitMsgTarget){
3952 // this.waitMsgTarget.unmask();
3954 // Roo.MessageBox.updateProgress(1);
3955 // Roo.MessageBox.hide();
3962 Roo.callback(o.success, o.scope, [this, action]);
3963 this.fireEvent('actioncomplete', this, action);
3967 // failure condition..
3968 // we have a scenario where updates need confirming.
3969 // eg. if a locking scenario exists..
3970 // we look for { errors : { needs_confirm : true }} in the response.
3972 (typeof(action.result) != 'undefined') &&
3973 (typeof(action.result.errors) != 'undefined') &&
3974 (typeof(action.result.errors.needs_confirm) != 'undefined')
3977 Roo.log("not supported yet");
3980 Roo.MessageBox.confirm(
3981 "Change requires confirmation",
3982 action.result.errorMsg,
3987 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
3997 Roo.callback(o.failure, o.scope, [this, action]);
3998 // show an error message if no failed handler is set..
3999 if (!this.hasListener('actionfailed')) {
4000 Roo.log("need to add dialog support");
4002 Roo.MessageBox.alert("Error",
4003 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4004 action.result.errorMsg :
4005 "Saving Failed, please check your entries or try again"
4010 this.fireEvent('actionfailed', this, action);
4015 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4016 * @param {String} id The value to search for
4019 findField : function(id){
4020 var items = this.getItems();
4021 var field = items.get(id);
4023 items.each(function(f){
4024 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4031 return field || null;
4034 * Mark fields in this form invalid in bulk.
4035 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4036 * @return {BasicForm} this
4038 markInvalid : function(errors){
4039 if(errors instanceof Array){
4040 for(var i = 0, len = errors.length; i < len; i++){
4041 var fieldError = errors[i];
4042 var f = this.findField(fieldError.id);
4044 f.markInvalid(fieldError.msg);
4050 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4051 field.markInvalid(errors[id]);
4055 //Roo.each(this.childForms || [], function (f) {
4056 // f.markInvalid(errors);
4063 * Set values for fields in this form in bulk.
4064 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4065 * @return {BasicForm} this
4067 setValues : function(values){
4068 if(values instanceof Array){ // array of objects
4069 for(var i = 0, len = values.length; i < len; i++){
4071 var f = this.findField(v.id);
4073 f.setValue(v.value);
4074 if(this.trackResetOnLoad){
4075 f.originalValue = f.getValue();
4079 }else{ // object hash
4082 if(typeof values[id] != 'function' && (field = this.findField(id))){
4084 if (field.setFromData &&
4086 field.displayField &&
4087 // combos' with local stores can
4088 // be queried via setValue()
4089 // to set their value..
4090 (field.store && !field.store.isLocal)
4094 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4095 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4096 field.setFromData(sd);
4099 field.setValue(values[id]);
4103 if(this.trackResetOnLoad){
4104 field.originalValue = field.getValue();
4110 //Roo.each(this.childForms || [], function (f) {
4111 // f.setValues(values);
4118 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4119 * they are returned as an array.
4120 * @param {Boolean} asString
4123 getValues : function(asString){
4124 //if (this.childForms) {
4125 // copy values from the child forms
4126 // Roo.each(this.childForms, function (f) {
4127 // this.setValues(f.getValues());
4133 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4134 if(asString === true){
4137 return Roo.urlDecode(fs);
4141 * Returns the fields in this form as an object with key/value pairs.
4142 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4145 getFieldValues : function(with_hidden)
4147 var items = this.getItems();
4149 items.each(function(f){
4153 var v = f.getValue();
4154 if (f.inputType =='radio') {
4155 if (typeof(ret[f.getName()]) == 'undefined') {
4156 ret[f.getName()] = ''; // empty..
4159 if (!f.el.dom.checked) {
4167 // not sure if this supported any more..
4168 if ((typeof(v) == 'object') && f.getRawValue) {
4169 v = f.getRawValue() ; // dates..
4171 // combo boxes where name != hiddenName...
4172 if (f.name != f.getName()) {
4173 ret[f.name] = f.getRawValue();
4175 ret[f.getName()] = v;
4182 * Clears all invalid messages in this form.
4183 * @return {BasicForm} this
4185 clearInvalid : function(){
4186 var items = this.getItems();
4188 items.each(function(f){
4199 * @return {BasicForm} this
4202 var items = this.getItems();
4203 items.each(function(f){
4207 Roo.each(this.childForms || [], function (f) {
4214 getItems : function()
4216 var r=new Roo.util.MixedCollection(false, function(o){
4217 return o.id || (o.id = Roo.id());
4219 var iter = function(el) {
4226 Roo.each(el.items,function(e) {
4245 * Ext JS Library 1.1.1
4246 * Copyright(c) 2006-2007, Ext JS, LLC.
4248 * Originally Released Under LGPL - original licence link has changed is not relivant.
4251 * <script type="text/javascript">
4254 * @class Roo.form.VTypes
4255 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4258 Roo.form.VTypes = function(){
4259 // closure these in so they are only created once.
4260 var alpha = /^[a-zA-Z_]+$/;
4261 var alphanum = /^[a-zA-Z0-9_]+$/;
4262 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4263 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4265 // All these messages and functions are configurable
4268 * The function used to validate email addresses
4269 * @param {String} value The email address
4271 'email' : function(v){
4272 return email.test(v);
4275 * The error text to display when the email validation function returns false
4278 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4280 * The keystroke filter mask to be applied on email input
4283 'emailMask' : /[a-z0-9_\.\-@]/i,
4286 * The function used to validate URLs
4287 * @param {String} value The URL
4289 'url' : function(v){
4293 * The error text to display when the url validation function returns false
4296 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4299 * The function used to validate alpha values
4300 * @param {String} value The value
4302 'alpha' : function(v){
4303 return alpha.test(v);
4306 * The error text to display when the alpha validation function returns false
4309 'alphaText' : 'This field should only contain letters and _',
4311 * The keystroke filter mask to be applied on alpha input
4314 'alphaMask' : /[a-z_]/i,
4317 * The function used to validate alphanumeric values
4318 * @param {String} value The value
4320 'alphanum' : function(v){
4321 return alphanum.test(v);
4324 * The error text to display when the alphanumeric validation function returns false
4327 'alphanumText' : 'This field should only contain letters, numbers and _',
4329 * The keystroke filter mask to be applied on alphanumeric input
4332 'alphanumMask' : /[a-z0-9_]/i
4342 * @class Roo.bootstrap.Input
4343 * @extends Roo.bootstrap.Component
4344 * Bootstrap Input class
4345 * @cfg {Boolean} disabled is it disabled
4346 * @cfg {String} fieldLabel - the label associated
4347 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4348 * @cfg {String} name name of the input
4349 * @cfg {string} fieldLabel - the label associated
4350 * @cfg {string} inputType - input / file submit ...
4351 * @cfg {string} placeholder - placeholder to put in text.
4352 * @cfg {string} before - input group add on before
4353 * @cfg {string} after - input group add on after
4354 * @cfg {string} size - (lg|sm) or leave empty..
4355 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4356 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4357 * @cfg {Number} md colspan out of 12 for computer-sized screens
4358 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4359 * @cfg {string} value default value of the input
4360 * @cfg {Number} labelWidth set the width of label (0-12)
4361 * @cfg {String} labelAlign (top|left)
4362 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4366 * Create a new Input
4367 * @param {Object} config The config object
4370 Roo.bootstrap.Input = function(config){
4371 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4376 * Fires when this field receives input focus.
4377 * @param {Roo.form.Field} this
4382 * Fires when this field loses input focus.
4383 * @param {Roo.form.Field} this
4388 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4389 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4390 * @param {Roo.form.Field} this
4391 * @param {Roo.EventObject} e The event object
4396 * Fires just before the field blurs if the field value has changed.
4397 * @param {Roo.form.Field} this
4398 * @param {Mixed} newValue The new value
4399 * @param {Mixed} oldValue The original value
4404 * Fires after the field has been marked as invalid.
4405 * @param {Roo.form.Field} this
4406 * @param {String} msg The validation message
4411 * Fires after the field has been validated with no errors.
4412 * @param {Roo.form.Field} this
4417 * Fires after the key up
4418 * @param {Roo.form.Field} this
4419 * @param {Roo.EventObject} e The event Object
4425 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4427 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4428 automatic validation (defaults to "keyup").
4430 validationEvent : "keyup",
4432 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4434 validateOnBlur : true,
4436 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4438 validationDelay : 250,
4440 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4442 focusClass : "x-form-focus", // not needed???
4446 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4448 invalidClass : "has-error",
4451 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4453 selectOnFocus : false,
4456 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4460 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4465 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4467 disableKeyFilter : false,
4470 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4474 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4478 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4480 blankText : "This field is required",
4483 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4487 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4489 maxLength : Number.MAX_VALUE,
4491 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4493 minLengthText : "The minimum length for this field is {0}",
4495 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4497 maxLengthText : "The maximum length for this field is {0}",
4501 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4502 * If available, this function will be called only after the basic validators all return true, and will be passed the
4503 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4507 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4508 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4509 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4513 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4536 parentLabelAlign : function()
4539 while (parent.parent()) {
4540 parent = parent.parent();
4541 if (typeof(parent.labelAlign) !='undefined') {
4542 return parent.labelAlign;
4549 getAutoCreate : function(){
4551 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4557 if(this.inputType != 'hidden'){
4558 cfg.cls = 'form-group' //input-group
4564 type : this.inputType,
4566 cls : 'form-control',
4567 placeholder : this.placeholder || ''
4571 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4572 input.maxLength = this.maxLength;
4575 if (this.disabled) {
4576 input.disabled=true;
4579 if (this.readOnly) {
4580 input.readonly=true;
4584 input.name = this.name;
4587 input.cls += ' input-' + this.size;
4590 ['xs','sm','md','lg'].map(function(size){
4591 if (settings[size]) {
4592 cfg.cls += ' col-' + size + '-' + settings[size];
4596 var inputblock = input;
4598 if (this.before || this.after) {
4601 cls : 'input-group',
4605 inputblock.cn.push({
4607 cls : 'input-group-addon',
4611 inputblock.cn.push(input);
4613 inputblock.cn.push({
4615 cls : 'input-group-addon',
4622 if (align ==='left' && this.fieldLabel.length) {
4623 Roo.log("left and has label");
4629 cls : 'control-label col-sm-' + this.labelWidth,
4630 html : this.fieldLabel
4634 cls : "col-sm-" + (12 - this.labelWidth),
4641 } else if ( this.fieldLabel.length) {
4647 //cls : 'input-group-addon',
4648 html : this.fieldLabel
4658 Roo.log(" no label && no align");
4667 Roo.log('input-parentType: ' + this.parentType);
4669 if (this.parentType === 'Navbar' && this.parent().bar) {
4670 cfg.cls += ' navbar-form';
4678 * return the real input element.
4680 inputEl: function ()
4682 return this.el.select('input.form-control',true).first();
4684 setDisabled : function(v)
4686 var i = this.inputEl().dom;
4688 i.removeAttribute('disabled');
4692 i.setAttribute('disabled','true');
4694 initEvents : function()
4697 this.inputEl().on("keydown" , this.fireKey, this);
4698 this.inputEl().on("focus", this.onFocus, this);
4699 this.inputEl().on("blur", this.onBlur, this);
4701 this.inputEl().relayEvent('keyup', this);
4703 // reference to original value for reset
4704 this.originalValue = this.getValue();
4705 //Roo.form.TextField.superclass.initEvents.call(this);
4706 if(this.validationEvent == 'keyup'){
4707 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4708 this.inputEl().on('keyup', this.filterValidation, this);
4710 else if(this.validationEvent !== false){
4711 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4714 if(this.selectOnFocus){
4715 this.on("focus", this.preFocus, this);
4718 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4719 this.inputEl().on("keypress", this.filterKeys, this);
4722 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4723 this.el.on("click", this.autoSize, this);
4726 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4727 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4731 filterValidation : function(e){
4732 if(!e.isNavKeyPress()){
4733 this.validationTask.delay(this.validationDelay);
4737 * Validates the field value
4738 * @return {Boolean} True if the value is valid, else false
4740 validate : function(){
4741 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4742 if(this.disabled || this.validateValue(this.getRawValue())){
4743 this.clearInvalid();
4751 * Validates a value according to the field's validation rules and marks the field as invalid
4752 * if the validation fails
4753 * @param {Mixed} value The value to validate
4754 * @return {Boolean} True if the value is valid, else false
4756 validateValue : function(value){
4757 if(value.length < 1) { // if it's blank
4758 if(this.allowBlank){
4759 this.clearInvalid();
4762 this.markInvalid(this.blankText);
4766 if(value.length < this.minLength){
4767 this.markInvalid(String.format(this.minLengthText, this.minLength));
4770 if(value.length > this.maxLength){
4771 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4775 var vt = Roo.form.VTypes;
4776 if(!vt[this.vtype](value, this)){
4777 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4781 if(typeof this.validator == "function"){
4782 var msg = this.validator(value);
4784 this.markInvalid(msg);
4788 if(this.regex && !this.regex.test(value)){
4789 this.markInvalid(this.regexText);
4798 fireKey : function(e){
4799 //Roo.log('field ' + e.getKey());
4800 if(e.isNavKeyPress()){
4801 this.fireEvent("specialkey", this, e);
4804 focus : function (selectText){
4806 this.inputEl().focus();
4807 if(selectText === true){
4808 this.inputEl().dom.select();
4814 onFocus : function(){
4815 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4816 // this.el.addClass(this.focusClass);
4819 this.hasFocus = true;
4820 this.startValue = this.getValue();
4821 this.fireEvent("focus", this);
4825 beforeBlur : Roo.emptyFn,
4829 onBlur : function(){
4831 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4832 //this.el.removeClass(this.focusClass);
4834 this.hasFocus = false;
4835 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4838 var v = this.getValue();
4839 if(String(v) !== String(this.startValue)){
4840 this.fireEvent('change', this, v, this.startValue);
4842 this.fireEvent("blur", this);
4846 * Resets the current field value to the originally loaded value and clears any validation messages
4849 this.setValue(this.originalValue);
4850 this.clearInvalid();
4853 * Returns the name of the field
4854 * @return {Mixed} name The name field
4856 getName: function(){
4860 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4861 * @return {Mixed} value The field value
4863 getValue : function(){
4864 return this.inputEl().getValue();
4867 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4868 * @return {Mixed} value The field value
4870 getRawValue : function(){
4871 var v = this.inputEl().getValue();
4877 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4878 * @param {Mixed} value The value to set
4880 setRawValue : function(v){
4881 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4884 selectText : function(start, end){
4885 var v = this.getRawValue();
4887 start = start === undefined ? 0 : start;
4888 end = end === undefined ? v.length : end;
4889 var d = this.inputEl().dom;
4890 if(d.setSelectionRange){
4891 d.setSelectionRange(start, end);
4892 }else if(d.createTextRange){
4893 var range = d.createTextRange();
4894 range.moveStart("character", start);
4895 range.moveEnd("character", v.length-end);
4902 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4903 * @param {Mixed} value The value to set
4905 setValue : function(v){
4908 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4914 processValue : function(value){
4915 if(this.stripCharsRe){
4916 var newValue = value.replace(this.stripCharsRe, '');
4917 if(newValue !== value){
4918 this.setRawValue(newValue);
4925 preFocus : function(){
4927 if(this.selectOnFocus){
4928 this.inputEl().dom.select();
4931 filterKeys : function(e){
4933 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4936 var c = e.getCharCode(), cc = String.fromCharCode(c);
4937 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4940 if(!this.maskRe.test(cc)){
4945 * Clear any invalid styles/messages for this field
4947 clearInvalid : function(){
4949 if(!this.el || this.preventMark){ // not rendered
4952 this.el.removeClass(this.invalidClass);
4954 switch(this.msgTarget){
4956 this.el.dom.qtip = '';
4959 this.el.dom.title = '';
4963 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4968 this.errorIcon.dom.qtip = '';
4969 this.errorIcon.hide();
4970 this.un('resize', this.alignErrorIcon, this);
4974 var t = Roo.getDom(this.msgTarget);
4976 t.style.display = 'none';
4980 this.fireEvent('valid', this);
4983 * Mark this field as invalid
4984 * @param {String} msg The validation message
4986 markInvalid : function(msg){
4987 if(!this.el || this.preventMark){ // not rendered
4990 this.el.addClass(this.invalidClass);
4992 msg = msg || this.invalidText;
4993 switch(this.msgTarget){
4995 this.el.dom.qtip = msg;
4996 this.el.dom.qclass = 'x-form-invalid-tip';
4997 if(Roo.QuickTips){ // fix for floating editors interacting with DND
4998 Roo.QuickTips.enable();
5002 this.el.dom.title = msg;
5006 var elp = this.el.findParent('.x-form-element', 5, true);
5007 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5008 this.errorEl.setWidth(elp.getWidth(true)-20);
5010 this.errorEl.update(msg);
5011 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5014 if(!this.errorIcon){
5015 var elp = this.el.findParent('.x-form-element', 5, true);
5016 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5018 this.alignErrorIcon();
5019 this.errorIcon.dom.qtip = msg;
5020 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5021 this.errorIcon.show();
5022 this.on('resize', this.alignErrorIcon, this);
5025 var t = Roo.getDom(this.msgTarget);
5027 t.style.display = this.msgDisplay;
5031 this.fireEvent('invalid', this, msg);
5034 SafariOnKeyDown : function(event)
5036 // this is a workaround for a password hang bug on chrome/ webkit.
5038 var isSelectAll = false;
5040 if(this.inputEl().dom.selectionEnd > 0){
5041 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5043 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5044 event.preventDefault();
5049 if(isSelectAll){ // backspace and delete key
5051 event.preventDefault();
5052 // this is very hacky as keydown always get's upper case.
5054 var cc = String.fromCharCode(event.getCharCode());
5055 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5059 adjustWidth : function(tag, w){
5060 tag = tag.toLowerCase();
5061 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5062 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5066 if(tag == 'textarea'){
5069 }else if(Roo.isOpera){
5073 if(tag == 'textarea'){
5092 * @class Roo.bootstrap.TextArea
5093 * @extends Roo.bootstrap.Input
5094 * Bootstrap TextArea class
5095 * @cfg {Number} cols Specifies the visible width of a text area
5096 * @cfg {Number} rows Specifies the visible number of lines in a text area
5097 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5098 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5099 * @cfg {string} html text
5102 * Create a new TextArea
5103 * @param {Object} config The config object
5106 Roo.bootstrap.TextArea = function(config){
5107 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5111 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5121 getAutoCreate : function(){
5123 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5134 value : this.value || '',
5135 html: this.html || '',
5136 cls : 'form-control',
5137 placeholder : this.placeholder || ''
5141 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5142 input.maxLength = this.maxLength;
5146 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5150 input.cols = this.cols;
5153 if (this.readOnly) {
5154 input.readonly = true;
5158 input.name = this.name;
5162 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5166 ['xs','sm','md','lg'].map(function(size){
5167 if (settings[size]) {
5168 cfg.cls += ' col-' + size + '-' + settings[size];
5172 var inputblock = input;
5174 if (this.before || this.after) {
5177 cls : 'input-group',
5181 inputblock.cn.push({
5183 cls : 'input-group-addon',
5187 inputblock.cn.push(input);
5189 inputblock.cn.push({
5191 cls : 'input-group-addon',
5198 if (align ==='left' && this.fieldLabel.length) {
5199 Roo.log("left and has label");
5205 cls : 'control-label col-sm-' + this.labelWidth,
5206 html : this.fieldLabel
5210 cls : "col-sm-" + (12 - this.labelWidth),
5217 } else if ( this.fieldLabel.length) {
5223 //cls : 'input-group-addon',
5224 html : this.fieldLabel
5234 Roo.log(" no label && no align");
5244 if (this.disabled) {
5245 input.disabled=true;
5252 * return the real textarea element.
5254 inputEl: function ()
5256 return this.el.select('textarea.form-control',true).first();
5264 * trigger field - base class for combo..
5269 * @class Roo.bootstrap.TriggerField
5270 * @extends Roo.bootstrap.Input
5271 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5272 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5273 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5274 * for which you can provide a custom implementation. For example:
5276 var trigger = new Roo.bootstrap.TriggerField();
5277 trigger.onTriggerClick = myTriggerFn;
5278 trigger.applyTo('my-field');
5281 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5282 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5283 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5284 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5286 * Create a new TriggerField.
5287 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5288 * to the base TextField)
5290 Roo.bootstrap.TriggerField = function(config){
5291 this.mimicing = false;
5292 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5295 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5297 * @cfg {String} triggerClass A CSS class to apply to the trigger
5300 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5304 /** @cfg {Boolean} grow @hide */
5305 /** @cfg {Number} growMin @hide */
5306 /** @cfg {Number} growMax @hide */
5312 autoSize: Roo.emptyFn,
5319 actionMode : 'wrap',
5323 getAutoCreate : function(){
5325 var parent = this.parent();
5327 var align = this.parentLabelAlign();
5332 cls: 'form-group' //input-group
5339 type : this.inputType,
5340 cls : 'form-control',
5341 autocomplete: 'off',
5342 placeholder : this.placeholder || ''
5346 input.name = this.name;
5349 input.cls += ' input-' + this.size;
5352 if (this.disabled) {
5353 input.disabled=true;
5356 var inputblock = input;
5358 if (this.before || this.after) {
5361 cls : 'input-group',
5365 inputblock.cn.push({
5367 cls : 'input-group-addon',
5371 inputblock.cn.push(input);
5373 inputblock.cn.push({
5375 cls : 'input-group-addon',
5388 cls: 'form-hidden-field'
5396 Roo.log('multiple');
5404 cls: 'form-hidden-field'
5408 cls: 'select2-choices',
5412 cls: 'select2-search-field',
5425 cls: 'select2-container input-group',
5430 cls: 'typeahead typeahead-long dropdown-menu',
5431 style: 'display:none'
5439 cls : 'input-group-addon btn dropdown-toggle',
5447 cls: 'combobox-clear',
5461 combobox.cls += ' select2-container-multi';
5464 if (align ==='left' && this.fieldLabel.length) {
5466 Roo.log("left and has label");
5472 cls : 'control-label col-sm-' + this.labelWidth,
5473 html : this.fieldLabel
5477 cls : "col-sm-" + (12 - this.labelWidth),
5484 } else if ( this.fieldLabel.length) {
5490 //cls : 'input-group-addon',
5491 html : this.fieldLabel
5501 Roo.log(" no label && no align");
5508 ['xs','sm','md','lg'].map(function(size){
5509 if (settings[size]) {
5510 cfg.cls += ' col-' + size + '-' + settings[size];
5521 onResize : function(w, h){
5522 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5523 // if(typeof w == 'number'){
5524 // var x = w - this.trigger.getWidth();
5525 // this.inputEl().setWidth(this.adjustWidth('input', x));
5526 // this.trigger.setStyle('left', x+'px');
5531 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5534 getResizeEl : function(){
5535 return this.inputEl();
5539 getPositionEl : function(){
5540 return this.inputEl();
5544 alignErrorIcon : function(){
5545 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5549 initEvents : function(){
5551 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5552 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5554 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5555 if(this.hideTrigger){
5556 this.trigger.setDisplayed(false);
5558 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5562 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5565 //this.trigger.addClassOnOver('x-form-trigger-over');
5566 //this.trigger.addClassOnClick('x-form-trigger-click');
5569 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5574 initTrigger : function(){
5579 onDestroy : function(){
5581 this.trigger.removeAllListeners();
5582 // this.trigger.remove();
5585 // this.wrap.remove();
5587 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5591 onFocus : function(){
5592 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5595 this.wrap.addClass('x-trigger-wrap-focus');
5596 this.mimicing = true;
5597 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5598 if(this.monitorTab){
5599 this.el.on("keydown", this.checkTab, this);
5606 checkTab : function(e){
5607 if(e.getKey() == e.TAB){
5613 onBlur : function(){
5618 mimicBlur : function(e, t){
5620 if(!this.wrap.contains(t) && this.validateBlur()){
5627 triggerBlur : function(){
5628 this.mimicing = false;
5629 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5630 if(this.monitorTab){
5631 this.el.un("keydown", this.checkTab, this);
5633 //this.wrap.removeClass('x-trigger-wrap-focus');
5634 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5638 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5639 validateBlur : function(e, t){
5644 onDisable : function(){
5645 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5647 // this.wrap.addClass('x-item-disabled');
5652 onEnable : function(){
5653 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5655 // this.el.removeClass('x-item-disabled');
5660 onShow : function(){
5661 var ae = this.getActionEl();
5664 ae.dom.style.display = '';
5665 ae.dom.style.visibility = 'visible';
5671 onHide : function(){
5672 var ae = this.getActionEl();
5673 ae.dom.style.display = 'none';
5677 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5678 * by an implementing function.
5680 * @param {EventObject} e
5682 onTriggerClick : Roo.emptyFn
5686 * Ext JS Library 1.1.1
5687 * Copyright(c) 2006-2007, Ext JS, LLC.
5689 * Originally Released Under LGPL - original licence link has changed is not relivant.
5692 * <script type="text/javascript">
5697 * @class Roo.data.SortTypes
5699 * Defines the default sorting (casting?) comparison functions used when sorting data.
5701 Roo.data.SortTypes = {
5703 * Default sort that does nothing
5704 * @param {Mixed} s The value being converted
5705 * @return {Mixed} The comparison value
5712 * The regular expression used to strip tags
5716 stripTagsRE : /<\/?[^>]+>/gi,
5719 * Strips all HTML tags to sort on text only
5720 * @param {Mixed} s The value being converted
5721 * @return {String} The comparison value
5723 asText : function(s){
5724 return String(s).replace(this.stripTagsRE, "");
5728 * Strips all HTML tags to sort on text only - Case insensitive
5729 * @param {Mixed} s The value being converted
5730 * @return {String} The comparison value
5732 asUCText : function(s){
5733 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5737 * Case insensitive string
5738 * @param {Mixed} s The value being converted
5739 * @return {String} The comparison value
5741 asUCString : function(s) {
5742 return String(s).toUpperCase();
5747 * @param {Mixed} s The value being converted
5748 * @return {Number} The comparison value
5750 asDate : function(s) {
5754 if(s instanceof Date){
5757 return Date.parse(String(s));
5762 * @param {Mixed} s The value being converted
5763 * @return {Float} The comparison value
5765 asFloat : function(s) {
5766 var val = parseFloat(String(s).replace(/,/g, ""));
5767 if(isNaN(val)) val = 0;
5773 * @param {Mixed} s The value being converted
5774 * @return {Number} The comparison value
5776 asInt : function(s) {
5777 var val = parseInt(String(s).replace(/,/g, ""));
5778 if(isNaN(val)) val = 0;
5783 * Ext JS Library 1.1.1
5784 * Copyright(c) 2006-2007, Ext JS, LLC.
5786 * Originally Released Under LGPL - original licence link has changed is not relivant.
5789 * <script type="text/javascript">
5793 * @class Roo.data.Record
5794 * Instances of this class encapsulate both record <em>definition</em> information, and record
5795 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5796 * to access Records cached in an {@link Roo.data.Store} object.<br>
5798 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5799 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5802 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5804 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5805 * {@link #create}. The parameters are the same.
5806 * @param {Array} data An associative Array of data values keyed by the field name.
5807 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5808 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5809 * not specified an integer id is generated.
5811 Roo.data.Record = function(data, id){
5812 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5817 * Generate a constructor for a specific record layout.
5818 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5819 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5820 * Each field definition object may contain the following properties: <ul>
5821 * <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,
5822 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5823 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5824 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5825 * is being used, then this is a string containing the javascript expression to reference the data relative to
5826 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5827 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5828 * this may be omitted.</p></li>
5829 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5830 * <ul><li>auto (Default, implies no conversion)</li>
5835 * <li>date</li></ul></p></li>
5836 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5837 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5838 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5839 * by the Reader into an object that will be stored in the Record. It is passed the
5840 * following parameters:<ul>
5841 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5843 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5845 * <br>usage:<br><pre><code>
5846 var TopicRecord = Roo.data.Record.create(
5847 {name: 'title', mapping: 'topic_title'},
5848 {name: 'author', mapping: 'username'},
5849 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5850 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5851 {name: 'lastPoster', mapping: 'user2'},
5852 {name: 'excerpt', mapping: 'post_text'}
5855 var myNewRecord = new TopicRecord({
5856 title: 'Do my job please',
5859 lastPost: new Date(),
5860 lastPoster: 'Animal',
5861 excerpt: 'No way dude!'
5863 myStore.add(myNewRecord);
5868 Roo.data.Record.create = function(o){
5870 f.superclass.constructor.apply(this, arguments);
5872 Roo.extend(f, Roo.data.Record);
5873 var p = f.prototype;
5874 p.fields = new Roo.util.MixedCollection(false, function(field){
5877 for(var i = 0, len = o.length; i < len; i++){
5878 p.fields.add(new Roo.data.Field(o[i]));
5880 f.getField = function(name){
5881 return p.fields.get(name);
5886 Roo.data.Record.AUTO_ID = 1000;
5887 Roo.data.Record.EDIT = 'edit';
5888 Roo.data.Record.REJECT = 'reject';
5889 Roo.data.Record.COMMIT = 'commit';
5891 Roo.data.Record.prototype = {
5893 * Readonly flag - true if this record has been modified.
5902 join : function(store){
5907 * Set the named field to the specified value.
5908 * @param {String} name The name of the field to set.
5909 * @param {Object} value The value to set the field to.
5911 set : function(name, value){
5912 if(this.data[name] == value){
5919 if(typeof this.modified[name] == 'undefined'){
5920 this.modified[name] = this.data[name];
5922 this.data[name] = value;
5923 if(!this.editing && this.store){
5924 this.store.afterEdit(this);
5929 * Get the value of the named field.
5930 * @param {String} name The name of the field to get the value of.
5931 * @return {Object} The value of the field.
5933 get : function(name){
5934 return this.data[name];
5938 beginEdit : function(){
5939 this.editing = true;
5944 cancelEdit : function(){
5945 this.editing = false;
5946 delete this.modified;
5950 endEdit : function(){
5951 this.editing = false;
5952 if(this.dirty && this.store){
5953 this.store.afterEdit(this);
5958 * Usually called by the {@link Roo.data.Store} which owns the Record.
5959 * Rejects all changes made to the Record since either creation, or the last commit operation.
5960 * Modified fields are reverted to their original values.
5962 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5963 * of reject operations.
5965 reject : function(){
5966 var m = this.modified;
5968 if(typeof m[n] != "function"){
5969 this.data[n] = m[n];
5973 delete this.modified;
5974 this.editing = false;
5976 this.store.afterReject(this);
5981 * Usually called by the {@link Roo.data.Store} which owns the Record.
5982 * Commits all changes made to the Record since either creation, or the last commit operation.
5984 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5985 * of commit operations.
5987 commit : function(){
5989 delete this.modified;
5990 this.editing = false;
5992 this.store.afterCommit(this);
5997 hasError : function(){
5998 return this.error != null;
6002 clearError : function(){
6007 * Creates a copy of this record.
6008 * @param {String} id (optional) A new record id if you don't want to use this record's id
6011 copy : function(newId) {
6012 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6016 * Ext JS Library 1.1.1
6017 * Copyright(c) 2006-2007, Ext JS, LLC.
6019 * Originally Released Under LGPL - original licence link has changed is not relivant.
6022 * <script type="text/javascript">
6028 * @class Roo.data.Store
6029 * @extends Roo.util.Observable
6030 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6031 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6033 * 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
6034 * has no knowledge of the format of the data returned by the Proxy.<br>
6036 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6037 * instances from the data object. These records are cached and made available through accessor functions.
6039 * Creates a new Store.
6040 * @param {Object} config A config object containing the objects needed for the Store to access data,
6041 * and read the data into Records.
6043 Roo.data.Store = function(config){
6044 this.data = new Roo.util.MixedCollection(false);
6045 this.data.getKey = function(o){
6048 this.baseParams = {};
6055 "multisort" : "_multisort"
6058 if(config && config.data){
6059 this.inlineData = config.data;
6063 Roo.apply(this, config);
6065 if(this.reader){ // reader passed
6066 this.reader = Roo.factory(this.reader, Roo.data);
6067 this.reader.xmodule = this.xmodule || false;
6068 if(!this.recordType){
6069 this.recordType = this.reader.recordType;
6071 if(this.reader.onMetaChange){
6072 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6076 if(this.recordType){
6077 this.fields = this.recordType.prototype.fields;
6083 * @event datachanged
6084 * Fires when the data cache has changed, and a widget which is using this Store
6085 * as a Record cache should refresh its view.
6086 * @param {Store} this
6091 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6092 * @param {Store} this
6093 * @param {Object} meta The JSON metadata
6098 * Fires when Records have been added to the Store
6099 * @param {Store} this
6100 * @param {Roo.data.Record[]} records The array of Records added
6101 * @param {Number} index The index at which the record(s) were added
6106 * Fires when a Record has been removed from the Store
6107 * @param {Store} this
6108 * @param {Roo.data.Record} record The Record that was removed
6109 * @param {Number} index The index at which the record was removed
6114 * Fires when a Record has been updated
6115 * @param {Store} this
6116 * @param {Roo.data.Record} record The Record that was updated
6117 * @param {String} operation The update operation being performed. Value may be one of:
6119 Roo.data.Record.EDIT
6120 Roo.data.Record.REJECT
6121 Roo.data.Record.COMMIT
6127 * Fires when the data cache has been cleared.
6128 * @param {Store} this
6133 * Fires before a request is made for a new data object. If the beforeload handler returns false
6134 * the load action will be canceled.
6135 * @param {Store} this
6136 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6140 * @event beforeloadadd
6141 * Fires after a new set of Records has been loaded.
6142 * @param {Store} this
6143 * @param {Roo.data.Record[]} records The Records that were loaded
6144 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6146 beforeloadadd : true,
6149 * Fires after a new set of Records has been loaded, before they are added to the store.
6150 * @param {Store} this
6151 * @param {Roo.data.Record[]} records The Records that were loaded
6152 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6153 * @params {Object} return from reader
6157 * @event loadexception
6158 * Fires if an exception occurs in the Proxy during loading.
6159 * Called with the signature of the Proxy's "loadexception" event.
6160 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6163 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6164 * @param {Object} load options
6165 * @param {Object} jsonData from your request (normally this contains the Exception)
6167 loadexception : true
6171 this.proxy = Roo.factory(this.proxy, Roo.data);
6172 this.proxy.xmodule = this.xmodule || false;
6173 this.relayEvents(this.proxy, ["loadexception"]);
6175 this.sortToggle = {};
6176 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6178 Roo.data.Store.superclass.constructor.call(this);
6180 if(this.inlineData){
6181 this.loadData(this.inlineData);
6182 delete this.inlineData;
6186 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6188 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6189 * without a remote query - used by combo/forms at present.
6193 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6196 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6199 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6200 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6203 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6204 * on any HTTP request
6207 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6210 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6214 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6215 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6220 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6221 * loaded or when a record is removed. (defaults to false).
6223 pruneModifiedRecords : false,
6229 * Add Records to the Store and fires the add event.
6230 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6232 add : function(records){
6233 records = [].concat(records);
6234 for(var i = 0, len = records.length; i < len; i++){
6235 records[i].join(this);
6237 var index = this.data.length;
6238 this.data.addAll(records);
6239 this.fireEvent("add", this, records, index);
6243 * Remove a Record from the Store and fires the remove event.
6244 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6246 remove : function(record){
6247 var index = this.data.indexOf(record);
6248 this.data.removeAt(index);
6249 if(this.pruneModifiedRecords){
6250 this.modified.remove(record);
6252 this.fireEvent("remove", this, record, index);
6256 * Remove all Records from the Store and fires the clear event.
6258 removeAll : function(){
6260 if(this.pruneModifiedRecords){
6263 this.fireEvent("clear", this);
6267 * Inserts Records to the Store at the given index and fires the add event.
6268 * @param {Number} index The start index at which to insert the passed Records.
6269 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6271 insert : function(index, records){
6272 records = [].concat(records);
6273 for(var i = 0, len = records.length; i < len; i++){
6274 this.data.insert(index, records[i]);
6275 records[i].join(this);
6277 this.fireEvent("add", this, records, index);
6281 * Get the index within the cache of the passed Record.
6282 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6283 * @return {Number} The index of the passed Record. Returns -1 if not found.
6285 indexOf : function(record){
6286 return this.data.indexOf(record);
6290 * Get the index within the cache of the Record with the passed id.
6291 * @param {String} id The id of the Record to find.
6292 * @return {Number} The index of the Record. Returns -1 if not found.
6294 indexOfId : function(id){
6295 return this.data.indexOfKey(id);
6299 * Get the Record with the specified id.
6300 * @param {String} id The id of the Record to find.
6301 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6303 getById : function(id){
6304 return this.data.key(id);
6308 * Get the Record at the specified index.
6309 * @param {Number} index The index of the Record to find.
6310 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6312 getAt : function(index){
6313 return this.data.itemAt(index);
6317 * Returns a range of Records between specified indices.
6318 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6319 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6320 * @return {Roo.data.Record[]} An array of Records
6322 getRange : function(start, end){
6323 return this.data.getRange(start, end);
6327 storeOptions : function(o){
6328 o = Roo.apply({}, o);
6331 this.lastOptions = o;
6335 * Loads the Record cache from the configured Proxy using the configured Reader.
6337 * If using remote paging, then the first load call must specify the <em>start</em>
6338 * and <em>limit</em> properties in the options.params property to establish the initial
6339 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6341 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6342 * and this call will return before the new data has been loaded. Perform any post-processing
6343 * in a callback function, or in a "load" event handler.</strong>
6345 * @param {Object} options An object containing properties which control loading options:<ul>
6346 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6347 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6348 * passed the following arguments:<ul>
6349 * <li>r : Roo.data.Record[]</li>
6350 * <li>options: Options object from the load call</li>
6351 * <li>success: Boolean success indicator</li></ul></li>
6352 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6353 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6356 load : function(options){
6357 options = options || {};
6358 if(this.fireEvent("beforeload", this, options) !== false){
6359 this.storeOptions(options);
6360 var p = Roo.apply(options.params || {}, this.baseParams);
6361 // if meta was not loaded from remote source.. try requesting it.
6362 if (!this.reader.metaFromRemote) {
6365 if(this.sortInfo && this.remoteSort){
6366 var pn = this.paramNames;
6367 p[pn["sort"]] = this.sortInfo.field;
6368 p[pn["dir"]] = this.sortInfo.direction;
6370 if (this.multiSort) {
6371 var pn = this.paramNames;
6372 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6375 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6380 * Reloads the Record cache from the configured Proxy using the configured Reader and
6381 * the options from the last load operation performed.
6382 * @param {Object} options (optional) An object containing properties which may override the options
6383 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6384 * the most recently used options are reused).
6386 reload : function(options){
6387 this.load(Roo.applyIf(options||{}, this.lastOptions));
6391 // Called as a callback by the Reader during a load operation.
6392 loadRecords : function(o, options, success){
6393 if(!o || success === false){
6394 if(success !== false){
6395 this.fireEvent("load", this, [], options, o);
6397 if(options.callback){
6398 options.callback.call(options.scope || this, [], options, false);
6402 // if data returned failure - throw an exception.
6403 if (o.success === false) {
6404 // show a message if no listener is registered.
6405 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6406 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6408 // loadmask wil be hooked into this..
6409 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6412 var r = o.records, t = o.totalRecords || r.length;
6414 this.fireEvent("beforeloadadd", this, r, options, o);
6416 if(!options || options.add !== true){
6417 if(this.pruneModifiedRecords){
6420 for(var i = 0, len = r.length; i < len; i++){
6424 this.data = this.snapshot;
6425 delete this.snapshot;
6428 this.data.addAll(r);
6429 this.totalLength = t;
6431 this.fireEvent("datachanged", this);
6433 this.totalLength = Math.max(t, this.data.length+r.length);
6436 this.fireEvent("load", this, r, options, o);
6437 if(options.callback){
6438 options.callback.call(options.scope || this, r, options, true);
6444 * Loads data from a passed data block. A Reader which understands the format of the data
6445 * must have been configured in the constructor.
6446 * @param {Object} data The data block from which to read the Records. The format of the data expected
6447 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6448 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6450 loadData : function(o, append){
6451 var r = this.reader.readRecords(o);
6452 this.loadRecords(r, {add: append}, true);
6456 * Gets the number of cached records.
6458 * <em>If using paging, this may not be the total size of the dataset. If the data object
6459 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6460 * the data set size</em>
6462 getCount : function(){
6463 return this.data.length || 0;
6467 * Gets the total number of records in the dataset as returned by the server.
6469 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6470 * the dataset size</em>
6472 getTotalCount : function(){
6473 return this.totalLength || 0;
6477 * Returns the sort state of the Store as an object with two properties:
6479 field {String} The name of the field by which the Records are sorted
6480 direction {String} The sort order, "ASC" or "DESC"
6483 getSortState : function(){
6484 return this.sortInfo;
6488 applySort : function(){
6489 if(this.sortInfo && !this.remoteSort){
6490 var s = this.sortInfo, f = s.field;
6491 var st = this.fields.get(f).sortType;
6492 var fn = function(r1, r2){
6493 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6494 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6496 this.data.sort(s.direction, fn);
6497 if(this.snapshot && this.snapshot != this.data){
6498 this.snapshot.sort(s.direction, fn);
6504 * Sets the default sort column and order to be used by the next load operation.
6505 * @param {String} fieldName The name of the field to sort by.
6506 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6508 setDefaultSort : function(field, dir){
6509 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6514 * If remote sorting is used, the sort is performed on the server, and the cache is
6515 * reloaded. If local sorting is used, the cache is sorted internally.
6516 * @param {String} fieldName The name of the field to sort by.
6517 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6519 sort : function(fieldName, dir){
6520 var f = this.fields.get(fieldName);
6522 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6524 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6525 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6530 this.sortToggle[f.name] = dir;
6531 this.sortInfo = {field: f.name, direction: dir};
6532 if(!this.remoteSort){
6534 this.fireEvent("datachanged", this);
6536 this.load(this.lastOptions);
6541 * Calls the specified function for each of the Records in the cache.
6542 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6543 * Returning <em>false</em> aborts and exits the iteration.
6544 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6546 each : function(fn, scope){
6547 this.data.each(fn, scope);
6551 * Gets all records modified since the last commit. Modified records are persisted across load operations
6552 * (e.g., during paging).
6553 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6555 getModifiedRecords : function(){
6556 return this.modified;
6560 createFilterFn : function(property, value, anyMatch){
6561 if(!value.exec){ // not a regex
6562 value = String(value);
6563 if(value.length == 0){
6566 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6569 return value.test(r.data[property]);
6574 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6575 * @param {String} property A field on your records
6576 * @param {Number} start The record index to start at (defaults to 0)
6577 * @param {Number} end The last record index to include (defaults to length - 1)
6578 * @return {Number} The sum
6580 sum : function(property, start, end){
6581 var rs = this.data.items, v = 0;
6583 end = (end || end === 0) ? end : rs.length-1;
6585 for(var i = start; i <= end; i++){
6586 v += (rs[i].data[property] || 0);
6592 * Filter the records by a specified property.
6593 * @param {String} field A field on your records
6594 * @param {String/RegExp} value Either a string that the field
6595 * should start with or a RegExp to test against the field
6596 * @param {Boolean} anyMatch True to match any part not just the beginning
6598 filter : function(property, value, anyMatch){
6599 var fn = this.createFilterFn(property, value, anyMatch);
6600 return fn ? this.filterBy(fn) : this.clearFilter();
6604 * Filter by a function. The specified function will be called with each
6605 * record in this data source. If the function returns true the record is included,
6606 * otherwise it is filtered.
6607 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6608 * @param {Object} scope (optional) The scope of the function (defaults to this)
6610 filterBy : function(fn, scope){
6611 this.snapshot = this.snapshot || this.data;
6612 this.data = this.queryBy(fn, scope||this);
6613 this.fireEvent("datachanged", this);
6617 * Query the records by a specified property.
6618 * @param {String} field A field on your records
6619 * @param {String/RegExp} value Either a string that the field
6620 * should start with or a RegExp to test against the field
6621 * @param {Boolean} anyMatch True to match any part not just the beginning
6622 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6624 query : function(property, value, anyMatch){
6625 var fn = this.createFilterFn(property, value, anyMatch);
6626 return fn ? this.queryBy(fn) : this.data.clone();
6630 * Query by a function. The specified function will be called with each
6631 * record in this data source. If the function returns true the record is included
6633 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6634 * @param {Object} scope (optional) The scope of the function (defaults to this)
6635 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6637 queryBy : function(fn, scope){
6638 var data = this.snapshot || this.data;
6639 return data.filterBy(fn, scope||this);
6643 * Collects unique values for a particular dataIndex from this store.
6644 * @param {String} dataIndex The property to collect
6645 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6646 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6647 * @return {Array} An array of the unique values
6649 collect : function(dataIndex, allowNull, bypassFilter){
6650 var d = (bypassFilter === true && this.snapshot) ?
6651 this.snapshot.items : this.data.items;
6652 var v, sv, r = [], l = {};
6653 for(var i = 0, len = d.length; i < len; i++){
6654 v = d[i].data[dataIndex];
6656 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6665 * Revert to a view of the Record cache with no filtering applied.
6666 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6668 clearFilter : function(suppressEvent){
6669 if(this.snapshot && this.snapshot != this.data){
6670 this.data = this.snapshot;
6671 delete this.snapshot;
6672 if(suppressEvent !== true){
6673 this.fireEvent("datachanged", this);
6679 afterEdit : function(record){
6680 if(this.modified.indexOf(record) == -1){
6681 this.modified.push(record);
6683 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6687 afterReject : function(record){
6688 this.modified.remove(record);
6689 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6693 afterCommit : function(record){
6694 this.modified.remove(record);
6695 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6699 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6700 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6702 commitChanges : function(){
6703 var m = this.modified.slice(0);
6705 for(var i = 0, len = m.length; i < len; i++){
6711 * Cancel outstanding changes on all changed records.
6713 rejectChanges : function(){
6714 var m = this.modified.slice(0);
6716 for(var i = 0, len = m.length; i < len; i++){
6721 onMetaChange : function(meta, rtype, o){
6722 this.recordType = rtype;
6723 this.fields = rtype.prototype.fields;
6724 delete this.snapshot;
6725 this.sortInfo = meta.sortInfo || this.sortInfo;
6727 this.fireEvent('metachange', this, this.reader.meta);
6730 moveIndex : function(data, type)
6732 var index = this.indexOf(data);
6734 var newIndex = index + type;
6738 this.insert(newIndex, data);
6743 * Ext JS Library 1.1.1
6744 * Copyright(c) 2006-2007, Ext JS, LLC.
6746 * Originally Released Under LGPL - original licence link has changed is not relivant.
6749 * <script type="text/javascript">
6753 * @class Roo.data.SimpleStore
6754 * @extends Roo.data.Store
6755 * Small helper class to make creating Stores from Array data easier.
6756 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6757 * @cfg {Array} fields An array of field definition objects, or field name strings.
6758 * @cfg {Array} data The multi-dimensional array of data
6760 * @param {Object} config
6762 Roo.data.SimpleStore = function(config){
6763 Roo.data.SimpleStore.superclass.constructor.call(this, {
6765 reader: new Roo.data.ArrayReader({
6768 Roo.data.Record.create(config.fields)
6770 proxy : new Roo.data.MemoryProxy(config.data)
6774 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6776 * Ext JS Library 1.1.1
6777 * Copyright(c) 2006-2007, Ext JS, LLC.
6779 * Originally Released Under LGPL - original licence link has changed is not relivant.
6782 * <script type="text/javascript">
6787 * @extends Roo.data.Store
6788 * @class Roo.data.JsonStore
6789 * Small helper class to make creating Stores for JSON data easier. <br/>
6791 var store = new Roo.data.JsonStore({
6792 url: 'get-images.php',
6794 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6797 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6798 * JsonReader and HttpProxy (unless inline data is provided).</b>
6799 * @cfg {Array} fields An array of field definition objects, or field name strings.
6801 * @param {Object} config
6803 Roo.data.JsonStore = function(c){
6804 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6805 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6806 reader: new Roo.data.JsonReader(c, c.fields)
6809 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6811 * Ext JS Library 1.1.1
6812 * Copyright(c) 2006-2007, Ext JS, LLC.
6814 * Originally Released Under LGPL - original licence link has changed is not relivant.
6817 * <script type="text/javascript">
6821 Roo.data.Field = function(config){
6822 if(typeof config == "string"){
6823 config = {name: config};
6825 Roo.apply(this, config);
6831 var st = Roo.data.SortTypes;
6832 // named sortTypes are supported, here we look them up
6833 if(typeof this.sortType == "string"){
6834 this.sortType = st[this.sortType];
6837 // set default sortType for strings and dates
6841 this.sortType = st.asUCString;
6844 this.sortType = st.asDate;
6847 this.sortType = st.none;
6852 var stripRe = /[\$,%]/g;
6854 // prebuilt conversion function for this field, instead of
6855 // switching every time we're reading a value
6857 var cv, dateFormat = this.dateFormat;
6862 cv = function(v){ return v; };
6865 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6869 return v !== undefined && v !== null && v !== '' ?
6870 parseInt(String(v).replace(stripRe, ""), 10) : '';
6875 return v !== undefined && v !== null && v !== '' ?
6876 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6881 cv = function(v){ return v === true || v === "true" || v == 1; };
6888 if(v instanceof Date){
6892 if(dateFormat == "timestamp"){
6893 return new Date(v*1000);
6895 return Date.parseDate(v, dateFormat);
6897 var parsed = Date.parse(v);
6898 return parsed ? new Date(parsed) : null;
6907 Roo.data.Field.prototype = {
6915 * Ext JS Library 1.1.1
6916 * Copyright(c) 2006-2007, Ext JS, LLC.
6918 * Originally Released Under LGPL - original licence link has changed is not relivant.
6921 * <script type="text/javascript">
6924 // Base class for reading structured data from a data source. This class is intended to be
6925 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6928 * @class Roo.data.DataReader
6929 * Base class for reading structured data from a data source. This class is intended to be
6930 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6933 Roo.data.DataReader = function(meta, recordType){
6937 this.recordType = recordType instanceof Array ?
6938 Roo.data.Record.create(recordType) : recordType;
6941 Roo.data.DataReader.prototype = {
6943 * Create an empty record
6944 * @param {Object} data (optional) - overlay some values
6945 * @return {Roo.data.Record} record created.
6947 newRow : function(d) {
6949 this.recordType.prototype.fields.each(function(c) {
6951 case 'int' : da[c.name] = 0; break;
6952 case 'date' : da[c.name] = new Date(); break;
6953 case 'float' : da[c.name] = 0.0; break;
6954 case 'boolean' : da[c.name] = false; break;
6955 default : da[c.name] = ""; break;
6959 return new this.recordType(Roo.apply(da, d));
6964 * Ext JS Library 1.1.1
6965 * Copyright(c) 2006-2007, Ext JS, LLC.
6967 * Originally Released Under LGPL - original licence link has changed is not relivant.
6970 * <script type="text/javascript">
6974 * @class Roo.data.DataProxy
6975 * @extends Roo.data.Observable
6976 * This class is an abstract base class for implementations which provide retrieval of
6977 * unformatted data objects.<br>
6979 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6980 * (of the appropriate type which knows how to parse the data object) to provide a block of
6981 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6983 * Custom implementations must implement the load method as described in
6984 * {@link Roo.data.HttpProxy#load}.
6986 Roo.data.DataProxy = function(){
6990 * Fires before a network request is made to retrieve a data object.
6991 * @param {Object} This DataProxy object.
6992 * @param {Object} params The params parameter to the load function.
6997 * Fires before the load method's callback is called.
6998 * @param {Object} This DataProxy object.
6999 * @param {Object} o The data object.
7000 * @param {Object} arg The callback argument object passed to the load function.
7004 * @event loadexception
7005 * Fires if an Exception occurs during data retrieval.
7006 * @param {Object} This DataProxy object.
7007 * @param {Object} o The data object.
7008 * @param {Object} arg The callback argument object passed to the load function.
7009 * @param {Object} e The Exception.
7011 loadexception : true
7013 Roo.data.DataProxy.superclass.constructor.call(this);
7016 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7019 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7023 * Ext JS Library 1.1.1
7024 * Copyright(c) 2006-2007, Ext JS, LLC.
7026 * Originally Released Under LGPL - original licence link has changed is not relivant.
7029 * <script type="text/javascript">
7032 * @class Roo.data.MemoryProxy
7033 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7034 * to the Reader when its load method is called.
7036 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7038 Roo.data.MemoryProxy = function(data){
7042 Roo.data.MemoryProxy.superclass.constructor.call(this);
7046 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7048 * Load data from the requested source (in this case an in-memory
7049 * data object passed to the constructor), read the data object into
7050 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7051 * process that block using the passed callback.
7052 * @param {Object} params This parameter is not used by the MemoryProxy class.
7053 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7054 * object into a block of Roo.data.Records.
7055 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7056 * The function must be passed <ul>
7057 * <li>The Record block object</li>
7058 * <li>The "arg" argument from the load function</li>
7059 * <li>A boolean success indicator</li>
7061 * @param {Object} scope The scope in which to call the callback
7062 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7064 load : function(params, reader, callback, scope, arg){
7065 params = params || {};
7068 result = reader.readRecords(this.data);
7070 this.fireEvent("loadexception", this, arg, null, e);
7071 callback.call(scope, null, arg, false);
7074 callback.call(scope, result, arg, true);
7078 update : function(params, records){
7083 * Ext JS Library 1.1.1
7084 * Copyright(c) 2006-2007, Ext JS, LLC.
7086 * Originally Released Under LGPL - original licence link has changed is not relivant.
7089 * <script type="text/javascript">
7092 * @class Roo.data.HttpProxy
7093 * @extends Roo.data.DataProxy
7094 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7095 * configured to reference a certain URL.<br><br>
7097 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7098 * from which the running page was served.<br><br>
7100 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7102 * Be aware that to enable the browser to parse an XML document, the server must set
7103 * the Content-Type header in the HTTP response to "text/xml".
7105 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7106 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7107 * will be used to make the request.
7109 Roo.data.HttpProxy = function(conn){
7110 Roo.data.HttpProxy.superclass.constructor.call(this);
7111 // is conn a conn config or a real conn?
7113 this.useAjax = !conn || !conn.events;
7117 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7118 // thse are take from connection...
7121 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7124 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7125 * extra parameters to each request made by this object. (defaults to undefined)
7128 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7129 * to each request made by this object. (defaults to undefined)
7132 * @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)
7135 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7138 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7144 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7148 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7149 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7150 * a finer-grained basis than the DataProxy events.
7152 getConnection : function(){
7153 return this.useAjax ? Roo.Ajax : this.conn;
7157 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7158 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7159 * process that block using the passed callback.
7160 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7161 * for the request to the remote server.
7162 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7163 * object into a block of Roo.data.Records.
7164 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7165 * The function must be passed <ul>
7166 * <li>The Record block object</li>
7167 * <li>The "arg" argument from the load function</li>
7168 * <li>A boolean success indicator</li>
7170 * @param {Object} scope The scope in which to call the callback
7171 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7173 load : function(params, reader, callback, scope, arg){
7174 if(this.fireEvent("beforeload", this, params) !== false){
7176 params : params || {},
7178 callback : callback,
7183 callback : this.loadResponse,
7187 Roo.applyIf(o, this.conn);
7188 if(this.activeRequest){
7189 Roo.Ajax.abort(this.activeRequest);
7191 this.activeRequest = Roo.Ajax.request(o);
7193 this.conn.request(o);
7196 callback.call(scope||this, null, arg, false);
7201 loadResponse : function(o, success, response){
7202 delete this.activeRequest;
7204 this.fireEvent("loadexception", this, o, response);
7205 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7210 result = o.reader.read(response);
7212 this.fireEvent("loadexception", this, o, response, e);
7213 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7217 this.fireEvent("load", this, o, o.request.arg);
7218 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7222 update : function(dataSet){
7227 updateResponse : function(dataSet){
7232 * Ext JS Library 1.1.1
7233 * Copyright(c) 2006-2007, Ext JS, LLC.
7235 * Originally Released Under LGPL - original licence link has changed is not relivant.
7238 * <script type="text/javascript">
7242 * @class Roo.data.ScriptTagProxy
7243 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7244 * other than the originating domain of the running page.<br><br>
7246 * <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
7247 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7249 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7250 * source code that is used as the source inside a <script> tag.<br><br>
7252 * In order for the browser to process the returned data, the server must wrap the data object
7253 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7254 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7255 * depending on whether the callback name was passed:
7258 boolean scriptTag = false;
7259 String cb = request.getParameter("callback");
7262 response.setContentType("text/javascript");
7264 response.setContentType("application/x-json");
7266 Writer out = response.getWriter();
7268 out.write(cb + "(");
7270 out.print(dataBlock.toJsonString());
7277 * @param {Object} config A configuration object.
7279 Roo.data.ScriptTagProxy = function(config){
7280 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7281 Roo.apply(this, config);
7282 this.head = document.getElementsByTagName("head")[0];
7285 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7287 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7289 * @cfg {String} url The URL from which to request the data object.
7292 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7296 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7297 * the server the name of the callback function set up by the load call to process the returned data object.
7298 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7299 * javascript output which calls this named function passing the data object as its only parameter.
7301 callbackParam : "callback",
7303 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7304 * name to the request.
7309 * Load data from the configured URL, read the data object into
7310 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7311 * process that block using the passed callback.
7312 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7313 * for the request to the remote server.
7314 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7315 * object into a block of Roo.data.Records.
7316 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7317 * The function must be passed <ul>
7318 * <li>The Record block object</li>
7319 * <li>The "arg" argument from the load function</li>
7320 * <li>A boolean success indicator</li>
7322 * @param {Object} scope The scope in which to call the callback
7323 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7325 load : function(params, reader, callback, scope, arg){
7326 if(this.fireEvent("beforeload", this, params) !== false){
7328 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7331 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7333 url += "&_dc=" + (new Date().getTime());
7335 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7338 cb : "stcCallback"+transId,
7339 scriptId : "stcScript"+transId,
7343 callback : callback,
7349 window[trans.cb] = function(o){
7350 conn.handleResponse(o, trans);
7353 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7355 if(this.autoAbort !== false){
7359 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7361 var script = document.createElement("script");
7362 script.setAttribute("src", url);
7363 script.setAttribute("type", "text/javascript");
7364 script.setAttribute("id", trans.scriptId);
7365 this.head.appendChild(script);
7369 callback.call(scope||this, null, arg, false);
7374 isLoading : function(){
7375 return this.trans ? true : false;
7379 * Abort the current server request.
7382 if(this.isLoading()){
7383 this.destroyTrans(this.trans);
7388 destroyTrans : function(trans, isLoaded){
7389 this.head.removeChild(document.getElementById(trans.scriptId));
7390 clearTimeout(trans.timeoutId);
7392 window[trans.cb] = undefined;
7394 delete window[trans.cb];
7397 // if hasn't been loaded, wait for load to remove it to prevent script error
7398 window[trans.cb] = function(){
7399 window[trans.cb] = undefined;
7401 delete window[trans.cb];
7408 handleResponse : function(o, trans){
7410 this.destroyTrans(trans, true);
7413 result = trans.reader.readRecords(o);
7415 this.fireEvent("loadexception", this, o, trans.arg, e);
7416 trans.callback.call(trans.scope||window, null, trans.arg, false);
7419 this.fireEvent("load", this, o, trans.arg);
7420 trans.callback.call(trans.scope||window, result, trans.arg, true);
7424 handleFailure : function(trans){
7426 this.destroyTrans(trans, false);
7427 this.fireEvent("loadexception", this, null, trans.arg);
7428 trans.callback.call(trans.scope||window, null, trans.arg, false);
7432 * Ext JS Library 1.1.1
7433 * Copyright(c) 2006-2007, Ext JS, LLC.
7435 * Originally Released Under LGPL - original licence link has changed is not relivant.
7438 * <script type="text/javascript">
7442 * @class Roo.data.JsonReader
7443 * @extends Roo.data.DataReader
7444 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7445 * based on mappings in a provided Roo.data.Record constructor.
7447 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7448 * in the reply previously.
7453 var RecordDef = Roo.data.Record.create([
7454 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7455 {name: 'occupation'} // This field will use "occupation" as the mapping.
7457 var myReader = new Roo.data.JsonReader({
7458 totalProperty: "results", // The property which contains the total dataset size (optional)
7459 root: "rows", // The property which contains an Array of row objects
7460 id: "id" // The property within each row object that provides an ID for the record (optional)
7464 * This would consume a JSON file like this:
7466 { 'results': 2, 'rows': [
7467 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7468 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7471 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7472 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7473 * paged from the remote server.
7474 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7475 * @cfg {String} root name of the property which contains the Array of row objects.
7476 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7478 * Create a new JsonReader
7479 * @param {Object} meta Metadata configuration options
7480 * @param {Object} recordType Either an Array of field definition objects,
7481 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7483 Roo.data.JsonReader = function(meta, recordType){
7486 // set some defaults:
7488 totalProperty: 'total',
7489 successProperty : 'success',
7494 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7496 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7499 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7500 * Used by Store query builder to append _requestMeta to params.
7503 metaFromRemote : false,
7505 * This method is only used by a DataProxy which has retrieved data from a remote server.
7506 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7507 * @return {Object} data A data block which is used by an Roo.data.Store object as
7508 * a cache of Roo.data.Records.
7510 read : function(response){
7511 var json = response.responseText;
7513 var o = /* eval:var:o */ eval("("+json+")");
7515 throw {message: "JsonReader.read: Json object not found"};
7521 this.metaFromRemote = true;
7522 this.meta = o.metaData;
7523 this.recordType = Roo.data.Record.create(o.metaData.fields);
7524 this.onMetaChange(this.meta, this.recordType, o);
7526 return this.readRecords(o);
7529 // private function a store will implement
7530 onMetaChange : function(meta, recordType, o){
7537 simpleAccess: function(obj, subsc) {
7544 getJsonAccessor: function(){
7546 return function(expr) {
7548 return(re.test(expr))
7549 ? new Function("obj", "return obj." + expr)
7559 * Create a data block containing Roo.data.Records from an XML document.
7560 * @param {Object} o An object which contains an Array of row objects in the property specified
7561 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7562 * which contains the total size of the dataset.
7563 * @return {Object} data A data block which is used by an Roo.data.Store object as
7564 * a cache of Roo.data.Records.
7566 readRecords : function(o){
7568 * After any data loads, the raw JSON data is available for further custom processing.
7572 var s = this.meta, Record = this.recordType,
7573 f = Record.prototype.fields, fi = f.items, fl = f.length;
7575 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7577 if(s.totalProperty) {
7578 this.getTotal = this.getJsonAccessor(s.totalProperty);
7580 if(s.successProperty) {
7581 this.getSuccess = this.getJsonAccessor(s.successProperty);
7583 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7585 var g = this.getJsonAccessor(s.id);
7586 this.getId = function(rec) {
7588 return (r === undefined || r === "") ? null : r;
7591 this.getId = function(){return null;};
7594 for(var jj = 0; jj < fl; jj++){
7596 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7597 this.ef[jj] = this.getJsonAccessor(map);
7601 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7602 if(s.totalProperty){
7603 var vt = parseInt(this.getTotal(o), 10);
7608 if(s.successProperty){
7609 var vs = this.getSuccess(o);
7610 if(vs === false || vs === 'false'){
7615 for(var i = 0; i < c; i++){
7618 var id = this.getId(n);
7619 for(var j = 0; j < fl; j++){
7621 var v = this.ef[j](n);
7623 Roo.log('missing convert for ' + f.name);
7627 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7629 var record = new Record(values, id);
7631 records[i] = record;
7637 totalRecords : totalRecords
7642 * Ext JS Library 1.1.1
7643 * Copyright(c) 2006-2007, Ext JS, LLC.
7645 * Originally Released Under LGPL - original licence link has changed is not relivant.
7648 * <script type="text/javascript">
7652 * @class Roo.data.ArrayReader
7653 * @extends Roo.data.DataReader
7654 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7655 * Each element of that Array represents a row of data fields. The
7656 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7657 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7661 var RecordDef = Roo.data.Record.create([
7662 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7663 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7665 var myReader = new Roo.data.ArrayReader({
7666 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7670 * This would consume an Array like this:
7672 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7674 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7676 * Create a new JsonReader
7677 * @param {Object} meta Metadata configuration options.
7678 * @param {Object} recordType Either an Array of field definition objects
7679 * as specified to {@link Roo.data.Record#create},
7680 * or an {@link Roo.data.Record} object
7681 * created using {@link Roo.data.Record#create}.
7683 Roo.data.ArrayReader = function(meta, recordType){
7684 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7687 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7689 * Create a data block containing Roo.data.Records from an XML document.
7690 * @param {Object} o An Array of row objects which represents the dataset.
7691 * @return {Object} data A data block which is used by an Roo.data.Store object as
7692 * a cache of Roo.data.Records.
7694 readRecords : function(o){
7695 var sid = this.meta ? this.meta.id : null;
7696 var recordType = this.recordType, fields = recordType.prototype.fields;
7699 for(var i = 0; i < root.length; i++){
7702 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7703 for(var j = 0, jlen = fields.length; j < jlen; j++){
7704 var f = fields.items[j];
7705 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7706 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7710 var record = new recordType(values, id);
7712 records[records.length] = record;
7716 totalRecords : records.length
7725 * @class Roo.bootstrap.ComboBox
7726 * @extends Roo.bootstrap.TriggerField
7727 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7728 * @cfg {Boolean} append (true|false) default false
7730 * Create a new ComboBox.
7731 * @param {Object} config Configuration options
7733 Roo.bootstrap.ComboBox = function(config){
7734 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7738 * Fires when the dropdown list is expanded
7739 * @param {Roo.bootstrap.ComboBox} combo This combo box
7744 * Fires when the dropdown list is collapsed
7745 * @param {Roo.bootstrap.ComboBox} combo This combo box
7749 * @event beforeselect
7750 * Fires before a list item is selected. Return false to cancel the selection.
7751 * @param {Roo.bootstrap.ComboBox} combo This combo box
7752 * @param {Roo.data.Record} record The data record returned from the underlying store
7753 * @param {Number} index The index of the selected item in the dropdown list
7755 'beforeselect' : true,
7758 * Fires when a list item is selected
7759 * @param {Roo.bootstrap.ComboBox} combo This combo box
7760 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7761 * @param {Number} index The index of the selected item in the dropdown list
7765 * @event beforequery
7766 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7767 * The event object passed has these properties:
7768 * @param {Roo.bootstrap.ComboBox} combo This combo box
7769 * @param {String} query The query
7770 * @param {Boolean} forceAll true to force "all" query
7771 * @param {Boolean} cancel true to cancel the query
7772 * @param {Object} e The query event object
7774 'beforequery': true,
7777 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7778 * @param {Roo.bootstrap.ComboBox} combo This combo box
7783 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7784 * @param {Roo.bootstrap.ComboBox} combo This combo box
7785 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7790 * Fires when the remove value from the combobox array
7791 * @param {Roo.bootstrap.ComboBox} combo This combo box
7798 this.selectedIndex = -1;
7799 if(this.mode == 'local'){
7800 if(config.queryDelay === undefined){
7801 this.queryDelay = 10;
7803 if(config.minChars === undefined){
7809 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7812 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7813 * rendering into an Roo.Editor, defaults to false)
7816 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7817 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7820 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7823 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7824 * the dropdown list (defaults to undefined, with no header element)
7828 * @cfg {String/Roo.Template} tpl The template to use to render the output
7832 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7834 listWidth: undefined,
7836 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7837 * mode = 'remote' or 'text' if mode = 'local')
7839 displayField: undefined,
7841 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7842 * mode = 'remote' or 'value' if mode = 'local').
7843 * Note: use of a valueField requires the user make a selection
7844 * in order for a value to be mapped.
7846 valueField: undefined,
7850 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7851 * field's data value (defaults to the underlying DOM element's name)
7853 hiddenName: undefined,
7855 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7859 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7861 selectedClass: 'active',
7864 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7868 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7869 * anchor positions (defaults to 'tl-bl')
7871 listAlign: 'tl-bl?',
7873 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7877 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7878 * query specified by the allQuery config option (defaults to 'query')
7880 triggerAction: 'query',
7882 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7883 * (defaults to 4, does not apply if editable = false)
7887 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7888 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7892 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7893 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7897 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7898 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7902 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7903 * when editable = true (defaults to false)
7905 selectOnFocus:false,
7907 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7909 queryParam: 'query',
7911 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7912 * when mode = 'remote' (defaults to 'Loading...')
7914 loadingText: 'Loading...',
7916 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7920 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7924 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7925 * traditional select (defaults to true)
7929 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7933 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7937 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7938 * listWidth has a higher value)
7942 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7943 * allow the user to set arbitrary text into the field (defaults to false)
7945 forceSelection:false,
7947 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7948 * if typeAhead = true (defaults to 250)
7950 typeAheadDelay : 250,
7952 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7953 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7955 valueNotFoundText : undefined,
7957 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7962 * @cfg {Boolean} disableClear Disable showing of clear button.
7964 disableClear : false,
7966 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
7968 alwaysQuery : false,
7971 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
7985 // element that contains real text value.. (when hidden is used..)
7988 initEvents: function(){
7991 throw "can not find store for combo";
7993 this.store = Roo.factory(this.store, Roo.data);
7997 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8000 if(this.hiddenName){
8002 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8004 this.hiddenField.dom.value =
8005 this.hiddenValue !== undefined ? this.hiddenValue :
8006 this.value !== undefined ? this.value : '';
8008 // prevent input submission
8009 this.el.dom.removeAttribute('name');
8010 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8015 // this.el.dom.setAttribute('autocomplete', 'off');
8018 var cls = 'x-combo-list';
8019 this.list = this.el.select('ul.dropdown-menu',true).first();
8021 //this.list = new Roo.Layer({
8022 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8025 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8026 this.list.setWidth(lw);
8028 this.list.on('mouseover', this.onViewOver, this);
8029 this.list.on('mousemove', this.onViewMove, this);
8031 this.list.on('scroll', this.onViewScroll, this);
8034 this.list.swallowEvent('mousewheel');
8035 this.assetHeight = 0;
8038 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8039 this.assetHeight += this.header.getHeight();
8042 this.innerList = this.list.createChild({cls:cls+'-inner'});
8043 this.innerList.on('mouseover', this.onViewOver, this);
8044 this.innerList.on('mousemove', this.onViewMove, this);
8045 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8047 if(this.allowBlank && !this.pageSize && !this.disableClear){
8048 this.footer = this.list.createChild({cls:cls+'-ft'});
8049 this.pageTb = new Roo.Toolbar(this.footer);
8053 this.footer = this.list.createChild({cls:cls+'-ft'});
8054 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8055 {pageSize: this.pageSize});
8059 if (this.pageTb && this.allowBlank && !this.disableClear) {
8061 this.pageTb.add(new Roo.Toolbar.Fill(), {
8062 cls: 'x-btn-icon x-btn-clear',
8068 _this.onSelect(false, -1);
8073 this.assetHeight += this.footer.getHeight();
8078 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8081 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8082 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8084 //this.view.wrapEl.setDisplayed(false);
8085 this.view.on('click', this.onViewClick, this);
8089 this.store.on('beforeload', this.onBeforeLoad, this);
8090 this.store.on('load', this.onLoad, this);
8091 this.store.on('loadexception', this.onLoadException, this);
8094 this.resizer = new Roo.Resizable(this.list, {
8095 pinned:true, handles:'se'
8097 this.resizer.on('resize', function(r, w, h){
8098 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8100 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8101 this.restrictHeight();
8103 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8107 this.editable = true;
8108 this.setEditable(false);
8113 if (typeof(this.events.add.listeners) != 'undefined') {
8115 this.addicon = this.wrap.createChild(
8116 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8118 this.addicon.on('click', function(e) {
8119 this.fireEvent('add', this);
8122 if (typeof(this.events.edit.listeners) != 'undefined') {
8124 this.editicon = this.wrap.createChild(
8125 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8127 this.editicon.setStyle('margin-left', '40px');
8129 this.editicon.on('click', function(e) {
8131 // we fire even if inothing is selected..
8132 this.fireEvent('edit', this, this.lastData );
8138 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8140 this.inKeyMode = true;
8144 "down" : function(e){
8145 if(!this.isExpanded()){
8146 this.onTriggerClick();
8148 this.inKeyMode = true;
8153 "enter" : function(e){
8158 "esc" : function(e){
8162 "tab" : function(e){
8165 if(this.fireEvent("specialkey", this, e)){
8166 this.onViewClick(false);
8174 doRelay : function(foo, bar, hname){
8175 if(hname == 'down' || this.scope.isExpanded()){
8176 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8185 this.queryDelay = Math.max(this.queryDelay || 10,
8186 this.mode == 'local' ? 10 : 250);
8189 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8192 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8194 if(this.editable !== false){
8195 this.inputEl().on("keyup", this.onKeyUp, this);
8197 if(this.forceSelection){
8198 this.on('blur', this.doForce, this);
8202 this.choices = this.el.select('ul.select2-choices', true).first();
8203 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8207 onDestroy : function(){
8209 this.view.setStore(null);
8210 this.view.el.removeAllListeners();
8211 this.view.el.remove();
8212 this.view.purgeListeners();
8215 this.list.dom.innerHTML = '';
8218 this.store.un('beforeload', this.onBeforeLoad, this);
8219 this.store.un('load', this.onLoad, this);
8220 this.store.un('loadexception', this.onLoadException, this);
8222 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8226 fireKey : function(e){
8227 if(e.isNavKeyPress() && !this.list.isVisible()){
8228 this.fireEvent("specialkey", this, e);
8233 onResize: function(w, h){
8234 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8236 // if(typeof w != 'number'){
8237 // // we do not handle it!?!?
8240 // var tw = this.trigger.getWidth();
8241 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8242 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8244 // this.inputEl().setWidth( this.adjustWidth('input', x));
8246 // //this.trigger.setStyle('left', x+'px');
8248 // if(this.list && this.listWidth === undefined){
8249 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8250 // this.list.setWidth(lw);
8251 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8259 * Allow or prevent the user from directly editing the field text. If false is passed,
8260 * the user will only be able to select from the items defined in the dropdown list. This method
8261 * is the runtime equivalent of setting the 'editable' config option at config time.
8262 * @param {Boolean} value True to allow the user to directly edit the field text
8264 setEditable : function(value){
8265 if(value == this.editable){
8268 this.editable = value;
8270 this.inputEl().dom.setAttribute('readOnly', true);
8271 this.inputEl().on('mousedown', this.onTriggerClick, this);
8272 this.inputEl().addClass('x-combo-noedit');
8274 this.inputEl().dom.setAttribute('readOnly', false);
8275 this.inputEl().un('mousedown', this.onTriggerClick, this);
8276 this.inputEl().removeClass('x-combo-noedit');
8282 onBeforeLoad : function(combo,opts){
8287 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8289 this.restrictHeight();
8290 this.selectedIndex = -1;
8294 onLoad : function(){
8296 this.hasQuery = false;
8302 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8303 this.loading.hide();
8306 if(this.store.getCount() > 0){
8308 this.restrictHeight();
8309 if(this.lastQuery == this.allQuery){
8311 this.inputEl().dom.select();
8313 if(!this.selectByValue(this.value, true)){
8314 this.select(0, true);
8318 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8319 this.taTask.delay(this.typeAheadDelay);
8323 this.onEmptyResults();
8329 onLoadException : function()
8331 this.hasQuery = false;
8333 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8334 this.loading.hide();
8338 Roo.log(this.store.reader.jsonData);
8339 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8341 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8347 onTypeAhead : function(){
8348 if(this.store.getCount() > 0){
8349 var r = this.store.getAt(0);
8350 var newValue = r.data[this.displayField];
8351 var len = newValue.length;
8352 var selStart = this.getRawValue().length;
8354 if(selStart != len){
8355 this.setRawValue(newValue);
8356 this.selectText(selStart, newValue.length);
8362 onSelect : function(record, index){
8364 if(this.fireEvent('beforeselect', this, record, index) !== false){
8366 this.setFromData(index > -1 ? record.data : false);
8369 this.fireEvent('select', this, record, index);
8374 * Returns the currently selected field value or empty string if no value is set.
8375 * @return {String} value The selected value
8377 getValue : function(){
8380 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8383 if(this.valueField){
8384 return typeof this.value != 'undefined' ? this.value : '';
8386 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8391 * Clears any text/value currently set in the field
8393 clearValue : function(){
8394 if(this.hiddenField){
8395 this.hiddenField.dom.value = '';
8398 this.setRawValue('');
8399 this.lastSelectionText = '';
8404 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8405 * will be displayed in the field. If the value does not match the data value of an existing item,
8406 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8407 * Otherwise the field will be blank (although the value will still be set).
8408 * @param {String} value The value to match
8410 setValue : function(v){
8417 if(this.valueField){
8418 var r = this.findRecord(this.valueField, v);
8420 text = r.data[this.displayField];
8421 }else if(this.valueNotFoundText !== undefined){
8422 text = this.valueNotFoundText;
8425 this.lastSelectionText = text;
8426 if(this.hiddenField){
8427 this.hiddenField.dom.value = v;
8429 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8433 * @property {Object} the last set data for the element
8438 * Sets the value of the field based on a object which is related to the record format for the store.
8439 * @param {Object} value the value to set as. or false on reset?
8441 setFromData : function(o){
8448 var dv = ''; // display value
8449 var vv = ''; // value value..
8451 if (this.displayField) {
8452 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8454 // this is an error condition!!!
8455 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8458 if(this.valueField){
8459 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8462 if(this.hiddenField){
8463 this.hiddenField.dom.value = vv;
8465 this.lastSelectionText = dv;
8466 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8470 // no hidden field.. - we store the value in 'value', but still display
8471 // display field!!!!
8472 this.lastSelectionText = dv;
8473 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8480 // overridden so that last data is reset..
8481 this.setValue(this.originalValue);
8482 this.clearInvalid();
8483 this.lastData = false;
8485 this.view.clearSelections();
8489 findRecord : function(prop, value){
8491 if(this.store.getCount() > 0){
8492 this.store.each(function(r){
8493 if(r.data[prop] == value){
8505 // returns hidden if it's set..
8506 if (!this.rendered) {return ''};
8507 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8511 onViewMove : function(e, t){
8512 this.inKeyMode = false;
8516 onViewOver : function(e, t){
8517 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8520 var item = this.view.findItemFromChild(t);
8522 var index = this.view.indexOf(item);
8523 this.select(index, false);
8528 onViewClick : function(doFocus)
8530 var index = this.view.getSelectedIndexes()[0];
8531 var r = this.store.getAt(index);
8533 this.onSelect(r, index);
8535 if(doFocus !== false && !this.blockFocus){
8536 this.inputEl().focus();
8541 restrictHeight : function(){
8542 //this.innerList.dom.style.height = '';
8543 //var inner = this.innerList.dom;
8544 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8545 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8546 //this.list.beginUpdate();
8547 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8548 this.list.alignTo(this.inputEl(), this.listAlign);
8549 //this.list.endUpdate();
8553 onEmptyResults : function(){
8558 * Returns true if the dropdown list is expanded, else false.
8560 isExpanded : function(){
8561 return this.list.isVisible();
8565 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8566 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8567 * @param {String} value The data value of the item to select
8568 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8569 * selected item if it is not currently in view (defaults to true)
8570 * @return {Boolean} True if the value matched an item in the list, else false
8572 selectByValue : function(v, scrollIntoView){
8573 if(v !== undefined && v !== null){
8574 var r = this.findRecord(this.valueField || this.displayField, v);
8576 this.select(this.store.indexOf(r), scrollIntoView);
8584 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8585 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8586 * @param {Number} index The zero-based index of the list item to select
8587 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8588 * selected item if it is not currently in view (defaults to true)
8590 select : function(index, scrollIntoView){
8591 this.selectedIndex = index;
8592 this.view.select(index);
8593 if(scrollIntoView !== false){
8594 var el = this.view.getNode(index);
8596 //this.innerList.scrollChildIntoView(el, false);
8603 selectNext : function(){
8604 var ct = this.store.getCount();
8606 if(this.selectedIndex == -1){
8608 }else if(this.selectedIndex < ct-1){
8609 this.select(this.selectedIndex+1);
8615 selectPrev : function(){
8616 var ct = this.store.getCount();
8618 if(this.selectedIndex == -1){
8620 }else if(this.selectedIndex != 0){
8621 this.select(this.selectedIndex-1);
8627 onKeyUp : function(e){
8628 if(this.editable !== false && !e.isSpecialKey()){
8629 this.lastKey = e.getKey();
8630 this.dqTask.delay(this.queryDelay);
8635 validateBlur : function(){
8636 return !this.list || !this.list.isVisible();
8640 initQuery : function(){
8641 this.doQuery(this.getRawValue());
8645 doForce : function(){
8646 if(this.el.dom.value.length > 0){
8648 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8654 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8655 * query allowing the query action to be canceled if needed.
8656 * @param {String} query The SQL query to execute
8657 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8658 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8659 * saved in the current store (defaults to false)
8661 doQuery : function(q, forceAll){
8663 if(q === undefined || q === null){
8672 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8677 forceAll = qe.forceAll;
8678 if(forceAll === true || (q.length >= this.minChars)){
8680 this.hasQuery = true;
8682 if(this.lastQuery != q || this.alwaysQuery){
8684 if(this.mode == 'local'){
8685 this.selectedIndex = -1;
8687 this.store.clearFilter();
8689 this.store.filter(this.displayField, q);
8693 this.store.baseParams[this.queryParam] = q;
8695 var options = {params : this.getParams(q)};
8699 options.params.start = this.page * this.pageSize;
8702 this.store.load(options);
8706 this.selectedIndex = -1;
8711 this.loadNext = false;
8715 getParams : function(q){
8717 //p[this.queryParam] = q;
8721 p.limit = this.pageSize;
8727 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8729 collapse : function(){
8730 if(!this.isExpanded()){
8735 Roo.get(document).un('mousedown', this.collapseIf, this);
8736 Roo.get(document).un('mousewheel', this.collapseIf, this);
8737 if (!this.editable) {
8738 Roo.get(document).un('keydown', this.listKeyPress, this);
8740 this.fireEvent('collapse', this);
8744 collapseIf : function(e){
8745 var in_combo = e.within(this.el);
8746 var in_list = e.within(this.list);
8748 if (in_combo || in_list) {
8749 //e.stopPropagation();
8758 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8760 expand : function(){
8762 if(this.isExpanded() || !this.hasFocus){
8766 this.list.alignTo(this.inputEl(), this.listAlign);
8768 Roo.get(document).on('mousedown', this.collapseIf, this);
8769 Roo.get(document).on('mousewheel', this.collapseIf, this);
8770 if (!this.editable) {
8771 Roo.get(document).on('keydown', this.listKeyPress, this);
8774 this.fireEvent('expand', this);
8778 // Implements the default empty TriggerField.onTriggerClick function
8779 onTriggerClick : function()
8781 Roo.log('trigger click');
8788 this.loadNext = false;
8790 if(this.isExpanded()){
8792 if (!this.blockFocus) {
8793 this.inputEl().focus();
8797 this.hasFocus = true;
8798 if(this.triggerAction == 'all') {
8799 this.doQuery(this.allQuery, true);
8801 this.doQuery(this.getRawValue());
8803 if (!this.blockFocus) {
8804 this.inputEl().focus();
8808 listKeyPress : function(e)
8810 //Roo.log('listkeypress');
8811 // scroll to first matching element based on key pres..
8812 if (e.isSpecialKey()) {
8815 var k = String.fromCharCode(e.getKey()).toUpperCase();
8818 var csel = this.view.getSelectedNodes();
8819 var cselitem = false;
8821 var ix = this.view.indexOf(csel[0]);
8822 cselitem = this.store.getAt(ix);
8823 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8829 this.store.each(function(v) {
8831 // start at existing selection.
8832 if (cselitem.id == v.id) {
8838 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8839 match = this.store.indexOf(v);
8845 if (match === false) {
8846 return true; // no more action?
8849 this.view.select(match);
8850 var sn = Roo.get(this.view.getSelectedNodes()[0])
8851 //sn.scrollIntoView(sn.dom.parentNode, false);
8854 onViewScroll : function(e, t){
8856 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8860 this.hasQuery = true;
8862 this.loading = this.list.select('.loading', true).first();
8864 if(this.loading === null){
8865 this.list.createChild({
8867 cls: 'loading select2-more-results select2-active',
8868 html: 'Loading more results...'
8871 this.loading = this.list.select('.loading', true).first();
8873 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8875 this.loading.hide();
8878 this.loading.show();
8883 this.loadNext = true;
8885 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8890 addItem : function(o)
8892 var dv = ''; // display value
8894 if (this.displayField) {
8895 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8897 // this is an error condition!!!
8898 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8905 var choice = this.choices.createChild({
8907 cls: 'select2-search-choice',
8916 cls: 'select2-search-choice-close',
8921 }, this.searchField);
8923 var close = choice.select('a.select2-search-choice-close', true).first()
8925 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8932 this.inputEl().dom.value = '';
8936 onRemoveItem : function(e, _self, o)
8938 Roo.log('remove item');
8939 var index = this.item.indexOf(o.data) * 1;
8942 Roo.log('not this item?!');
8946 this.item.splice(index, 1);
8951 this.fireEvent('remove', this);
8955 syncValue : function()
8957 if(!this.item.length){
8964 Roo.each(this.item, function(i){
8965 if(_this.valueField){
8966 value.push(i[_this.valueField]);
8973 this.value = value.join(',');
8975 if(this.hiddenField){
8976 this.hiddenField.dom.value = this.value;
8980 clearItem : function()
8988 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
8998 * @cfg {Boolean} grow
9002 * @cfg {Number} growMin
9006 * @cfg {Number} growMax
9016 * Ext JS Library 1.1.1
9017 * Copyright(c) 2006-2007, Ext JS, LLC.
9019 * Originally Released Under LGPL - original licence link has changed is not relivant.
9022 * <script type="text/javascript">
9027 * @extends Roo.util.Observable
9028 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9029 * This class also supports single and multi selection modes. <br>
9030 * Create a data model bound view:
9032 var store = new Roo.data.Store(...);
9034 var view = new Roo.View({
9036 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9039 selectedClass: "ydataview-selected",
9043 // listen for node click?
9044 view.on("click", function(vw, index, node, e){
9045 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9049 dataModel.load("foobar.xml");
9051 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9053 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9054 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9056 * Note: old style constructor is still suported (container, template, config)
9060 * @param {Object} config The config object
9063 Roo.View = function(config, depreciated_tpl, depreciated_config){
9065 if (typeof(depreciated_tpl) == 'undefined') {
9066 // new way.. - universal constructor.
9067 Roo.apply(this, config);
9068 this.el = Roo.get(this.el);
9071 this.el = Roo.get(config);
9072 this.tpl = depreciated_tpl;
9073 Roo.apply(this, depreciated_config);
9075 this.wrapEl = this.el.wrap().wrap();
9076 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9079 if(typeof(this.tpl) == "string"){
9080 this.tpl = new Roo.Template(this.tpl);
9082 // support xtype ctors..
9083 this.tpl = new Roo.factory(this.tpl, Roo);
9095 * @event beforeclick
9096 * Fires before a click is processed. Returns false to cancel the default action.
9097 * @param {Roo.View} this
9098 * @param {Number} index The index of the target node
9099 * @param {HTMLElement} node The target node
9100 * @param {Roo.EventObject} e The raw event object
9102 "beforeclick" : true,
9105 * Fires when a template node is clicked.
9106 * @param {Roo.View} this
9107 * @param {Number} index The index of the target node
9108 * @param {HTMLElement} node The target node
9109 * @param {Roo.EventObject} e The raw event object
9114 * Fires when a template node is double clicked.
9115 * @param {Roo.View} this
9116 * @param {Number} index The index of the target node
9117 * @param {HTMLElement} node The target node
9118 * @param {Roo.EventObject} e The raw event object
9122 * @event contextmenu
9123 * Fires when a template node is right clicked.
9124 * @param {Roo.View} this
9125 * @param {Number} index The index of the target node
9126 * @param {HTMLElement} node The target node
9127 * @param {Roo.EventObject} e The raw event object
9129 "contextmenu" : true,
9131 * @event selectionchange
9132 * Fires when the selected nodes change.
9133 * @param {Roo.View} this
9134 * @param {Array} selections Array of the selected nodes
9136 "selectionchange" : true,
9139 * @event beforeselect
9140 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9141 * @param {Roo.View} this
9142 * @param {HTMLElement} node The node to be selected
9143 * @param {Array} selections Array of currently selected nodes
9145 "beforeselect" : true,
9147 * @event preparedata
9148 * Fires on every row to render, to allow you to change the data.
9149 * @param {Roo.View} this
9150 * @param {Object} data to be rendered (change this)
9152 "preparedata" : true
9160 "click": this.onClick,
9161 "dblclick": this.onDblClick,
9162 "contextmenu": this.onContextMenu,
9166 this.selections = [];
9168 this.cmp = new Roo.CompositeElementLite([]);
9170 this.store = Roo.factory(this.store, Roo.data);
9171 this.setStore(this.store, true);
9174 if ( this.footer && this.footer.xtype) {
9176 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9178 this.footer.dataSource = this.store
9179 this.footer.container = fctr;
9180 this.footer = Roo.factory(this.footer, Roo);
9181 fctr.insertFirst(this.el);
9183 // this is a bit insane - as the paging toolbar seems to detach the el..
9184 // dom.parentNode.parentNode.parentNode
9185 // they get detached?
9189 Roo.View.superclass.constructor.call(this);
9194 Roo.extend(Roo.View, Roo.util.Observable, {
9197 * @cfg {Roo.data.Store} store Data store to load data from.
9202 * @cfg {String|Roo.Element} el The container element.
9207 * @cfg {String|Roo.Template} tpl The template used by this View
9211 * @cfg {String} dataName the named area of the template to use as the data area
9212 * Works with domtemplates roo-name="name"
9216 * @cfg {String} selectedClass The css class to add to selected nodes
9218 selectedClass : "x-view-selected",
9220 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9225 * @cfg {String} text to display on mask (default Loading)
9229 * @cfg {Boolean} multiSelect Allow multiple selection
9231 multiSelect : false,
9233 * @cfg {Boolean} singleSelect Allow single selection
9235 singleSelect: false,
9238 * @cfg {Boolean} toggleSelect - selecting
9240 toggleSelect : false,
9243 * Returns the element this view is bound to.
9244 * @return {Roo.Element}
9253 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9255 refresh : function(){
9259 // if we are using something like 'domtemplate', then
9260 // the what gets used is:
9261 // t.applySubtemplate(NAME, data, wrapping data..)
9262 // the outer template then get' applied with
9263 // the store 'extra data'
9264 // and the body get's added to the
9265 // roo-name="data" node?
9266 // <span class='roo-tpl-{name}'></span> ?????
9270 this.clearSelections();
9273 var records = this.store.getRange();
9274 if(records.length < 1) {
9276 // is this valid?? = should it render a template??
9278 this.el.update(this.emptyText);
9282 if (this.dataName) {
9283 this.el.update(t.apply(this.store.meta)); //????
9284 el = this.el.child('.roo-tpl-' + this.dataName);
9287 for(var i = 0, len = records.length; i < len; i++){
9288 var data = this.prepareData(records[i].data, i, records[i]);
9289 this.fireEvent("preparedata", this, data, i, records[i]);
9290 html[html.length] = Roo.util.Format.trim(
9292 t.applySubtemplate(this.dataName, data, this.store.meta) :
9299 el.update(html.join(""));
9300 this.nodes = el.dom.childNodes;
9301 this.updateIndexes(0);
9306 * Function to override to reformat the data that is sent to
9307 * the template for each node.
9308 * DEPRICATED - use the preparedata event handler.
9309 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9310 * a JSON object for an UpdateManager bound view).
9312 prepareData : function(data, index, record)
9314 this.fireEvent("preparedata", this, data, index, record);
9318 onUpdate : function(ds, record){
9319 Roo.log('on update');
9320 this.clearSelections();
9321 var index = this.store.indexOf(record);
9322 var n = this.nodes[index];
9323 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9324 n.parentNode.removeChild(n);
9325 this.updateIndexes(index, index);
9331 onAdd : function(ds, records, index)
9333 Roo.log(['on Add', ds, records, index] );
9334 this.clearSelections();
9335 if(this.nodes.length == 0){
9339 var n = this.nodes[index];
9340 for(var i = 0, len = records.length; i < len; i++){
9341 var d = this.prepareData(records[i].data, i, records[i]);
9343 this.tpl.insertBefore(n, d);
9346 this.tpl.append(this.el, d);
9349 this.updateIndexes(index);
9352 onRemove : function(ds, record, index){
9353 Roo.log('onRemove');
9354 this.clearSelections();
9355 var el = this.dataName ?
9356 this.el.child('.roo-tpl-' + this.dataName) :
9359 el.dom.removeChild(this.nodes[index]);
9360 this.updateIndexes(index);
9364 * Refresh an individual node.
9365 * @param {Number} index
9367 refreshNode : function(index){
9368 this.onUpdate(this.store, this.store.getAt(index));
9371 updateIndexes : function(startIndex, endIndex){
9372 var ns = this.nodes;
9373 startIndex = startIndex || 0;
9374 endIndex = endIndex || ns.length - 1;
9375 for(var i = startIndex; i <= endIndex; i++){
9376 ns[i].nodeIndex = i;
9381 * Changes the data store this view uses and refresh the view.
9382 * @param {Store} store
9384 setStore : function(store, initial){
9385 if(!initial && this.store){
9386 this.store.un("datachanged", this.refresh);
9387 this.store.un("add", this.onAdd);
9388 this.store.un("remove", this.onRemove);
9389 this.store.un("update", this.onUpdate);
9390 this.store.un("clear", this.refresh);
9391 this.store.un("beforeload", this.onBeforeLoad);
9392 this.store.un("load", this.onLoad);
9393 this.store.un("loadexception", this.onLoad);
9397 store.on("datachanged", this.refresh, this);
9398 store.on("add", this.onAdd, this);
9399 store.on("remove", this.onRemove, this);
9400 store.on("update", this.onUpdate, this);
9401 store.on("clear", this.refresh, this);
9402 store.on("beforeload", this.onBeforeLoad, this);
9403 store.on("load", this.onLoad, this);
9404 store.on("loadexception", this.onLoad, this);
9412 * onbeforeLoad - masks the loading area.
9415 onBeforeLoad : function(store,opts)
9417 Roo.log('onBeforeLoad');
9421 this.el.mask(this.mask ? this.mask : "Loading" );
9423 onLoad : function ()
9430 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9431 * @param {HTMLElement} node
9432 * @return {HTMLElement} The template node
9434 findItemFromChild : function(node){
9435 var el = this.dataName ?
9436 this.el.child('.roo-tpl-' + this.dataName,true) :
9439 if(!node || node.parentNode == el){
9442 var p = node.parentNode;
9443 while(p && p != el){
9444 if(p.parentNode == el){
9453 onClick : function(e){
9454 var item = this.findItemFromChild(e.getTarget());
9456 var index = this.indexOf(item);
9457 if(this.onItemClick(item, index, e) !== false){
9458 this.fireEvent("click", this, index, item, e);
9461 this.clearSelections();
9466 onContextMenu : function(e){
9467 var item = this.findItemFromChild(e.getTarget());
9469 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9474 onDblClick : function(e){
9475 var item = this.findItemFromChild(e.getTarget());
9477 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9481 onItemClick : function(item, index, e)
9483 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9486 if (this.toggleSelect) {
9487 var m = this.isSelected(item) ? 'unselect' : 'select';
9490 _t[m](item, true, false);
9493 if(this.multiSelect || this.singleSelect){
9494 if(this.multiSelect && e.shiftKey && this.lastSelection){
9495 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9497 this.select(item, this.multiSelect && e.ctrlKey);
9498 this.lastSelection = item;
9506 * Get the number of selected nodes.
9509 getSelectionCount : function(){
9510 return this.selections.length;
9514 * Get the currently selected nodes.
9515 * @return {Array} An array of HTMLElements
9517 getSelectedNodes : function(){
9518 return this.selections;
9522 * Get the indexes of the selected nodes.
9525 getSelectedIndexes : function(){
9526 var indexes = [], s = this.selections;
9527 for(var i = 0, len = s.length; i < len; i++){
9528 indexes.push(s[i].nodeIndex);
9534 * Clear all selections
9535 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9537 clearSelections : function(suppressEvent){
9538 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9539 this.cmp.elements = this.selections;
9540 this.cmp.removeClass(this.selectedClass);
9541 this.selections = [];
9543 this.fireEvent("selectionchange", this, this.selections);
9549 * Returns true if the passed node is selected
9550 * @param {HTMLElement/Number} node The node or node index
9553 isSelected : function(node){
9554 var s = this.selections;
9558 node = this.getNode(node);
9559 return s.indexOf(node) !== -1;
9564 * @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
9565 * @param {Boolean} keepExisting (optional) true to keep existing selections
9566 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9568 select : function(nodeInfo, keepExisting, suppressEvent){
9569 if(nodeInfo instanceof Array){
9571 this.clearSelections(true);
9573 for(var i = 0, len = nodeInfo.length; i < len; i++){
9574 this.select(nodeInfo[i], true, true);
9578 var node = this.getNode(nodeInfo);
9579 if(!node || this.isSelected(node)){
9580 return; // already selected.
9583 this.clearSelections(true);
9585 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9586 Roo.fly(node).addClass(this.selectedClass);
9587 this.selections.push(node);
9589 this.fireEvent("selectionchange", this, this.selections);
9597 * @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
9598 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9599 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9601 unselect : function(nodeInfo, keepExisting, suppressEvent)
9603 if(nodeInfo instanceof Array){
9604 Roo.each(this.selections, function(s) {
9605 this.unselect(s, nodeInfo);
9609 var node = this.getNode(nodeInfo);
9610 if(!node || !this.isSelected(node)){
9611 Roo.log("not selected");
9612 return; // not selected.
9616 Roo.each(this.selections, function(s) {
9618 Roo.fly(node).removeClass(this.selectedClass);
9625 this.selections= ns;
9626 this.fireEvent("selectionchange", this, this.selections);
9630 * Gets a template node.
9631 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9632 * @return {HTMLElement} The node or null if it wasn't found
9634 getNode : function(nodeInfo){
9635 if(typeof nodeInfo == "string"){
9636 return document.getElementById(nodeInfo);
9637 }else if(typeof nodeInfo == "number"){
9638 return this.nodes[nodeInfo];
9644 * Gets a range template nodes.
9645 * @param {Number} startIndex
9646 * @param {Number} endIndex
9647 * @return {Array} An array of nodes
9649 getNodes : function(start, end){
9650 var ns = this.nodes;
9652 end = typeof end == "undefined" ? ns.length - 1 : end;
9655 for(var i = start; i <= end; i++){
9659 for(var i = start; i >= end; i--){
9667 * Finds the index of the passed node
9668 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9669 * @return {Number} The index of the node or -1
9671 indexOf : function(node){
9672 node = this.getNode(node);
9673 if(typeof node.nodeIndex == "number"){
9674 return node.nodeIndex;
9676 var ns = this.nodes;
9677 for(var i = 0, len = ns.length; i < len; i++){
9688 * based on jquery fullcalendar
9692 Roo.bootstrap = Roo.bootstrap || {};
9694 * @class Roo.bootstrap.Calendar
9695 * @extends Roo.bootstrap.Component
9696 * Bootstrap Calendar class
9697 * @cfg {Boolean} loadMask (true|false) default false
9700 * Create a new Container
9701 * @param {Object} config The config object
9706 Roo.bootstrap.Calendar = function(config){
9707 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9711 * Fires when a date is selected
9712 * @param {DatePicker} this
9713 * @param {Date} date The selected date
9717 * @event monthchange
9718 * Fires when the displayed month changes
9719 * @param {DatePicker} this
9720 * @param {Date} date The selected month
9722 'monthchange': true,
9725 * Fires when mouse over an event
9726 * @param {Calendar} this
9727 * @param {event} Event
9732 * Fires when the mouse leaves an
9733 * @param {Calendar} this
9739 * Fires when the mouse click an
9740 * @param {Calendar} this
9749 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9752 * @cfg {Number} startDay
9753 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9759 getAutoCreate : function(){
9762 var fc_button = function(name, corner, style, content ) {
9763 return Roo.apply({},{
9765 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9767 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9770 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9778 style : 'width:100%',
9785 cls : 'fc-header-left',
9787 fc_button('prev', 'left', 'arrow', '‹' ),
9788 fc_button('next', 'right', 'arrow', '›' ),
9789 { tag: 'span', cls: 'fc-header-space' },
9790 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9798 cls : 'fc-header-center',
9802 cls: 'fc-header-title',
9805 html : 'month / year'
9813 cls : 'fc-header-right',
9815 /* fc_button('month', 'left', '', 'month' ),
9816 fc_button('week', '', '', 'week' ),
9817 fc_button('day', 'right', '', 'day' )
9829 var cal_heads = function() {
9831 // fixme - handle this.
9833 for (var i =0; i < Date.dayNames.length; i++) {
9834 var d = Date.dayNames[i];
9837 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9838 html : d.substring(0,3)
9842 ret[0].cls += ' fc-first';
9843 ret[6].cls += ' fc-last';
9846 var cal_cell = function(n) {
9849 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9854 cls: 'fc-day-number',
9858 cls: 'fc-day-content',
9862 style: 'position: relative;' // height: 17px;
9874 var cal_rows = function() {
9877 for (var r = 0; r < 6; r++) {
9884 for (var i =0; i < Date.dayNames.length; i++) {
9885 var d = Date.dayNames[i];
9886 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9889 row.cn[0].cls+=' fc-first';
9890 row.cn[0].cn[0].style = 'min-height:90px';
9891 row.cn[6].cls+=' fc-last';
9895 ret[0].cls += ' fc-first';
9896 ret[4].cls += ' fc-prev-last';
9897 ret[5].cls += ' fc-last';
9904 cls: 'fc-border-separate',
9905 style : 'width:100%',
9913 cls : 'fc-first fc-last',
9932 style : "position: relative;",
9935 cls : 'fc-view fc-view-month fc-grid',
9936 style : 'position: relative',
9937 unselectable : 'on',
9940 cls : 'fc-event-container',
9941 style : 'position:absolute;z-index:8;top:0;left:0;'
9959 initEvents : function()
9962 throw "can not find store for calendar";
9968 style: "text-align:center",
9972 style: "background-color:white;width:50%;margin:250 auto",
9976 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9987 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9989 var size = this.el.select('.fc-content', true).first().getSize();
9990 this.maskEl.setSize(size.width, size.height);
9991 this.maskEl.enableDisplayMode("block");
9996 this.store = Roo.factory(this.store, Roo.data);
9997 this.store.on('load', this.onLoad, this);
9998 this.store.on('beforeload', this.onBeforeLoad, this);
10002 this.cells = this.el.select('.fc-day',true);
10003 //Roo.log(this.cells);
10004 this.textNodes = this.el.query('.fc-day-number');
10005 this.cells.addClassOnOver('fc-state-hover');
10007 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10008 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10009 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10010 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10012 this.on('monthchange', this.onMonthChange, this);
10014 this.update(new Date().clearTime());
10017 resize : function() {
10018 var sz = this.el.getSize();
10020 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10021 this.el.select('.fc-day-content div',true).setHeight(34);
10026 showPrevMonth : function(e){
10027 this.update(this.activeDate.add("mo", -1));
10029 showToday : function(e){
10030 this.update(new Date().clearTime());
10033 showNextMonth : function(e){
10034 this.update(this.activeDate.add("mo", 1));
10038 showPrevYear : function(){
10039 this.update(this.activeDate.add("y", -1));
10043 showNextYear : function(){
10044 this.update(this.activeDate.add("y", 1));
10049 update : function(date)
10051 var vd = this.activeDate;
10052 this.activeDate = date;
10053 // if(vd && this.el){
10054 // var t = date.getTime();
10055 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10056 // Roo.log('using add remove');
10058 // this.fireEvent('monthchange', this, date);
10060 // this.cells.removeClass("fc-state-highlight");
10061 // this.cells.each(function(c){
10062 // if(c.dateValue == t){
10063 // c.addClass("fc-state-highlight");
10064 // setTimeout(function(){
10065 // try{c.dom.firstChild.focus();}catch(e){}
10075 var days = date.getDaysInMonth();
10077 var firstOfMonth = date.getFirstDateOfMonth();
10078 var startingPos = firstOfMonth.getDay()-this.startDay;
10080 if(startingPos < this.startDay){
10084 var pm = date.add(Date.MONTH, -1);
10085 var prevStart = pm.getDaysInMonth()-startingPos;
10087 this.cells = this.el.select('.fc-day',true);
10088 this.textNodes = this.el.query('.fc-day-number');
10089 this.cells.addClassOnOver('fc-state-hover');
10091 var cells = this.cells.elements;
10092 var textEls = this.textNodes;
10094 Roo.each(cells, function(cell){
10095 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10098 days += startingPos;
10100 // convert everything to numbers so it's fast
10101 var day = 86400000;
10102 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10105 //Roo.log(prevStart);
10107 var today = new Date().clearTime().getTime();
10108 var sel = date.clearTime().getTime();
10109 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10110 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10111 var ddMatch = this.disabledDatesRE;
10112 var ddText = this.disabledDatesText;
10113 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10114 var ddaysText = this.disabledDaysText;
10115 var format = this.format;
10117 var setCellClass = function(cal, cell){
10119 //Roo.log('set Cell Class');
10121 var t = d.getTime();
10125 cell.dateValue = t;
10127 cell.className += " fc-today";
10128 cell.className += " fc-state-highlight";
10129 cell.title = cal.todayText;
10132 // disable highlight in other month..
10133 //cell.className += " fc-state-highlight";
10138 cell.className = " fc-state-disabled";
10139 cell.title = cal.minText;
10143 cell.className = " fc-state-disabled";
10144 cell.title = cal.maxText;
10148 if(ddays.indexOf(d.getDay()) != -1){
10149 cell.title = ddaysText;
10150 cell.className = " fc-state-disabled";
10153 if(ddMatch && format){
10154 var fvalue = d.dateFormat(format);
10155 if(ddMatch.test(fvalue)){
10156 cell.title = ddText.replace("%0", fvalue);
10157 cell.className = " fc-state-disabled";
10161 if (!cell.initialClassName) {
10162 cell.initialClassName = cell.dom.className;
10165 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10170 for(; i < startingPos; i++) {
10171 textEls[i].innerHTML = (++prevStart);
10172 d.setDate(d.getDate()+1);
10174 cells[i].className = "fc-past fc-other-month";
10175 setCellClass(this, cells[i]);
10180 for(; i < days; i++){
10181 intDay = i - startingPos + 1;
10182 textEls[i].innerHTML = (intDay);
10183 d.setDate(d.getDate()+1);
10185 cells[i].className = ''; // "x-date-active";
10186 setCellClass(this, cells[i]);
10190 for(; i < 42; i++) {
10191 textEls[i].innerHTML = (++extraDays);
10192 d.setDate(d.getDate()+1);
10194 cells[i].className = "fc-future fc-other-month";
10195 setCellClass(this, cells[i]);
10198 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10200 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10202 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10203 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10205 if(totalRows != 6){
10206 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10207 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10210 this.fireEvent('monthchange', this, date);
10214 if(!this.internalRender){
10215 var main = this.el.dom.firstChild;
10216 var w = main.offsetWidth;
10217 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10218 Roo.fly(main).setWidth(w);
10219 this.internalRender = true;
10220 // opera does not respect the auto grow header center column
10221 // then, after it gets a width opera refuses to recalculate
10222 // without a second pass
10223 if(Roo.isOpera && !this.secondPass){
10224 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10225 this.secondPass = true;
10226 this.update.defer(10, this, [date]);
10233 findCell : function(dt) {
10234 dt = dt.clearTime().getTime();
10236 this.cells.each(function(c){
10237 //Roo.log("check " +c.dateValue + '?=' + dt);
10238 if(c.dateValue == dt){
10248 findCells : function(ev) {
10249 var s = ev.start.clone().clearTime().getTime();
10251 var e= ev.end.clone().clearTime().getTime();
10254 this.cells.each(function(c){
10255 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10257 if(c.dateValue > e){
10260 if(c.dateValue < s){
10269 findBestRow: function(cells)
10273 for (var i =0 ; i < cells.length;i++) {
10274 ret = Math.max(cells[i].rows || 0,ret);
10281 addItem : function(ev)
10283 // look for vertical location slot in
10284 var cells = this.findCells(ev);
10286 ev.row = this.findBestRow(cells);
10288 // work out the location.
10292 for(var i =0; i < cells.length; i++) {
10300 if (crow.start.getY() == cells[i].getY()) {
10302 crow.end = cells[i];
10318 for (var i = 0; i < cells.length;i++) {
10319 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10323 this.calevents.push(ev);
10326 clearEvents: function() {
10328 if(!this.calevents){
10332 Roo.each(this.cells.elements, function(c){
10336 Roo.each(this.calevents, function(e) {
10337 Roo.each(e.els, function(el) {
10338 el.un('mouseenter' ,this.onEventEnter, this);
10339 el.un('mouseleave' ,this.onEventLeave, this);
10346 renderEvents: function()
10348 // first make sure there is enough space..
10350 this.cells.each(function(c) {
10352 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10355 for (var e = 0; e < this.calevents.length; e++) {
10356 var ev = this.calevents[e];
10357 var cells = ev.cells;
10358 var rows = ev.rows;
10360 for(var i =0; i < rows.length; i++) {
10363 // how many rows should it span..
10366 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10367 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10369 unselectable : "on",
10372 cls: 'fc-event-inner',
10376 // cls: 'fc-event-time',
10377 // html : cells.length > 1 ? '' : ev.time
10381 cls: 'fc-event-title',
10382 html : String.format('{0}', ev.title)
10389 cls: 'ui-resizable-handle ui-resizable-e',
10390 html : '  '
10396 cfg.cls += ' fc-event-start';
10398 if ((i+1) == rows.length) {
10399 cfg.cls += ' fc-event-end';
10402 var ctr = this.el.select('.fc-event-container',true).first();
10403 var cg = ctr.createChild(cfg);
10405 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10406 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10407 cg.on('click', this.onEventClick, this, ev);
10411 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10412 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10414 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10415 cg.setWidth(ebox.right - sbox.x -2);
10423 onEventEnter: function (e, el,event,d) {
10424 this.fireEvent('evententer', this, el, event);
10427 onEventLeave: function (e, el,event,d) {
10428 this.fireEvent('eventleave', this, el, event);
10431 onEventClick: function (e, el,event,d) {
10432 this.fireEvent('eventclick', this, el, event);
10435 onMonthChange: function () {
10439 onLoad: function ()
10441 this.calevents = [];
10444 if(this.store.getCount() > 0){
10445 this.store.data.each(function(d){
10448 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10449 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10450 time : d.data.start_time,
10451 title : d.data.title,
10452 description : d.data.description,
10453 venue : d.data.venue
10458 this.renderEvents();
10461 this.maskEl.hide();
10465 onBeforeLoad: function()
10467 this.clearEvents();
10470 this.maskEl.show();
10484 * @class Roo.bootstrap.Popover
10485 * @extends Roo.bootstrap.Component
10486 * Bootstrap Popover class
10487 * @cfg {String} html contents of the popover (or false to use children..)
10488 * @cfg {String} title of popover (or false to hide)
10489 * @cfg {String} placement how it is placed
10490 * @cfg {String} trigger click || hover (or false to trigger manually)
10491 * @cfg {String} over what (parent or false to trigger manually.)
10494 * Create a new Popover
10495 * @param {Object} config The config object
10498 Roo.bootstrap.Popover = function(config){
10499 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10502 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10504 title: 'Fill in a title',
10507 placement : 'right',
10508 trigger : 'hover', // hover
10512 can_build_overlaid : false,
10514 getChildContainer : function()
10516 return this.el.select('.popover-content',true).first();
10519 getAutoCreate : function(){
10520 Roo.log('make popover?');
10522 cls : 'popover roo-dynamic',
10523 style: 'display:block',
10529 cls : 'popover-inner',
10533 cls: 'popover-title',
10537 cls : 'popover-content',
10548 setTitle: function(str)
10550 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10552 setContent: function(str)
10554 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10556 // as it get's added to the bottom of the page.
10557 onRender : function(ct, position)
10559 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10561 var cfg = Roo.apply({}, this.getAutoCreate());
10565 cfg.cls += ' ' + this.cls;
10568 cfg.style = this.style;
10570 Roo.log("adding to ")
10571 this.el = Roo.get(document.body).createChild(cfg, position);
10577 initEvents : function()
10579 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10580 this.el.enableDisplayMode('block');
10582 if (this.over === false) {
10585 if (this.triggers === false) {
10588 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10589 var triggers = this.trigger ? this.trigger.split(' ') : [];
10590 Roo.each(triggers, function(trigger) {
10592 if (trigger == 'click') {
10593 on_el.on('click', this.toggle, this);
10594 } else if (trigger != 'manual') {
10595 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10596 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10598 on_el.on(eventIn ,this.enter, this);
10599 on_el.on(eventOut, this.leave, this);
10610 toggle : function () {
10611 this.hoverState == 'in' ? this.leave() : this.enter();
10614 enter : function () {
10617 clearTimeout(this.timeout);
10619 this.hoverState = 'in'
10621 if (!this.delay || !this.delay.show) {
10626 this.timeout = setTimeout(function () {
10627 if (_t.hoverState == 'in') {
10630 }, this.delay.show)
10632 leave : function() {
10633 clearTimeout(this.timeout);
10635 this.hoverState = 'out'
10637 if (!this.delay || !this.delay.hide) {
10642 this.timeout = setTimeout(function () {
10643 if (_t.hoverState == 'out') {
10646 }, this.delay.hide)
10649 show : function (on_el)
10652 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10655 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10656 if (this.html !== false) {
10657 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10659 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10660 if (!this.title.length) {
10661 this.el.select('.popover-title',true).hide();
10664 var placement = typeof this.placement == 'function' ?
10665 this.placement.call(this, this.el, on_el) :
10668 var autoToken = /\s?auto?\s?/i;
10669 var autoPlace = autoToken.test(placement);
10671 placement = placement.replace(autoToken, '') || 'top';
10675 //this.el.setXY([0,0]);
10677 this.el.dom.style.display='block';
10678 this.el.addClass(placement);
10680 //this.el.appendTo(on_el);
10682 var p = this.getPosition();
10683 var box = this.el.getBox();
10688 var align = Roo.bootstrap.Popover.alignment[placement]
10689 this.el.alignTo(on_el, align[0],align[1]);
10690 //var arrow = this.el.select('.arrow',true).first();
10691 //arrow.set(align[2],
10693 this.el.addClass('in');
10694 this.hoverState = null;
10696 if (this.el.hasClass('fade')) {
10703 this.el.setXY([0,0]);
10704 this.el.removeClass('in');
10711 Roo.bootstrap.Popover.alignment = {
10712 'left' : ['r-l', [-10,0], 'right'],
10713 'right' : ['l-r', [10,0], 'left'],
10714 'bottom' : ['t-b', [0,10], 'top'],
10715 'top' : [ 'b-t', [0,-10], 'bottom']
10726 * @class Roo.bootstrap.Progress
10727 * @extends Roo.bootstrap.Component
10728 * Bootstrap Progress class
10729 * @cfg {Boolean} striped striped of the progress bar
10730 * @cfg {Boolean} active animated of the progress bar
10734 * Create a new Progress
10735 * @param {Object} config The config object
10738 Roo.bootstrap.Progress = function(config){
10739 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10742 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10747 getAutoCreate : function(){
10755 cfg.cls += ' progress-striped';
10759 cfg.cls += ' active';
10778 * @class Roo.bootstrap.ProgressBar
10779 * @extends Roo.bootstrap.Component
10780 * Bootstrap ProgressBar class
10781 * @cfg {Number} aria_valuenow aria-value now
10782 * @cfg {Number} aria_valuemin aria-value min
10783 * @cfg {Number} aria_valuemax aria-value max
10784 * @cfg {String} label label for the progress bar
10785 * @cfg {String} panel (success | info | warning | danger )
10786 * @cfg {String} role role of the progress bar
10787 * @cfg {String} sr_only text
10791 * Create a new ProgressBar
10792 * @param {Object} config The config object
10795 Roo.bootstrap.ProgressBar = function(config){
10796 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10799 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10803 aria_valuemax : 100,
10809 getAutoCreate : function()
10814 cls: 'progress-bar',
10815 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10827 cfg.role = this.role;
10830 if(this.aria_valuenow){
10831 cfg['aria-valuenow'] = this.aria_valuenow;
10834 if(this.aria_valuemin){
10835 cfg['aria-valuemin'] = this.aria_valuemin;
10838 if(this.aria_valuemax){
10839 cfg['aria-valuemax'] = this.aria_valuemax;
10842 if(this.label && !this.sr_only){
10843 cfg.html = this.label;
10847 cfg.cls += ' progress-bar-' + this.panel;
10853 update : function(aria_valuenow)
10855 this.aria_valuenow = aria_valuenow;
10857 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10872 * @class Roo.bootstrap.TabPanel
10873 * @extends Roo.bootstrap.Component
10874 * Bootstrap TabPanel class
10875 * @cfg {Boolean} active panel active
10876 * @cfg {String} html panel content
10877 * @cfg {String} tabId tab relate id
10881 * Create a new TabPanel
10882 * @param {Object} config The config object
10885 Roo.bootstrap.TabPanel = function(config){
10886 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10889 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10895 getAutoCreate : function(){
10899 html: this.html || ''
10903 cfg.cls += ' active';
10907 cfg.tabId = this.tabId;
10925 * @class Roo.bootstrap.DateField
10926 * @extends Roo.bootstrap.Input
10927 * Bootstrap DateField class
10928 * @cfg {Number} weekStart default 0
10929 * @cfg {Number} weekStart default 0
10930 * @cfg {Number} viewMode default empty, (months|years)
10931 * @cfg {Number} minViewMode default empty, (months|years)
10932 * @cfg {Number} startDate default -Infinity
10933 * @cfg {Number} endDate default Infinity
10934 * @cfg {Boolean} todayHighlight default false
10935 * @cfg {Boolean} todayBtn default false
10936 * @cfg {Boolean} calendarWeeks default false
10937 * @cfg {Object} daysOfWeekDisabled default empty
10939 * @cfg {Boolean} keyboardNavigation default true
10940 * @cfg {String} language default en
10943 * Create a new DateField
10944 * @param {Object} config The config object
10947 Roo.bootstrap.DateField = function(config){
10948 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10952 * Fires when this field show.
10953 * @param {Roo.bootstrap.DateField} this
10954 * @param {Mixed} date The date value
10959 * Fires when this field hide.
10960 * @param {Roo.bootstrap.DateField} this
10961 * @param {Mixed} date The date value
10966 * Fires when select a date.
10967 * @param {Roo.bootstrap.DateField} this
10968 * @param {Mixed} date The date value
10974 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
10977 * @cfg {String} format
10978 * The default date format string which can be overriden for localization support. The format must be
10979 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10983 * @cfg {String} altFormats
10984 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10985 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10987 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
10995 todayHighlight : false,
11001 keyboardNavigation: true,
11003 calendarWeeks: false,
11005 startDate: -Infinity,
11009 daysOfWeekDisabled: [],
11013 UTCDate: function()
11015 return new Date(Date.UTC.apply(Date, arguments));
11018 UTCToday: function()
11020 var today = new Date();
11021 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11024 getDate: function() {
11025 var d = this.getUTCDate();
11026 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11029 getUTCDate: function() {
11033 setDate: function(d) {
11034 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11037 setUTCDate: function(d) {
11039 this.setValue(this.formatDate(this.date));
11042 onRender: function(ct, position)
11045 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11047 this.language = this.language || 'en';
11048 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11049 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11051 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11052 this.format = this.format || 'm/d/y';
11053 this.isInline = false;
11054 this.isInput = true;
11055 this.component = this.el.select('.add-on', true).first() || false;
11056 this.component = (this.component && this.component.length === 0) ? false : this.component;
11057 this.hasInput = this.component && this.inputEL().length;
11059 if (typeof(this.minViewMode === 'string')) {
11060 switch (this.minViewMode) {
11062 this.minViewMode = 1;
11065 this.minViewMode = 2;
11068 this.minViewMode = 0;
11073 if (typeof(this.viewMode === 'string')) {
11074 switch (this.viewMode) {
11087 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11089 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11091 this.picker().on('mousedown', this.onMousedown, this);
11092 this.picker().on('click', this.onClick, this);
11094 this.picker().addClass('datepicker-dropdown');
11096 this.startViewMode = this.viewMode;
11099 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11100 if(!this.calendarWeeks){
11105 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11106 v.attr('colspan', function(i, val){
11107 return parseInt(val) + 1;
11112 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11114 this.setStartDate(this.startDate);
11115 this.setEndDate(this.endDate);
11117 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11124 if(this.isInline) {
11129 picker : function()
11131 return this.el.select('.datepicker', true).first();
11134 fillDow: function()
11136 var dowCnt = this.weekStart;
11145 if(this.calendarWeeks){
11153 while (dowCnt < this.weekStart + 7) {
11157 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11161 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11164 fillMonths: function()
11167 var months = this.picker().select('>.datepicker-months td', true).first();
11169 months.dom.innerHTML = '';
11175 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11178 months.createChild(month);
11183 update: function(){
11185 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11187 if (this.date < this.startDate) {
11188 this.viewDate = new Date(this.startDate);
11189 } else if (this.date > this.endDate) {
11190 this.viewDate = new Date(this.endDate);
11192 this.viewDate = new Date(this.date);
11199 var d = new Date(this.viewDate),
11200 year = d.getUTCFullYear(),
11201 month = d.getUTCMonth(),
11202 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11203 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11204 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11205 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11206 currentDate = this.date && this.date.valueOf(),
11207 today = this.UTCToday();
11209 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11211 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11213 // this.picker.select('>tfoot th.today').
11214 // .text(dates[this.language].today)
11215 // .toggle(this.todayBtn !== false);
11217 this.updateNavArrows();
11220 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11222 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11224 prevMonth.setUTCDate(day);
11226 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11228 var nextMonth = new Date(prevMonth);
11230 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11232 nextMonth = nextMonth.valueOf();
11234 var fillMonths = false;
11236 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11238 while(prevMonth.valueOf() < nextMonth) {
11241 if (prevMonth.getUTCDay() === this.weekStart) {
11243 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11251 if(this.calendarWeeks){
11252 // ISO 8601: First week contains first thursday.
11253 // ISO also states week starts on Monday, but we can be more abstract here.
11255 // Start of current week: based on weekstart/current date
11256 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11257 // Thursday of this week
11258 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11259 // First Thursday of year, year from thursday
11260 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11261 // Calendar week: ms between thursdays, div ms per day, div 7 days
11262 calWeek = (th - yth) / 864e5 / 7 + 1;
11264 fillMonths.cn.push({
11272 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11274 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11277 if (this.todayHighlight &&
11278 prevMonth.getUTCFullYear() == today.getFullYear() &&
11279 prevMonth.getUTCMonth() == today.getMonth() &&
11280 prevMonth.getUTCDate() == today.getDate()) {
11281 clsName += ' today';
11284 if (currentDate && prevMonth.valueOf() === currentDate) {
11285 clsName += ' active';
11288 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11289 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11290 clsName += ' disabled';
11293 fillMonths.cn.push({
11295 cls: 'day ' + clsName,
11296 html: prevMonth.getDate()
11299 prevMonth.setDate(prevMonth.getDate()+1);
11302 var currentYear = this.date && this.date.getUTCFullYear();
11303 var currentMonth = this.date && this.date.getUTCMonth();
11305 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11307 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11308 v.removeClass('active');
11310 if(currentYear === year && k === currentMonth){
11311 v.addClass('active');
11314 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11315 v.addClass('disabled');
11321 year = parseInt(year/10, 10) * 10;
11323 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11325 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11328 for (var i = -1; i < 11; i++) {
11329 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11331 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11339 showMode: function(dir) {
11341 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11343 Roo.each(this.picker().select('>div',true).elements, function(v){
11344 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11347 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11352 if(this.isInline) return;
11354 this.picker().removeClass(['bottom', 'top']);
11356 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11358 * place to the top of element!
11362 this.picker().addClass('top');
11363 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11368 this.picker().addClass('bottom');
11370 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11373 parseDate : function(value){
11374 if(!value || value instanceof Date){
11377 var v = Date.parseDate(value, this.format);
11378 if (!v && this.useIso) {
11379 v = Date.parseDate(value, 'Y-m-d');
11381 if(!v && this.altFormats){
11382 if(!this.altFormatsArray){
11383 this.altFormatsArray = this.altFormats.split("|");
11385 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11386 v = Date.parseDate(value, this.altFormatsArray[i]);
11392 formatDate : function(date, fmt){
11393 return (!date || !(date instanceof Date)) ?
11394 date : date.dateFormat(fmt || this.format);
11397 onFocus : function()
11399 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11403 onBlur : function()
11405 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11411 this.picker().show();
11415 this.fireEvent('show', this, this.date);
11420 if(this.isInline) return;
11421 this.picker().hide();
11422 this.viewMode = this.startViewMode;
11425 this.fireEvent('hide', this, this.date);
11429 onMousedown: function(e){
11430 e.stopPropagation();
11431 e.preventDefault();
11434 keyup: function(e){
11435 Roo.bootstrap.DateField.superclass.keyup.call(this);
11440 setValue: function(v){
11441 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11443 this.fireEvent('select', this, this.date);
11447 fireKey: function(e){
11448 if (!this.picker().isVisible()){
11449 if (e.keyCode == 27) // allow escape to hide and re-show picker
11453 var dateChanged = false,
11455 newDate, newViewDate;
11459 e.preventDefault();
11463 if (!this.keyboardNavigation) break;
11464 dir = e.keyCode == 37 ? -1 : 1;
11467 newDate = this.moveYear(this.date, dir);
11468 newViewDate = this.moveYear(this.viewDate, dir);
11469 } else if (e.shiftKey){
11470 newDate = this.moveMonth(this.date, dir);
11471 newViewDate = this.moveMonth(this.viewDate, dir);
11473 newDate = new Date(this.date);
11474 newDate.setUTCDate(this.date.getUTCDate() + dir);
11475 newViewDate = new Date(this.viewDate);
11476 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11478 if (this.dateWithinRange(newDate)){
11479 this.date = newDate;
11480 this.viewDate = newViewDate;
11481 this.setValue(this.formatDate(this.date));
11483 e.preventDefault();
11484 dateChanged = true;
11489 if (!this.keyboardNavigation) break;
11490 dir = e.keyCode == 38 ? -1 : 1;
11492 newDate = this.moveYear(this.date, dir);
11493 newViewDate = this.moveYear(this.viewDate, dir);
11494 } else if (e.shiftKey){
11495 newDate = this.moveMonth(this.date, dir);
11496 newViewDate = this.moveMonth(this.viewDate, dir);
11498 newDate = new Date(this.date);
11499 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11500 newViewDate = new Date(this.viewDate);
11501 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11503 if (this.dateWithinRange(newDate)){
11504 this.date = newDate;
11505 this.viewDate = newViewDate;
11506 this.setValue(this.formatDate(this.date));
11508 e.preventDefault();
11509 dateChanged = true;
11513 this.setValue(this.formatDate(this.date));
11515 e.preventDefault();
11518 this.setValue(this.formatDate(this.date));
11525 onClick: function(e) {
11526 e.stopPropagation();
11527 e.preventDefault();
11529 var target = e.getTarget();
11531 if(target.nodeName.toLowerCase() === 'i'){
11532 target = Roo.get(target).dom.parentNode;
11535 var nodeName = target.nodeName;
11536 var className = target.className;
11537 var html = target.innerHTML;
11539 switch(nodeName.toLowerCase()) {
11541 switch(className) {
11547 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11548 switch(this.viewMode){
11550 this.viewDate = this.moveMonth(this.viewDate, dir);
11554 this.viewDate = this.moveYear(this.viewDate, dir);
11560 var date = new Date();
11561 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11563 this.setValue(this.formatDate(this.date));
11569 if (className.indexOf('disabled') === -1) {
11570 this.viewDate.setUTCDate(1);
11571 if (className.indexOf('month') !== -1) {
11572 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11574 var year = parseInt(html, 10) || 0;
11575 this.viewDate.setUTCFullYear(year);
11584 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11585 var day = parseInt(html, 10) || 1;
11586 var year = this.viewDate.getUTCFullYear(),
11587 month = this.viewDate.getUTCMonth();
11589 if (className.indexOf('old') !== -1) {
11596 } else if (className.indexOf('new') !== -1) {
11604 this.date = this.UTCDate(year, month, day,0,0,0,0);
11605 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11607 this.setValue(this.formatDate(this.date));
11614 setStartDate: function(startDate){
11615 this.startDate = startDate || -Infinity;
11616 if (this.startDate !== -Infinity) {
11617 this.startDate = this.parseDate(this.startDate);
11620 this.updateNavArrows();
11623 setEndDate: function(endDate){
11624 this.endDate = endDate || Infinity;
11625 if (this.endDate !== Infinity) {
11626 this.endDate = this.parseDate(this.endDate);
11629 this.updateNavArrows();
11632 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11633 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11634 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11635 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11637 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11638 return parseInt(d, 10);
11641 this.updateNavArrows();
11644 updateNavArrows: function() {
11645 var d = new Date(this.viewDate),
11646 year = d.getUTCFullYear(),
11647 month = d.getUTCMonth();
11649 Roo.each(this.picker().select('.prev', true).elements, function(v){
11651 switch (this.viewMode) {
11654 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11660 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11667 Roo.each(this.picker().select('.next', true).elements, function(v){
11669 switch (this.viewMode) {
11672 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11678 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11686 moveMonth: function(date, dir){
11687 if (!dir) return date;
11688 var new_date = new Date(date.valueOf()),
11689 day = new_date.getUTCDate(),
11690 month = new_date.getUTCMonth(),
11691 mag = Math.abs(dir),
11693 dir = dir > 0 ? 1 : -1;
11696 // If going back one month, make sure month is not current month
11697 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11699 return new_date.getUTCMonth() == month;
11701 // If going forward one month, make sure month is as expected
11702 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11704 return new_date.getUTCMonth() != new_month;
11706 new_month = month + dir;
11707 new_date.setUTCMonth(new_month);
11708 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11709 if (new_month < 0 || new_month > 11)
11710 new_month = (new_month + 12) % 12;
11712 // For magnitudes >1, move one month at a time...
11713 for (var i=0; i<mag; i++)
11714 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11715 new_date = this.moveMonth(new_date, dir);
11716 // ...then reset the day, keeping it in the new month
11717 new_month = new_date.getUTCMonth();
11718 new_date.setUTCDate(day);
11720 return new_month != new_date.getUTCMonth();
11723 // Common date-resetting loop -- if date is beyond end of month, make it
11726 new_date.setUTCDate(--day);
11727 new_date.setUTCMonth(new_month);
11732 moveYear: function(date, dir){
11733 return this.moveMonth(date, dir*12);
11736 dateWithinRange: function(date){
11737 return date >= this.startDate && date <= this.endDate;
11741 remove: function() {
11742 this.picker().remove();
11747 Roo.apply(Roo.bootstrap.DateField, {
11758 html: '<i class="icon-arrow-left"/>'
11768 html: '<i class="icon-arrow-right"/>'
11810 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11811 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11812 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11813 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11814 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11827 navFnc: 'FullYear',
11832 navFnc: 'FullYear',
11837 Roo.apply(Roo.bootstrap.DateField, {
11841 cls: 'datepicker dropdown-menu',
11845 cls: 'datepicker-days',
11849 cls: 'table-condensed',
11851 Roo.bootstrap.DateField.head,
11855 Roo.bootstrap.DateField.footer
11862 cls: 'datepicker-months',
11866 cls: 'table-condensed',
11868 Roo.bootstrap.DateField.head,
11869 Roo.bootstrap.DateField.content,
11870 Roo.bootstrap.DateField.footer
11877 cls: 'datepicker-years',
11881 cls: 'table-condensed',
11883 Roo.bootstrap.DateField.head,
11884 Roo.bootstrap.DateField.content,
11885 Roo.bootstrap.DateField.footer
11904 * @class Roo.bootstrap.TimeField
11905 * @extends Roo.bootstrap.Input
11906 * Bootstrap DateField class
11910 * Create a new TimeField
11911 * @param {Object} config The config object
11914 Roo.bootstrap.TimeField = function(config){
11915 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11919 * Fires when this field show.
11920 * @param {Roo.bootstrap.DateField} this
11921 * @param {Mixed} date The date value
11926 * Fires when this field hide.
11927 * @param {Roo.bootstrap.DateField} this
11928 * @param {Mixed} date The date value
11933 * Fires when select a date.
11934 * @param {Roo.bootstrap.DateField} this
11935 * @param {Mixed} date The date value
11941 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
11944 * @cfg {String} format
11945 * The default time format string which can be overriden for localization support. The format must be
11946 * valid according to {@link Date#parseDate} (defaults to 'H:i').
11950 onRender: function(ct, position)
11953 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11955 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11957 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11959 this.pop = this.picker().select('>.datepicker-time',true).first();
11960 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
11962 this.picker().on('mousedown', this.onMousedown, this);
11963 this.picker().on('click', this.onClick, this);
11965 this.picker().addClass('datepicker-dropdown');
11970 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11971 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11972 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11973 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11974 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11975 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11979 fireKey: function(e){
11980 if (!this.picker().isVisible()){
11981 if (e.keyCode == 27) // allow escape to hide and re-show picker
11986 e.preventDefault();
11994 this.onTogglePeriod();
11997 this.onIncrementMinutes();
12000 this.onDecrementMinutes();
12009 onClick: function(e) {
12010 e.stopPropagation();
12011 e.preventDefault();
12014 picker : function()
12016 return this.el.select('.datepicker', true).first();
12019 fillTime: function()
12021 var time = this.pop.select('tbody', true).first();
12023 time.dom.innerHTML = '';
12038 cls: 'hours-up glyphicon glyphicon-chevron-up'
12058 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12079 cls: 'timepicker-hour',
12094 cls: 'timepicker-minute',
12109 cls: 'btn btn-primary period',
12131 cls: 'hours-down glyphicon glyphicon-chevron-down'
12151 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12169 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12176 var hours = this.time.getHours();
12177 var minutes = this.time.getMinutes();
12190 hours = hours - 12;
12194 hours = '0' + hours;
12198 minutes = '0' + minutes;
12201 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12202 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12203 this.pop.select('button', true).first().dom.innerHTML = period;
12209 this.picker().removeClass(['bottom', 'top']);
12211 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12213 * place to the top of element!
12217 this.picker().addClass('top');
12218 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12223 this.picker().addClass('bottom');
12225 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12228 onFocus : function()
12230 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12234 onBlur : function()
12236 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12242 this.picker().show();
12247 this.fireEvent('show', this, this.date);
12252 this.picker().hide();
12255 this.fireEvent('hide', this, this.date);
12258 setTime : function()
12261 this.setValue(this.time.format(this.format));
12263 this.fireEvent('select', this, this.date);
12268 onMousedown: function(e){
12269 e.stopPropagation();
12270 e.preventDefault();
12273 onIncrementHours: function()
12275 Roo.log('onIncrementHours');
12276 this.time = this.time.add(Date.HOUR, 1);
12281 onDecrementHours: function()
12283 Roo.log('onDecrementHours');
12284 this.time = this.time.add(Date.HOUR, -1);
12288 onIncrementMinutes: function()
12290 Roo.log('onIncrementMinutes');
12291 this.time = this.time.add(Date.MINUTE, 1);
12295 onDecrementMinutes: function()
12297 Roo.log('onDecrementMinutes');
12298 this.time = this.time.add(Date.MINUTE, -1);
12302 onTogglePeriod: function()
12304 Roo.log('onTogglePeriod');
12305 this.time = this.time.add(Date.HOUR, 12);
12312 Roo.apply(Roo.bootstrap.TimeField, {
12342 cls: 'btn btn-info ok',
12354 Roo.apply(Roo.bootstrap.TimeField, {
12358 cls: 'datepicker dropdown-menu',
12362 cls: 'datepicker-time',
12366 cls: 'table-condensed',
12368 Roo.bootstrap.TimeField.content,
12369 Roo.bootstrap.TimeField.footer
12388 * @class Roo.bootstrap.CheckBox
12389 * @extends Roo.bootstrap.Input
12390 * Bootstrap CheckBox class
12392 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12393 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12394 * @cfg {String} boxLabel The text that appears beside the checkbox
12395 * @cfg {Boolean} checked initnal the element
12398 * Create a new CheckBox
12399 * @param {Object} config The config object
12402 Roo.bootstrap.CheckBox = function(config){
12403 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12408 * Fires when the element is checked or unchecked.
12409 * @param {Roo.bootstrap.CheckBox} this This input
12410 * @param {Boolean} checked The new checked value
12416 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12418 inputType: 'checkbox',
12424 getAutoCreate : function()
12426 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12432 cfg.cls = 'form-group' //input-group
12437 type : this.inputType,
12438 value : (!this.checked) ? this.valueOff : this.inputValue,
12440 placeholder : this.placeholder || ''
12444 if (this.disabled) {
12445 input.disabled=true;
12449 input.checked = this.checked;
12453 input.name = this.name;
12457 input.cls += ' input-' + this.size;
12461 ['xs','sm','md','lg'].map(function(size){
12462 if (settings[size]) {
12463 cfg.cls += ' col-' + size + '-' + settings[size];
12467 var inputblock = input;
12469 if (this.before || this.after) {
12472 cls : 'input-group',
12476 inputblock.cn.push({
12478 cls : 'input-group-addon',
12482 inputblock.cn.push(input);
12484 inputblock.cn.push({
12486 cls : 'input-group-addon',
12493 if (align ==='left' && this.fieldLabel.length) {
12494 Roo.log("left and has label");
12500 cls : 'control-label col-md-' + this.labelWidth,
12501 html : this.fieldLabel
12505 cls : "col-md-" + (12 - this.labelWidth),
12512 } else if ( this.fieldLabel.length) {
12517 tag: this.boxLabel ? 'span' : 'label',
12519 cls: 'control-label box-input-label',
12520 //cls : 'input-group-addon',
12521 html : this.fieldLabel
12531 Roo.log(" no label && no align");
12546 html: this.boxLabel
12555 * return the real input element.
12557 inputEl: function ()
12559 return this.el.select('input.form-box',true).first();
12562 initEvents : function()
12564 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12566 this.inputEl().on('click', this.onClick, this);
12570 onClick : function()
12572 this.setChecked(!this.checked);
12575 setChecked : function(state,suppressEvent)
12577 this.checked = state;
12579 this.inputEl().dom.checked = state;
12581 if(suppressEvent !== true){
12582 this.fireEvent('check', this, state);
12585 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12589 setValue : function(v,suppressEvent)
12591 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12605 * @class Roo.bootstrap.Radio
12606 * @extends Roo.bootstrap.CheckBox
12607 * Bootstrap Radio class
12610 * Create a new Radio
12611 * @param {Object} config The config object
12614 Roo.bootstrap.Radio = function(config){
12615 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12619 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12621 inputType: 'radio',
12625 getAutoCreate : function()
12627 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12633 cfg.cls = 'form-group' //input-group
12638 type : this.inputType,
12639 value : (!this.checked) ? this.valueOff : this.inputValue,
12641 placeholder : this.placeholder || ''
12645 if (this.disabled) {
12646 input.disabled=true;
12650 input.checked = this.checked;
12654 input.name = this.name;
12658 input.cls += ' input-' + this.size;
12662 ['xs','sm','md','lg'].map(function(size){
12663 if (settings[size]) {
12664 cfg.cls += ' col-' + size + '-' + settings[size];
12668 var inputblock = input;
12670 if (this.before || this.after) {
12673 cls : 'input-group',
12677 inputblock.cn.push({
12679 cls : 'input-group-addon',
12683 inputblock.cn.push(input);
12685 inputblock.cn.push({
12687 cls : 'input-group-addon',
12694 if (align ==='left' && this.fieldLabel.length) {
12695 Roo.log("left and has label");
12701 cls : 'control-label col-md-' + this.labelWidth,
12702 html : this.fieldLabel
12706 cls : "col-md-" + (12 - this.labelWidth),
12713 } else if ( this.fieldLabel.length) {
12720 cls: 'control-label box-input-label',
12721 //cls : 'input-group-addon',
12722 html : this.fieldLabel
12732 Roo.log(" no label && no align");
12747 html: this.boxLabel
12755 onClick : function()
12757 this.setChecked(true);
12760 setChecked : function(state,suppressEvent)
12763 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12764 v.dom.checked = false;
12768 this.checked = state;
12769 this.inputEl().dom.checked = state;
12771 if(suppressEvent !== true){
12772 this.fireEvent('check', this, state);
12775 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12779 getGroupValue : function()
12782 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12783 if(v.dom.checked == true){
12784 value = v.dom.value;
12792 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12793 * @return {Mixed} value The field value
12795 getValue : function(){
12796 return this.getGroupValue();
12802 //<script type="text/javascript">
12805 * Based Ext JS Library 1.1.1
12806 * Copyright(c) 2006-2007, Ext JS, LLC.
12812 * @class Roo.HtmlEditorCore
12813 * @extends Roo.Component
12814 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12816 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12819 Roo.HtmlEditorCore = function(config){
12822 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12825 * @event initialize
12826 * Fires when the editor is fully initialized (including the iframe)
12827 * @param {Roo.HtmlEditorCore} this
12832 * Fires when the editor is first receives the focus. Any insertion must wait
12833 * until after this event.
12834 * @param {Roo.HtmlEditorCore} this
12838 * @event beforesync
12839 * Fires before the textarea is updated with content from the editor iframe. Return false
12840 * to cancel the sync.
12841 * @param {Roo.HtmlEditorCore} this
12842 * @param {String} html
12846 * @event beforepush
12847 * Fires before the iframe editor is updated with content from the textarea. Return false
12848 * to cancel the push.
12849 * @param {Roo.HtmlEditorCore} this
12850 * @param {String} html
12855 * Fires when the textarea is updated with content from the editor iframe.
12856 * @param {Roo.HtmlEditorCore} this
12857 * @param {String} html
12862 * Fires when the iframe editor is updated with content from the textarea.
12863 * @param {Roo.HtmlEditorCore} this
12864 * @param {String} html
12869 * @event editorevent
12870 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12871 * @param {Roo.HtmlEditorCore} this
12879 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12883 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12889 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12894 * @cfg {Number} height (in pixels)
12898 * @cfg {Number} width (in pixels)
12903 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12906 stylesheets: false,
12911 // private properties
12912 validationEvent : false,
12914 initialized : false,
12916 sourceEditMode : false,
12917 onFocus : Roo.emptyFn,
12919 hideMode:'offsets',
12925 * Protected method that will not generally be called directly. It
12926 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12927 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12929 getDocMarkup : function(){
12932 Roo.log(this.stylesheets);
12934 // inherit styels from page...??
12935 if (this.stylesheets === false) {
12937 Roo.get(document.head).select('style').each(function(node) {
12938 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12941 Roo.get(document.head).select('link').each(function(node) {
12942 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12945 } else if (!this.stylesheets.length) {
12947 st = '<style type="text/css">' +
12948 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12951 Roo.each(this.stylesheets, function(s) {
12952 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12957 st += '<style type="text/css">' +
12958 'IMG { cursor: pointer } ' +
12962 return '<html><head>' + st +
12963 //<style type="text/css">' +
12964 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12966 ' </head><body class="roo-htmleditor-body"></body></html>';
12970 onRender : function(ct, position)
12973 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12974 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12977 this.el.dom.style.border = '0 none';
12978 this.el.dom.setAttribute('tabIndex', -1);
12979 this.el.addClass('x-hidden hide');
12983 if(Roo.isIE){ // fix IE 1px bogus margin
12984 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
12988 this.frameId = Roo.id();
12992 var iframe = this.owner.wrap.createChild({
12994 cls: 'form-control', // bootstrap..
12996 name: this.frameId,
12997 frameBorder : 'no',
12998 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13003 this.iframe = iframe.dom;
13005 this.assignDocWin();
13007 this.doc.designMode = 'on';
13010 this.doc.write(this.getDocMarkup());
13014 var task = { // must defer to wait for browser to be ready
13016 //console.log("run task?" + this.doc.readyState);
13017 this.assignDocWin();
13018 if(this.doc.body || this.doc.readyState == 'complete'){
13020 this.doc.designMode="on";
13024 Roo.TaskMgr.stop(task);
13025 this.initEditor.defer(10, this);
13032 Roo.TaskMgr.start(task);
13039 onResize : function(w, h)
13041 Roo.log('resize: ' +w + ',' + h );
13042 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13046 if(typeof w == 'number'){
13048 this.iframe.style.width = w + 'px';
13050 if(typeof h == 'number'){
13052 this.iframe.style.height = h + 'px';
13054 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13061 * Toggles the editor between standard and source edit mode.
13062 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13064 toggleSourceEdit : function(sourceEditMode){
13066 this.sourceEditMode = sourceEditMode === true;
13068 if(this.sourceEditMode){
13070 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13073 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13074 //this.iframe.className = '';
13077 //this.setSize(this.owner.wrap.getSize());
13078 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13085 * Protected method that will not generally be called directly. If you need/want
13086 * custom HTML cleanup, this is the method you should override.
13087 * @param {String} html The HTML to be cleaned
13088 * return {String} The cleaned HTML
13090 cleanHtml : function(html){
13091 html = String(html);
13092 if(html.length > 5){
13093 if(Roo.isSafari){ // strip safari nonsense
13094 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13097 if(html == ' '){
13104 * HTML Editor -> Textarea
13105 * Protected method that will not generally be called directly. Syncs the contents
13106 * of the editor iframe with the textarea.
13108 syncValue : function(){
13109 if(this.initialized){
13110 var bd = (this.doc.body || this.doc.documentElement);
13111 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13112 var html = bd.innerHTML;
13114 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13115 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13117 html = '<div style="'+m[0]+'">' + html + '</div>';
13120 html = this.cleanHtml(html);
13121 // fix up the special chars.. normaly like back quotes in word...
13122 // however we do not want to do this with chinese..
13123 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13124 var cc = b.charCodeAt();
13126 (cc >= 0x4E00 && cc < 0xA000 ) ||
13127 (cc >= 0x3400 && cc < 0x4E00 ) ||
13128 (cc >= 0xf900 && cc < 0xfb00 )
13134 if(this.owner.fireEvent('beforesync', this, html) !== false){
13135 this.el.dom.value = html;
13136 this.owner.fireEvent('sync', this, html);
13142 * Protected method that will not generally be called directly. Pushes the value of the textarea
13143 * into the iframe editor.
13145 pushValue : function(){
13146 if(this.initialized){
13147 var v = this.el.dom.value.trim();
13149 // if(v.length < 1){
13153 if(this.owner.fireEvent('beforepush', this, v) !== false){
13154 var d = (this.doc.body || this.doc.documentElement);
13156 this.cleanUpPaste();
13157 this.el.dom.value = d.innerHTML;
13158 this.owner.fireEvent('push', this, v);
13164 deferFocus : function(){
13165 this.focus.defer(10, this);
13169 focus : function(){
13170 if(this.win && !this.sourceEditMode){
13177 assignDocWin: function()
13179 var iframe = this.iframe;
13182 this.doc = iframe.contentWindow.document;
13183 this.win = iframe.contentWindow;
13185 if (!Roo.get(this.frameId)) {
13188 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13189 this.win = Roo.get(this.frameId).dom.contentWindow;
13194 initEditor : function(){
13195 //console.log("INIT EDITOR");
13196 this.assignDocWin();
13200 this.doc.designMode="on";
13202 this.doc.write(this.getDocMarkup());
13205 var dbody = (this.doc.body || this.doc.documentElement);
13206 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13207 // this copies styles from the containing element into thsi one..
13208 // not sure why we need all of this..
13209 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13210 ss['background-attachment'] = 'fixed'; // w3c
13211 dbody.bgProperties = 'fixed'; // ie
13212 Roo.DomHelper.applyStyles(dbody, ss);
13213 Roo.EventManager.on(this.doc, {
13214 //'mousedown': this.onEditorEvent,
13215 'mouseup': this.onEditorEvent,
13216 'dblclick': this.onEditorEvent,
13217 'click': this.onEditorEvent,
13218 'keyup': this.onEditorEvent,
13223 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13225 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13226 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13228 this.initialized = true;
13230 this.owner.fireEvent('initialize', this);
13235 onDestroy : function(){
13241 //for (var i =0; i < this.toolbars.length;i++) {
13242 // // fixme - ask toolbars for heights?
13243 // this.toolbars[i].onDestroy();
13246 //this.wrap.dom.innerHTML = '';
13247 //this.wrap.remove();
13252 onFirstFocus : function(){
13254 this.assignDocWin();
13257 this.activated = true;
13260 if(Roo.isGecko){ // prevent silly gecko errors
13262 var s = this.win.getSelection();
13263 if(!s.focusNode || s.focusNode.nodeType != 3){
13264 var r = s.getRangeAt(0);
13265 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13270 this.execCmd('useCSS', true);
13271 this.execCmd('styleWithCSS', false);
13274 this.owner.fireEvent('activate', this);
13278 adjustFont: function(btn){
13279 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13280 //if(Roo.isSafari){ // safari
13283 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13284 if(Roo.isSafari){ // safari
13285 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13286 v = (v < 10) ? 10 : v;
13287 v = (v > 48) ? 48 : v;
13288 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13293 v = Math.max(1, v+adjust);
13295 this.execCmd('FontSize', v );
13298 onEditorEvent : function(e){
13299 this.owner.fireEvent('editorevent', this, e);
13300 // this.updateToolbar();
13301 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13304 insertTag : function(tg)
13306 // could be a bit smarter... -> wrap the current selected tRoo..
13307 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13309 range = this.createRange(this.getSelection());
13310 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13311 wrappingNode.appendChild(range.extractContents());
13312 range.insertNode(wrappingNode);
13319 this.execCmd("formatblock", tg);
13323 insertText : function(txt)
13327 var range = this.createRange();
13328 range.deleteContents();
13329 //alert(Sender.getAttribute('label'));
13331 range.insertNode(this.doc.createTextNode(txt));
13337 * Executes a Midas editor command on the editor document and performs necessary focus and
13338 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13339 * @param {String} cmd The Midas command
13340 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13342 relayCmd : function(cmd, value){
13344 this.execCmd(cmd, value);
13345 this.owner.fireEvent('editorevent', this);
13346 //this.updateToolbar();
13347 this.owner.deferFocus();
13351 * Executes a Midas editor command directly on the editor document.
13352 * For visual commands, you should use {@link #relayCmd} instead.
13353 * <b>This should only be called after the editor is initialized.</b>
13354 * @param {String} cmd The Midas command
13355 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13357 execCmd : function(cmd, value){
13358 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13365 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13367 * @param {String} text | dom node..
13369 insertAtCursor : function(text)
13374 if(!this.activated){
13380 var r = this.doc.selection.createRange();
13391 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13395 // from jquery ui (MIT licenced)
13397 var win = this.win;
13399 if (win.getSelection && win.getSelection().getRangeAt) {
13400 range = win.getSelection().getRangeAt(0);
13401 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13402 range.insertNode(node);
13403 } else if (win.document.selection && win.document.selection.createRange) {
13404 // no firefox support
13405 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13406 win.document.selection.createRange().pasteHTML(txt);
13408 // no firefox support
13409 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13410 this.execCmd('InsertHTML', txt);
13419 mozKeyPress : function(e){
13421 var c = e.getCharCode(), cmd;
13424 c = String.fromCharCode(c).toLowerCase();
13438 this.cleanUpPaste.defer(100, this);
13446 e.preventDefault();
13454 fixKeys : function(){ // load time branching for fastest keydown performance
13456 return function(e){
13457 var k = e.getKey(), r;
13460 r = this.doc.selection.createRange();
13463 r.pasteHTML('    ');
13470 r = this.doc.selection.createRange();
13472 var target = r.parentElement();
13473 if(!target || target.tagName.toLowerCase() != 'li'){
13475 r.pasteHTML('<br />');
13481 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13482 this.cleanUpPaste.defer(100, this);
13488 }else if(Roo.isOpera){
13489 return function(e){
13490 var k = e.getKey();
13494 this.execCmd('InsertHTML','    ');
13497 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13498 this.cleanUpPaste.defer(100, this);
13503 }else if(Roo.isSafari){
13504 return function(e){
13505 var k = e.getKey();
13509 this.execCmd('InsertText','\t');
13513 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13514 this.cleanUpPaste.defer(100, this);
13522 getAllAncestors: function()
13524 var p = this.getSelectedNode();
13527 a.push(p); // push blank onto stack..
13528 p = this.getParentElement();
13532 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13536 a.push(this.doc.body);
13540 lastSelNode : false,
13543 getSelection : function()
13545 this.assignDocWin();
13546 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13549 getSelectedNode: function()
13551 // this may only work on Gecko!!!
13553 // should we cache this!!!!
13558 var range = this.createRange(this.getSelection()).cloneRange();
13561 var parent = range.parentElement();
13563 var testRange = range.duplicate();
13564 testRange.moveToElementText(parent);
13565 if (testRange.inRange(range)) {
13568 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13571 parent = parent.parentElement;
13576 // is ancestor a text element.
13577 var ac = range.commonAncestorContainer;
13578 if (ac.nodeType == 3) {
13579 ac = ac.parentNode;
13582 var ar = ac.childNodes;
13585 var other_nodes = [];
13586 var has_other_nodes = false;
13587 for (var i=0;i<ar.length;i++) {
13588 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13591 // fullly contained node.
13593 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13598 // probably selected..
13599 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13600 other_nodes.push(ar[i]);
13604 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13609 has_other_nodes = true;
13611 if (!nodes.length && other_nodes.length) {
13612 nodes= other_nodes;
13614 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13620 createRange: function(sel)
13622 // this has strange effects when using with
13623 // top toolbar - not sure if it's a great idea.
13624 //this.editor.contentWindow.focus();
13625 if (typeof sel != "undefined") {
13627 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13629 return this.doc.createRange();
13632 return this.doc.createRange();
13635 getParentElement: function()
13638 this.assignDocWin();
13639 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13641 var range = this.createRange(sel);
13644 var p = range.commonAncestorContainer;
13645 while (p.nodeType == 3) { // text node
13656 * Range intersection.. the hard stuff...
13660 * [ -- selected range --- ]
13664 * if end is before start or hits it. fail.
13665 * if start is after end or hits it fail.
13667 * if either hits (but other is outside. - then it's not
13673 // @see http://www.thismuchiknow.co.uk/?p=64.
13674 rangeIntersectsNode : function(range, node)
13676 var nodeRange = node.ownerDocument.createRange();
13678 nodeRange.selectNode(node);
13680 nodeRange.selectNodeContents(node);
13683 var rangeStartRange = range.cloneRange();
13684 rangeStartRange.collapse(true);
13686 var rangeEndRange = range.cloneRange();
13687 rangeEndRange.collapse(false);
13689 var nodeStartRange = nodeRange.cloneRange();
13690 nodeStartRange.collapse(true);
13692 var nodeEndRange = nodeRange.cloneRange();
13693 nodeEndRange.collapse(false);
13695 return rangeStartRange.compareBoundaryPoints(
13696 Range.START_TO_START, nodeEndRange) == -1 &&
13697 rangeEndRange.compareBoundaryPoints(
13698 Range.START_TO_START, nodeStartRange) == 1;
13702 rangeCompareNode : function(range, node)
13704 var nodeRange = node.ownerDocument.createRange();
13706 nodeRange.selectNode(node);
13708 nodeRange.selectNodeContents(node);
13712 range.collapse(true);
13714 nodeRange.collapse(true);
13716 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13717 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13719 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13721 var nodeIsBefore = ss == 1;
13722 var nodeIsAfter = ee == -1;
13724 if (nodeIsBefore && nodeIsAfter)
13726 if (!nodeIsBefore && nodeIsAfter)
13727 return 1; //right trailed.
13729 if (nodeIsBefore && !nodeIsAfter)
13730 return 2; // left trailed.
13735 // private? - in a new class?
13736 cleanUpPaste : function()
13738 // cleans up the whole document..
13739 Roo.log('cleanuppaste');
13740 this.cleanUpChildren(this.doc.body);
13741 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13742 if (clean != this.doc.body.innerHTML) {
13743 this.doc.body.innerHTML = clean;
13748 cleanWordChars : function(input) {// change the chars to hex code
13749 var he = Roo.HtmlEditorCore;
13751 var output = input;
13752 Roo.each(he.swapCodes, function(sw) {
13753 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13755 output = output.replace(swapper, sw[1]);
13762 cleanUpChildren : function (n)
13764 if (!n.childNodes.length) {
13767 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13768 this.cleanUpChild(n.childNodes[i]);
13775 cleanUpChild : function (node)
13778 //console.log(node);
13779 if (node.nodeName == "#text") {
13780 // clean up silly Windows -- stuff?
13783 if (node.nodeName == "#comment") {
13784 node.parentNode.removeChild(node);
13785 // clean up silly Windows -- stuff?
13789 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
13791 node.parentNode.removeChild(node);
13796 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13798 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13799 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13801 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13802 // remove_keep_children = true;
13805 if (remove_keep_children) {
13806 this.cleanUpChildren(node);
13807 // inserts everything just before this node...
13808 while (node.childNodes.length) {
13809 var cn = node.childNodes[0];
13810 node.removeChild(cn);
13811 node.parentNode.insertBefore(cn, node);
13813 node.parentNode.removeChild(node);
13817 if (!node.attributes || !node.attributes.length) {
13818 this.cleanUpChildren(node);
13822 function cleanAttr(n,v)
13825 if (v.match(/^\./) || v.match(/^\//)) {
13828 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13831 if (v.match(/^#/)) {
13834 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13835 node.removeAttribute(n);
13839 function cleanStyle(n,v)
13841 if (v.match(/expression/)) { //XSS?? should we even bother..
13842 node.removeAttribute(n);
13845 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13846 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13849 var parts = v.split(/;/);
13852 Roo.each(parts, function(p) {
13853 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13857 var l = p.split(':').shift().replace(/\s+/g,'');
13858 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13861 if ( cblack.indexOf(l) > -1) {
13862 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13863 //node.removeAttribute(n);
13867 // only allow 'c whitelisted system attributes'
13868 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13869 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13870 //node.removeAttribute(n);
13880 if (clean.length) {
13881 node.setAttribute(n, clean.join(';'));
13883 node.removeAttribute(n);
13889 for (var i = node.attributes.length-1; i > -1 ; i--) {
13890 var a = node.attributes[i];
13893 if (a.name.toLowerCase().substr(0,2)=='on') {
13894 node.removeAttribute(a.name);
13897 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13898 node.removeAttribute(a.name);
13901 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13902 cleanAttr(a.name,a.value); // fixme..
13905 if (a.name == 'style') {
13906 cleanStyle(a.name,a.value);
13909 /// clean up MS crap..
13910 // tecnically this should be a list of valid class'es..
13913 if (a.name == 'class') {
13914 if (a.value.match(/^Mso/)) {
13915 node.className = '';
13918 if (a.value.match(/body/)) {
13919 node.className = '';
13930 this.cleanUpChildren(node);
13936 // hide stuff that is not compatible
13950 * @event specialkey
13954 * @cfg {String} fieldClass @hide
13957 * @cfg {String} focusClass @hide
13960 * @cfg {String} autoCreate @hide
13963 * @cfg {String} inputType @hide
13966 * @cfg {String} invalidClass @hide
13969 * @cfg {String} invalidText @hide
13972 * @cfg {String} msgFx @hide
13975 * @cfg {String} validateOnBlur @hide
13979 Roo.HtmlEditorCore.white = [
13980 'area', 'br', 'img', 'input', 'hr', 'wbr',
13982 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
13983 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
13984 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
13985 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
13986 'table', 'ul', 'xmp',
13988 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
13991 'dir', 'menu', 'ol', 'ul', 'dl',
13997 Roo.HtmlEditorCore.black = [
13998 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14000 'base', 'basefont', 'bgsound', 'blink', 'body',
14001 'frame', 'frameset', 'head', 'html', 'ilayer',
14002 'iframe', 'layer', 'link', 'meta', 'object',
14003 'script', 'style' ,'title', 'xml' // clean later..
14005 Roo.HtmlEditorCore.clean = [
14006 'script', 'style', 'title', 'xml'
14008 Roo.HtmlEditorCore.remove = [
14013 Roo.HtmlEditorCore.ablack = [
14017 Roo.HtmlEditorCore.aclean = [
14018 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14022 Roo.HtmlEditorCore.pwhite= [
14023 'http', 'https', 'mailto'
14026 // white listed style attributes.
14027 Roo.HtmlEditorCore.cwhite= [
14028 // 'text-align', /// default is to allow most things..
14034 // black listed style attributes.
14035 Roo.HtmlEditorCore.cblack= [
14036 // 'font-size' -- this can be set by the project
14040 Roo.HtmlEditorCore.swapCodes =[
14059 * @class Roo.bootstrap.HtmlEditor
14060 * @extends Roo.bootstrap.TextArea
14061 * Bootstrap HtmlEditor class
14064 * Create a new HtmlEditor
14065 * @param {Object} config The config object
14068 Roo.bootstrap.HtmlEditor = function(config){
14069 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14070 if (!this.toolbars) {
14071 this.toolbars = [];
14073 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14076 * @event initialize
14077 * Fires when the editor is fully initialized (including the iframe)
14078 * @param {HtmlEditor} this
14083 * Fires when the editor is first receives the focus. Any insertion must wait
14084 * until after this event.
14085 * @param {HtmlEditor} this
14089 * @event beforesync
14090 * Fires before the textarea is updated with content from the editor iframe. Return false
14091 * to cancel the sync.
14092 * @param {HtmlEditor} this
14093 * @param {String} html
14097 * @event beforepush
14098 * Fires before the iframe editor is updated with content from the textarea. Return false
14099 * to cancel the push.
14100 * @param {HtmlEditor} this
14101 * @param {String} html
14106 * Fires when the textarea is updated with content from the editor iframe.
14107 * @param {HtmlEditor} this
14108 * @param {String} html
14113 * Fires when the iframe editor is updated with content from the textarea.
14114 * @param {HtmlEditor} this
14115 * @param {String} html
14119 * @event editmodechange
14120 * Fires when the editor switches edit modes
14121 * @param {HtmlEditor} this
14122 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14124 editmodechange: true,
14126 * @event editorevent
14127 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14128 * @param {HtmlEditor} this
14132 * @event firstfocus
14133 * Fires when on first focus - needed by toolbars..
14134 * @param {HtmlEditor} this
14139 * Auto save the htmlEditor value as a file into Events
14140 * @param {HtmlEditor} this
14144 * @event savedpreview
14145 * preview the saved version of htmlEditor
14146 * @param {HtmlEditor} this
14153 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14157 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14162 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14167 * @cfg {Number} height (in pixels)
14171 * @cfg {Number} width (in pixels)
14176 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14179 stylesheets: false,
14184 // private properties
14185 validationEvent : false,
14187 initialized : false,
14190 onFocus : Roo.emptyFn,
14192 hideMode:'offsets',
14195 tbContainer : false,
14197 toolbarContainer :function() {
14198 return this.wrap.select('.x-html-editor-tb',true).first();
14202 * Protected method that will not generally be called directly. It
14203 * is called when the editor creates its toolbar. Override this method if you need to
14204 * add custom toolbar buttons.
14205 * @param {HtmlEditor} editor
14207 createToolbar : function(){
14209 Roo.log("create toolbars");
14211 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14212 this.toolbars[0].render(this.toolbarContainer());
14216 // if (!editor.toolbars || !editor.toolbars.length) {
14217 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14220 // for (var i =0 ; i < editor.toolbars.length;i++) {
14221 // editor.toolbars[i] = Roo.factory(
14222 // typeof(editor.toolbars[i]) == 'string' ?
14223 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14224 // Roo.bootstrap.HtmlEditor);
14225 // editor.toolbars[i].init(editor);
14231 onRender : function(ct, position)
14233 // Roo.log("Call onRender: " + this.xtype);
14235 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14237 this.wrap = this.inputEl().wrap({
14238 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14241 this.editorcore.onRender(ct, position);
14243 if (this.resizable) {
14244 this.resizeEl = new Roo.Resizable(this.wrap, {
14248 minHeight : this.height,
14249 height: this.height,
14250 handles : this.resizable,
14253 resize : function(r, w, h) {
14254 _t.onResize(w,h); // -something
14260 this.createToolbar(this);
14263 if(!this.width && this.resizable){
14264 this.setSize(this.wrap.getSize());
14266 if (this.resizeEl) {
14267 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14268 // should trigger onReize..
14274 onResize : function(w, h)
14276 Roo.log('resize: ' +w + ',' + h );
14277 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14281 if(this.inputEl() ){
14282 if(typeof w == 'number'){
14283 var aw = w - this.wrap.getFrameWidth('lr');
14284 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14287 if(typeof h == 'number'){
14288 var tbh = -11; // fixme it needs to tool bar size!
14289 for (var i =0; i < this.toolbars.length;i++) {
14290 // fixme - ask toolbars for heights?
14291 tbh += this.toolbars[i].el.getHeight();
14292 //if (this.toolbars[i].footer) {
14293 // tbh += this.toolbars[i].footer.el.getHeight();
14301 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14302 ah -= 5; // knock a few pixes off for look..
14303 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14307 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14308 this.editorcore.onResize(ew,eh);
14313 * Toggles the editor between standard and source edit mode.
14314 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14316 toggleSourceEdit : function(sourceEditMode)
14318 this.editorcore.toggleSourceEdit(sourceEditMode);
14320 if(this.editorcore.sourceEditMode){
14321 Roo.log('editor - showing textarea');
14324 // Roo.log(this.syncValue());
14326 this.inputEl().removeClass('hide');
14327 this.inputEl().dom.removeAttribute('tabIndex');
14328 this.inputEl().focus();
14330 Roo.log('editor - hiding textarea');
14332 // Roo.log(this.pushValue());
14335 this.inputEl().addClass('hide');
14336 this.inputEl().dom.setAttribute('tabIndex', -1);
14337 //this.deferFocus();
14340 if(this.resizable){
14341 this.setSize(this.wrap.getSize());
14344 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14347 // private (for BoxComponent)
14348 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14350 // private (for BoxComponent)
14351 getResizeEl : function(){
14355 // private (for BoxComponent)
14356 getPositionEl : function(){
14361 initEvents : function(){
14362 this.originalValue = this.getValue();
14366 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14369 // markInvalid : Roo.emptyFn,
14371 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14374 // clearInvalid : Roo.emptyFn,
14376 setValue : function(v){
14377 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14378 this.editorcore.pushValue();
14383 deferFocus : function(){
14384 this.focus.defer(10, this);
14388 focus : function(){
14389 this.editorcore.focus();
14395 onDestroy : function(){
14401 for (var i =0; i < this.toolbars.length;i++) {
14402 // fixme - ask toolbars for heights?
14403 this.toolbars[i].onDestroy();
14406 this.wrap.dom.innerHTML = '';
14407 this.wrap.remove();
14412 onFirstFocus : function(){
14413 //Roo.log("onFirstFocus");
14414 this.editorcore.onFirstFocus();
14415 for (var i =0; i < this.toolbars.length;i++) {
14416 this.toolbars[i].onFirstFocus();
14422 syncValue : function()
14424 this.editorcore.syncValue();
14427 pushValue : function()
14429 this.editorcore.pushValue();
14433 // hide stuff that is not compatible
14447 * @event specialkey
14451 * @cfg {String} fieldClass @hide
14454 * @cfg {String} focusClass @hide
14457 * @cfg {String} autoCreate @hide
14460 * @cfg {String} inputType @hide
14463 * @cfg {String} invalidClass @hide
14466 * @cfg {String} invalidText @hide
14469 * @cfg {String} msgFx @hide
14472 * @cfg {String} validateOnBlur @hide
14483 * @class Roo.bootstrap.HtmlEditorToolbar1
14488 new Roo.bootstrap.HtmlEditor({
14491 new Roo.bootstrap.HtmlEditorToolbar1({
14492 disable : { fonts: 1 , format: 1, ..., ... , ...],
14498 * @cfg {Object} disable List of elements to disable..
14499 * @cfg {Array} btns List of additional buttons.
14503 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14506 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14509 Roo.apply(this, config);
14511 // default disabled, based on 'good practice'..
14512 this.disable = this.disable || {};
14513 Roo.applyIf(this.disable, {
14516 specialElements : true
14518 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14520 this.editor = config.editor;
14521 this.editorcore = config.editor.editorcore;
14523 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14525 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14526 // dont call parent... till later.
14528 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14534 editorcore : false,
14539 "h1","h2","h3","h4","h5","h6",
14541 "abbr", "acronym", "address", "cite", "samp", "var",
14545 onRender : function(ct, position)
14547 // Roo.log("Call onRender: " + this.xtype);
14549 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14551 this.el.dom.style.marginBottom = '0';
14553 var editorcore = this.editorcore;
14554 var editor= this.editor;
14557 var btn = function(id,cmd , toggle, handler){
14559 var event = toggle ? 'toggle' : 'click';
14564 xns: Roo.bootstrap,
14567 enableToggle:toggle !== false,
14569 pressed : toggle ? false : null,
14572 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14573 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14582 xns: Roo.bootstrap,
14583 glyphicon : 'font',
14587 xns: Roo.bootstrap,
14591 Roo.each(this.formats, function(f) {
14592 style.menu.items.push({
14594 xns: Roo.bootstrap,
14595 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14600 editorcore.insertTag(this.tagname);
14607 children.push(style);
14610 btn('bold',false,true);
14611 btn('italic',false,true);
14612 btn('align-left', 'justifyleft',true);
14613 btn('align-center', 'justifycenter',true);
14614 btn('align-right' , 'justifyright',true);
14615 btn('link', false, false, function(btn) {
14616 //Roo.log("create link?");
14617 var url = prompt(this.createLinkText, this.defaultLinkValue);
14618 if(url && url != 'http:/'+'/'){
14619 this.editorcore.relayCmd('createlink', url);
14622 btn('list','insertunorderedlist',true);
14623 btn('pencil', false,true, function(btn){
14626 this.toggleSourceEdit(btn.pressed);
14632 xns: Roo.bootstrap,
14637 xns: Roo.bootstrap,
14642 cog.menu.items.push({
14644 xns: Roo.bootstrap,
14645 html : Clean styles,
14650 editorcore.insertTag(this.tagname);
14659 this.xtype = 'Navbar';
14661 for(var i=0;i< children.length;i++) {
14663 this.buttons.add(this.addxtypeChild(children[i]));
14667 editor.on('editorevent', this.updateToolbar, this);
14669 onBtnClick : function(id)
14671 this.editorcore.relayCmd(id);
14672 this.editorcore.focus();
14676 * Protected method that will not generally be called directly. It triggers
14677 * a toolbar update by reading the markup state of the current selection in the editor.
14679 updateToolbar: function(){
14681 if(!this.editorcore.activated){
14682 this.editor.onFirstFocus(); // is this neeed?
14686 var btns = this.buttons;
14687 var doc = this.editorcore.doc;
14688 btns.get('bold').setActive(doc.queryCommandState('bold'));
14689 btns.get('italic').setActive(doc.queryCommandState('italic'));
14690 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14692 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14693 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14694 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14696 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14697 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14700 var ans = this.editorcore.getAllAncestors();
14701 if (this.formatCombo) {
14704 var store = this.formatCombo.store;
14705 this.formatCombo.setValue("");
14706 for (var i =0; i < ans.length;i++) {
14707 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14709 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14717 // hides menus... - so this cant be on a menu...
14718 Roo.bootstrap.MenuMgr.hideAll();
14720 Roo.bootstrap.MenuMgr.hideAll();
14721 //this.editorsyncValue();
14723 onFirstFocus: function() {
14724 this.buttons.each(function(item){
14728 toggleSourceEdit : function(sourceEditMode){
14731 if(sourceEditMode){
14732 Roo.log("disabling buttons");
14733 this.buttons.each( function(item){
14734 if(item.cmd != 'pencil'){
14740 Roo.log("enabling buttons");
14741 if(this.editorcore.initialized){
14742 this.buttons.each( function(item){
14748 Roo.log("calling toggole on editor");
14749 // tell the editor that it's been pressed..
14750 this.editor.toggleSourceEdit(sourceEditMode);
14760 * @class Roo.bootstrap.Table.AbstractSelectionModel
14761 * @extends Roo.util.Observable
14762 * Abstract base class for grid SelectionModels. It provides the interface that should be
14763 * implemented by descendant classes. This class should not be directly instantiated.
14766 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14767 this.locked = false;
14768 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14772 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14773 /** @ignore Called by the grid automatically. Do not call directly. */
14774 init : function(grid){
14780 * Locks the selections.
14783 this.locked = true;
14787 * Unlocks the selections.
14789 unlock : function(){
14790 this.locked = false;
14794 * Returns true if the selections are locked.
14795 * @return {Boolean}
14797 isLocked : function(){
14798 return this.locked;
14802 * @class Roo.bootstrap.Table.ColumnModel
14803 * @extends Roo.util.Observable
14804 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14805 * the columns in the table.
14808 * @param {Object} config An Array of column config objects. See this class's
14809 * config objects for details.
14811 Roo.bootstrap.Table.ColumnModel = function(config){
14813 * The config passed into the constructor
14815 this.config = config;
14818 // if no id, create one
14819 // if the column does not have a dataIndex mapping,
14820 // map it to the order it is in the config
14821 for(var i = 0, len = config.length; i < len; i++){
14823 if(typeof c.dataIndex == "undefined"){
14826 if(typeof c.renderer == "string"){
14827 c.renderer = Roo.util.Format[c.renderer];
14829 if(typeof c.id == "undefined"){
14832 // if(c.editor && c.editor.xtype){
14833 // c.editor = Roo.factory(c.editor, Roo.grid);
14835 // if(c.editor && c.editor.isFormField){
14836 // c.editor = new Roo.grid.GridEditor(c.editor);
14839 this.lookup[c.id] = c;
14843 * The width of columns which have no width specified (defaults to 100)
14846 this.defaultWidth = 100;
14849 * Default sortable of columns which have no sortable specified (defaults to false)
14852 this.defaultSortable = false;
14856 * @event widthchange
14857 * Fires when the width of a column changes.
14858 * @param {ColumnModel} this
14859 * @param {Number} columnIndex The column index
14860 * @param {Number} newWidth The new width
14862 "widthchange": true,
14864 * @event headerchange
14865 * Fires when the text of a header changes.
14866 * @param {ColumnModel} this
14867 * @param {Number} columnIndex The column index
14868 * @param {Number} newText The new header text
14870 "headerchange": true,
14872 * @event hiddenchange
14873 * Fires when a column is hidden or "unhidden".
14874 * @param {ColumnModel} this
14875 * @param {Number} columnIndex The column index
14876 * @param {Boolean} hidden true if hidden, false otherwise
14878 "hiddenchange": true,
14880 * @event columnmoved
14881 * Fires when a column is moved.
14882 * @param {ColumnModel} this
14883 * @param {Number} oldIndex
14884 * @param {Number} newIndex
14886 "columnmoved" : true,
14888 * @event columlockchange
14889 * Fires when a column's locked state is changed
14890 * @param {ColumnModel} this
14891 * @param {Number} colIndex
14892 * @param {Boolean} locked true if locked
14894 "columnlockchange" : true
14896 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14898 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14900 * @cfg {String} header The header text to display in the Grid view.
14903 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14904 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14905 * specified, the column's index is used as an index into the Record's data Array.
14908 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14909 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14912 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14913 * Defaults to the value of the {@link #defaultSortable} property.
14914 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14917 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14920 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14923 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14926 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14929 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14930 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14931 * default renderer uses the raw data value.
14934 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14938 * Returns the id of the column at the specified index.
14939 * @param {Number} index The column index
14940 * @return {String} the id
14942 getColumnId : function(index){
14943 return this.config[index].id;
14947 * Returns the column for a specified id.
14948 * @param {String} id The column id
14949 * @return {Object} the column
14951 getColumnById : function(id){
14952 return this.lookup[id];
14957 * Returns the column for a specified dataIndex.
14958 * @param {String} dataIndex The column dataIndex
14959 * @return {Object|Boolean} the column or false if not found
14961 getColumnByDataIndex: function(dataIndex){
14962 var index = this.findColumnIndex(dataIndex);
14963 return index > -1 ? this.config[index] : false;
14967 * Returns the index for a specified column id.
14968 * @param {String} id The column id
14969 * @return {Number} the index, or -1 if not found
14971 getIndexById : function(id){
14972 for(var i = 0, len = this.config.length; i < len; i++){
14973 if(this.config[i].id == id){
14981 * Returns the index for a specified column dataIndex.
14982 * @param {String} dataIndex The column dataIndex
14983 * @return {Number} the index, or -1 if not found
14986 findColumnIndex : function(dataIndex){
14987 for(var i = 0, len = this.config.length; i < len; i++){
14988 if(this.config[i].dataIndex == dataIndex){
14996 moveColumn : function(oldIndex, newIndex){
14997 var c = this.config[oldIndex];
14998 this.config.splice(oldIndex, 1);
14999 this.config.splice(newIndex, 0, c);
15000 this.dataMap = null;
15001 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15004 isLocked : function(colIndex){
15005 return this.config[colIndex].locked === true;
15008 setLocked : function(colIndex, value, suppressEvent){
15009 if(this.isLocked(colIndex) == value){
15012 this.config[colIndex].locked = value;
15013 if(!suppressEvent){
15014 this.fireEvent("columnlockchange", this, colIndex, value);
15018 getTotalLockedWidth : function(){
15019 var totalWidth = 0;
15020 for(var i = 0; i < this.config.length; i++){
15021 if(this.isLocked(i) && !this.isHidden(i)){
15022 this.totalWidth += this.getColumnWidth(i);
15028 getLockedCount : function(){
15029 for(var i = 0, len = this.config.length; i < len; i++){
15030 if(!this.isLocked(i)){
15037 * Returns the number of columns.
15040 getColumnCount : function(visibleOnly){
15041 if(visibleOnly === true){
15043 for(var i = 0, len = this.config.length; i < len; i++){
15044 if(!this.isHidden(i)){
15050 return this.config.length;
15054 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15055 * @param {Function} fn
15056 * @param {Object} scope (optional)
15057 * @return {Array} result
15059 getColumnsBy : function(fn, scope){
15061 for(var i = 0, len = this.config.length; i < len; i++){
15062 var c = this.config[i];
15063 if(fn.call(scope||this, c, i) === true){
15071 * Returns true if the specified column is sortable.
15072 * @param {Number} col The column index
15073 * @return {Boolean}
15075 isSortable : function(col){
15076 if(typeof this.config[col].sortable == "undefined"){
15077 return this.defaultSortable;
15079 return this.config[col].sortable;
15083 * Returns the rendering (formatting) function defined for the column.
15084 * @param {Number} col The column index.
15085 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15087 getRenderer : function(col){
15088 if(!this.config[col].renderer){
15089 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15091 return this.config[col].renderer;
15095 * Sets the rendering (formatting) function for a column.
15096 * @param {Number} col The column index
15097 * @param {Function} fn The function to use to process the cell's raw data
15098 * to return HTML markup for the grid view. The render function is called with
15099 * the following parameters:<ul>
15100 * <li>Data value.</li>
15101 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15102 * <li>css A CSS style string to apply to the table cell.</li>
15103 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15104 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15105 * <li>Row index</li>
15106 * <li>Column index</li>
15107 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15109 setRenderer : function(col, fn){
15110 this.config[col].renderer = fn;
15114 * Returns the width for the specified column.
15115 * @param {Number} col The column index
15118 getColumnWidth : function(col){
15119 return this.config[col].width * 1 || this.defaultWidth;
15123 * Sets the width for a column.
15124 * @param {Number} col The column index
15125 * @param {Number} width The new width
15127 setColumnWidth : function(col, width, suppressEvent){
15128 this.config[col].width = width;
15129 this.totalWidth = null;
15130 if(!suppressEvent){
15131 this.fireEvent("widthchange", this, col, width);
15136 * Returns the total width of all columns.
15137 * @param {Boolean} includeHidden True to include hidden column widths
15140 getTotalWidth : function(includeHidden){
15141 if(!this.totalWidth){
15142 this.totalWidth = 0;
15143 for(var i = 0, len = this.config.length; i < len; i++){
15144 if(includeHidden || !this.isHidden(i)){
15145 this.totalWidth += this.getColumnWidth(i);
15149 return this.totalWidth;
15153 * Returns the header for the specified column.
15154 * @param {Number} col The column index
15157 getColumnHeader : function(col){
15158 return this.config[col].header;
15162 * Sets the header for a column.
15163 * @param {Number} col The column index
15164 * @param {String} header The new header
15166 setColumnHeader : function(col, header){
15167 this.config[col].header = header;
15168 this.fireEvent("headerchange", this, col, header);
15172 * Returns the tooltip for the specified column.
15173 * @param {Number} col The column index
15176 getColumnTooltip : function(col){
15177 return this.config[col].tooltip;
15180 * Sets the tooltip for a column.
15181 * @param {Number} col The column index
15182 * @param {String} tooltip The new tooltip
15184 setColumnTooltip : function(col, tooltip){
15185 this.config[col].tooltip = tooltip;
15189 * Returns the dataIndex for the specified column.
15190 * @param {Number} col The column index
15193 getDataIndex : function(col){
15194 return this.config[col].dataIndex;
15198 * Sets the dataIndex for a column.
15199 * @param {Number} col The column index
15200 * @param {Number} dataIndex The new dataIndex
15202 setDataIndex : function(col, dataIndex){
15203 this.config[col].dataIndex = dataIndex;
15209 * Returns true if the cell is editable.
15210 * @param {Number} colIndex The column index
15211 * @param {Number} rowIndex The row index
15212 * @return {Boolean}
15214 isCellEditable : function(colIndex, rowIndex){
15215 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15219 * Returns the editor defined for the cell/column.
15220 * return false or null to disable editing.
15221 * @param {Number} colIndex The column index
15222 * @param {Number} rowIndex The row index
15225 getCellEditor : function(colIndex, rowIndex){
15226 return this.config[colIndex].editor;
15230 * Sets if a column is editable.
15231 * @param {Number} col The column index
15232 * @param {Boolean} editable True if the column is editable
15234 setEditable : function(col, editable){
15235 this.config[col].editable = editable;
15240 * Returns true if the column is hidden.
15241 * @param {Number} colIndex The column index
15242 * @return {Boolean}
15244 isHidden : function(colIndex){
15245 return this.config[colIndex].hidden;
15250 * Returns true if the column width cannot be changed
15252 isFixed : function(colIndex){
15253 return this.config[colIndex].fixed;
15257 * Returns true if the column can be resized
15258 * @return {Boolean}
15260 isResizable : function(colIndex){
15261 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15264 * Sets if a column is hidden.
15265 * @param {Number} colIndex The column index
15266 * @param {Boolean} hidden True if the column is hidden
15268 setHidden : function(colIndex, hidden){
15269 this.config[colIndex].hidden = hidden;
15270 this.totalWidth = null;
15271 this.fireEvent("hiddenchange", this, colIndex, hidden);
15275 * Sets the editor for a column.
15276 * @param {Number} col The column index
15277 * @param {Object} editor The editor object
15279 setEditor : function(col, editor){
15280 this.config[col].editor = editor;
15284 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15285 if(typeof value == "string" && value.length < 1){
15291 // Alias for backwards compatibility
15292 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15295 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15296 * @class Roo.bootstrap.Table.RowSelectionModel
15297 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15298 * It supports multiple selections and keyboard selection/navigation.
15300 * @param {Object} config
15303 Roo.bootstrap.Table.RowSelectionModel = function(config){
15304 Roo.apply(this, config);
15305 this.selections = new Roo.util.MixedCollection(false, function(o){
15310 this.lastActive = false;
15314 * @event selectionchange
15315 * Fires when the selection changes
15316 * @param {SelectionModel} this
15318 "selectionchange" : true,
15320 * @event afterselectionchange
15321 * Fires after the selection changes (eg. by key press or clicking)
15322 * @param {SelectionModel} this
15324 "afterselectionchange" : true,
15326 * @event beforerowselect
15327 * Fires when a row is selected being selected, return false to cancel.
15328 * @param {SelectionModel} this
15329 * @param {Number} rowIndex The selected index
15330 * @param {Boolean} keepExisting False if other selections will be cleared
15332 "beforerowselect" : true,
15335 * Fires when a row is selected.
15336 * @param {SelectionModel} this
15337 * @param {Number} rowIndex The selected index
15338 * @param {Roo.data.Record} r The record
15340 "rowselect" : true,
15342 * @event rowdeselect
15343 * Fires when a row is deselected.
15344 * @param {SelectionModel} this
15345 * @param {Number} rowIndex The selected index
15347 "rowdeselect" : true
15349 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15350 this.locked = false;
15353 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15355 * @cfg {Boolean} singleSelect
15356 * True to allow selection of only one row at a time (defaults to false)
15358 singleSelect : false,
15361 initEvents : function(){
15363 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15364 this.grid.on("mousedown", this.handleMouseDown, this);
15365 }else{ // allow click to work like normal
15366 this.grid.on("rowclick", this.handleDragableRowClick, this);
15369 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15370 "up" : function(e){
15372 this.selectPrevious(e.shiftKey);
15373 }else if(this.last !== false && this.lastActive !== false){
15374 var last = this.last;
15375 this.selectRange(this.last, this.lastActive-1);
15376 this.grid.getView().focusRow(this.lastActive);
15377 if(last !== false){
15381 this.selectFirstRow();
15383 this.fireEvent("afterselectionchange", this);
15385 "down" : function(e){
15387 this.selectNext(e.shiftKey);
15388 }else if(this.last !== false && this.lastActive !== false){
15389 var last = this.last;
15390 this.selectRange(this.last, this.lastActive+1);
15391 this.grid.getView().focusRow(this.lastActive);
15392 if(last !== false){
15396 this.selectFirstRow();
15398 this.fireEvent("afterselectionchange", this);
15403 var view = this.grid.view;
15404 view.on("refresh", this.onRefresh, this);
15405 view.on("rowupdated", this.onRowUpdated, this);
15406 view.on("rowremoved", this.onRemove, this);
15410 onRefresh : function(){
15411 var ds = this.grid.dataSource, i, v = this.grid.view;
15412 var s = this.selections;
15413 s.each(function(r){
15414 if((i = ds.indexOfId(r.id)) != -1){
15423 onRemove : function(v, index, r){
15424 this.selections.remove(r);
15428 onRowUpdated : function(v, index, r){
15429 if(this.isSelected(r)){
15430 v.onRowSelect(index);
15436 * @param {Array} records The records to select
15437 * @param {Boolean} keepExisting (optional) True to keep existing selections
15439 selectRecords : function(records, keepExisting){
15441 this.clearSelections();
15443 var ds = this.grid.dataSource;
15444 for(var i = 0, len = records.length; i < len; i++){
15445 this.selectRow(ds.indexOf(records[i]), true);
15450 * Gets the number of selected rows.
15453 getCount : function(){
15454 return this.selections.length;
15458 * Selects the first row in the grid.
15460 selectFirstRow : function(){
15465 * Select the last row.
15466 * @param {Boolean} keepExisting (optional) True to keep existing selections
15468 selectLastRow : function(keepExisting){
15469 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15473 * Selects the row immediately following the last selected row.
15474 * @param {Boolean} keepExisting (optional) True to keep existing selections
15476 selectNext : function(keepExisting){
15477 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15478 this.selectRow(this.last+1, keepExisting);
15479 this.grid.getView().focusRow(this.last);
15484 * Selects the row that precedes the last selected row.
15485 * @param {Boolean} keepExisting (optional) True to keep existing selections
15487 selectPrevious : function(keepExisting){
15489 this.selectRow(this.last-1, keepExisting);
15490 this.grid.getView().focusRow(this.last);
15495 * Returns the selected records
15496 * @return {Array} Array of selected records
15498 getSelections : function(){
15499 return [].concat(this.selections.items);
15503 * Returns the first selected record.
15506 getSelected : function(){
15507 return this.selections.itemAt(0);
15512 * Clears all selections.
15514 clearSelections : function(fast){
15515 if(this.locked) return;
15517 var ds = this.grid.dataSource;
15518 var s = this.selections;
15519 s.each(function(r){
15520 this.deselectRow(ds.indexOfId(r.id));
15524 this.selections.clear();
15531 * Selects all rows.
15533 selectAll : function(){
15534 if(this.locked) return;
15535 this.selections.clear();
15536 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15537 this.selectRow(i, true);
15542 * Returns True if there is a selection.
15543 * @return {Boolean}
15545 hasSelection : function(){
15546 return this.selections.length > 0;
15550 * Returns True if the specified row is selected.
15551 * @param {Number/Record} record The record or index of the record to check
15552 * @return {Boolean}
15554 isSelected : function(index){
15555 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15556 return (r && this.selections.key(r.id) ? true : false);
15560 * Returns True if the specified record id is selected.
15561 * @param {String} id The id of record to check
15562 * @return {Boolean}
15564 isIdSelected : function(id){
15565 return (this.selections.key(id) ? true : false);
15569 handleMouseDown : function(e, t){
15570 var view = this.grid.getView(), rowIndex;
15571 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15574 if(e.shiftKey && this.last !== false){
15575 var last = this.last;
15576 this.selectRange(last, rowIndex, e.ctrlKey);
15577 this.last = last; // reset the last
15578 view.focusRow(rowIndex);
15580 var isSelected = this.isSelected(rowIndex);
15581 if(e.button !== 0 && isSelected){
15582 view.focusRow(rowIndex);
15583 }else if(e.ctrlKey && isSelected){
15584 this.deselectRow(rowIndex);
15585 }else if(!isSelected){
15586 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15587 view.focusRow(rowIndex);
15590 this.fireEvent("afterselectionchange", this);
15593 handleDragableRowClick : function(grid, rowIndex, e)
15595 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15596 this.selectRow(rowIndex, false);
15597 grid.view.focusRow(rowIndex);
15598 this.fireEvent("afterselectionchange", this);
15603 * Selects multiple rows.
15604 * @param {Array} rows Array of the indexes of the row to select
15605 * @param {Boolean} keepExisting (optional) True to keep existing selections
15607 selectRows : function(rows, keepExisting){
15609 this.clearSelections();
15611 for(var i = 0, len = rows.length; i < len; i++){
15612 this.selectRow(rows[i], true);
15617 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15618 * @param {Number} startRow The index of the first row in the range
15619 * @param {Number} endRow The index of the last row in the range
15620 * @param {Boolean} keepExisting (optional) True to retain existing selections
15622 selectRange : function(startRow, endRow, keepExisting){
15623 if(this.locked) return;
15625 this.clearSelections();
15627 if(startRow <= endRow){
15628 for(var i = startRow; i <= endRow; i++){
15629 this.selectRow(i, true);
15632 for(var i = startRow; i >= endRow; i--){
15633 this.selectRow(i, true);
15639 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15640 * @param {Number} startRow The index of the first row in the range
15641 * @param {Number} endRow The index of the last row in the range
15643 deselectRange : function(startRow, endRow, preventViewNotify){
15644 if(this.locked) return;
15645 for(var i = startRow; i <= endRow; i++){
15646 this.deselectRow(i, preventViewNotify);
15652 * @param {Number} row The index of the row to select
15653 * @param {Boolean} keepExisting (optional) True to keep existing selections
15655 selectRow : function(index, keepExisting, preventViewNotify){
15656 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15657 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15658 if(!keepExisting || this.singleSelect){
15659 this.clearSelections();
15661 var r = this.grid.dataSource.getAt(index);
15662 this.selections.add(r);
15663 this.last = this.lastActive = index;
15664 if(!preventViewNotify){
15665 this.grid.getView().onRowSelect(index);
15667 this.fireEvent("rowselect", this, index, r);
15668 this.fireEvent("selectionchange", this);
15674 * @param {Number} row The index of the row to deselect
15676 deselectRow : function(index, preventViewNotify){
15677 if(this.locked) return;
15678 if(this.last == index){
15681 if(this.lastActive == index){
15682 this.lastActive = false;
15684 var r = this.grid.dataSource.getAt(index);
15685 this.selections.remove(r);
15686 if(!preventViewNotify){
15687 this.grid.getView().onRowDeselect(index);
15689 this.fireEvent("rowdeselect", this, index);
15690 this.fireEvent("selectionchange", this);
15694 restoreLast : function(){
15696 this.last = this._last;
15701 acceptsNav : function(row, col, cm){
15702 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15706 onEditorKey : function(field, e){
15707 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15712 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15714 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15716 }else if(k == e.ENTER && !e.ctrlKey){
15720 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15722 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15724 }else if(k == e.ESC){
15728 g.startEditing(newCell[0], newCell[1]);
15739 * @class Roo.bootstrap.MessageBar
15740 * @extends Roo.bootstrap.Component
15741 * Bootstrap MessageBar class
15742 * @cfg {String} html contents of the MessageBar
15743 * @cfg {String} weight (info | success | warning | danger) default info
15744 * @cfg {String} beforeClass insert the bar before the given class
15745 * @cfg {Boolean} closable (true | false) default false
15746 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15749 * Create a new Element
15750 * @param {Object} config The config object
15753 Roo.bootstrap.MessageBar = function(config){
15754 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15757 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15763 beforeClass: 'bootstrap-sticky-wrap',
15765 getAutoCreate : function(){
15769 cls: 'alert alert-dismissable alert-' + this.weight,
15774 html: this.html || ''
15780 cfg.cls += ' alert-messages-fixed';
15794 onRender : function(ct, position)
15796 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15799 var cfg = Roo.apply({}, this.getAutoCreate());
15803 cfg.cls += ' ' + this.cls;
15806 cfg.style = this.style;
15808 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15810 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15813 this.el.select('>button.close').on('click', this.hide, this);
15819 if (!this.rendered) {
15825 this.fireEvent('show', this);
15831 if (!this.rendered) {
15837 this.fireEvent('hide', this);
15840 update : function()
15842 // var e = this.el.dom.firstChild;
15844 // if(this.closable){
15845 // e = e.nextSibling;
15848 // e.data = this.html || '';
15850 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';