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 {Number} width width of dialog
1749 * @cfg {Array} buttons Array of buttons or standard button set..
1752 * Create a new Modal Dialog
1753 * @param {Object} config The config object
1756 Roo.bootstrap.Modal = function(config){
1757 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1762 * The raw btnclick event for the button
1763 * @param {Roo.EventObject} e
1769 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1771 title : 'test dialog',
1777 onRender : function(ct, position)
1779 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1782 var cfg = Roo.apply({}, this.getAutoCreate());
1785 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1787 //if (!cfg.name.length) {
1791 cfg.cls += ' ' + this.cls;
1794 cfg.style = this.style;
1796 this.el = Roo.get(document.body).createChild(cfg, position);
1798 //var type = this.el.dom.type;
1800 if(this.tabIndex !== undefined){
1801 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1806 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1807 this.maskEl.enableDisplayMode("block");
1809 //this.el.addClass("x-dlg-modal");
1812 Roo.each(this.buttons, function(bb) {
1813 b = Roo.apply({}, bb);
1814 b.xns = b.xns || Roo.bootstrap;
1815 b.xtype = b.xtype || 'Button';
1816 if (typeof(b.listeners) == 'undefined') {
1817 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1820 var btn = Roo.factory(b);
1822 btn.onRender(this.el.select('.modal-footer').first());
1826 // render the children.
1829 if(typeof(this.items) != 'undefined'){
1830 var items = this.items;
1833 for(var i =0;i < items.length;i++) {
1834 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1838 this.items = nitems;
1840 //this.el.addClass([this.fieldClass, this.cls]);
1843 getAutoCreate : function(){
1848 html : this.html || ''
1856 cls: "modal-dialog",
1859 cls : "modal-content",
1862 cls : 'modal-header',
1871 cls : 'modal-title',
1879 cls : 'modal-footer'
1895 modal.cn[0].style = 'width:' + this.width + 'px';
1901 getChildContainer : function() {
1903 return this.el.select('.modal-body',true).first();
1906 getButtonContainer : function() {
1907 return this.el.select('.modal-footer',true).first();
1910 initEvents : function()
1912 this.el.select('.modal-header .close').on('click', this.hide, this);
1914 // this.addxtype(this);
1918 if (!this.rendered) {
1922 this.el.addClass('on');
1923 this.el.removeClass('fade');
1924 this.el.setStyle('display', 'block');
1925 Roo.get(document.body).addClass("x-body-masked");
1926 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1928 this.el.setStyle('zIndex', '10001');
1929 this.fireEvent('show', this);
1935 Roo.log('Modal hide?!');
1937 Roo.get(document.body).removeClass("x-body-masked");
1938 this.el.removeClass('on');
1939 this.el.addClass('fade');
1940 this.el.setStyle('display', 'none');
1941 this.fireEvent('hide', this);
1943 onButtonClick: function(btn,e)
1946 this.fireEvent('btnclick', btn.name, e);
1951 Roo.apply(Roo.bootstrap.Modal, {
1953 * Button config that displays a single OK button
1962 * Button config that displays Yes and No buttons
1978 * Button config that displays OK and Cancel buttons
1993 * Button config that displays Yes, No and Cancel buttons
2020 * @class Roo.bootstrap.Navbar
2021 * @extends Roo.bootstrap.Component
2022 * Bootstrap Navbar class
2023 * @cfg {Boolean} sidebar has side bar
2024 * @cfg {Boolean} bar is a bar?
2025 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2026 * @cfg {String} brand what is brand
2027 * @cfg {Boolean} inverse is inverted color
2028 * @cfg {String} type (nav | pills | tabs)
2029 * @cfg {Boolean} arrangement stacked | justified
2030 * @cfg {String} align (left | right) alignment
2031 * @cfg {String} brand_href href of the brand
2032 * @cfg {Boolean} main (true|false) main nav bar? default false
2033 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2037 * Create a new Navbar
2038 * @param {Object} config The config object
2042 Roo.bootstrap.Navbar = function(config){
2043 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2046 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2061 getAutoCreate : function(){
2066 if (this.sidebar === true) {
2074 if (this.bar === true) {
2082 cls: 'navbar-header',
2087 cls: 'navbar-toggle',
2088 'data-toggle': 'collapse',
2093 html: 'Toggle navigation'
2113 cls: 'collapse navbar-collapse'
2118 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2120 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2121 cfg.cls += ' navbar-' + this.position;
2122 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2125 if (this.brand !== '') {
2128 href: this.brand_href ? this.brand_href : '#',
2129 cls: 'navbar-brand',
2137 cfg.cls += ' main-nav';
2143 } else if (this.bar === false) {
2146 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2156 if (['tabs','pills'].indexOf(this.type)!==-1) {
2157 cfg.cn[0].cls += ' nav-' + this.type
2159 if (this.type!=='nav') {
2160 Roo.log('nav type must be nav/tabs/pills')
2162 cfg.cn[0].cls += ' navbar-nav'
2165 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2166 cfg.cn[0].cls += ' nav-' + this.arrangement;
2169 if (this.align === 'right') {
2170 cfg.cn[0].cls += ' navbar-right';
2173 cfg.cls += ' navbar-inverse';
2181 initEvents :function ()
2183 //Roo.log(this.el.select('.navbar-toggle',true));
2184 this.el.select('.navbar-toggle',true).on('click', function() {
2185 // Roo.log('click');
2186 this.el.select('.navbar-collapse',true).toggleClass('in');
2194 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2196 var size = this.el.getSize();
2197 this.maskEl.setSize(size.width, size.height);
2198 this.maskEl.enableDisplayMode("block");
2207 getChildContainer : function()
2209 if (this.bar === true) {
2210 return this.el.select('.collapse',true).first();
2238 * @class Roo.bootstrap.NavGroup
2239 * @extends Roo.bootstrap.Component
2240 * Bootstrap NavGroup class
2241 * @cfg {String} align left | right
2242 * @cfg {Boolean} inverse false | true
2243 * @cfg {String} type (nav|pills|tab) default nav
2246 * Create a new nav group
2247 * @param {Object} config The config object
2250 Roo.bootstrap.NavGroup = function(config){
2251 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2254 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2261 getAutoCreate : function(){
2262 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2269 if (['tabs','pills'].indexOf(this.type)!==-1) {
2270 cfg.cls += ' nav-' + this.type
2272 if (this.type!=='nav') {
2273 Roo.log('nav type must be nav/tabs/pills')
2275 cfg.cls += ' navbar-nav'
2278 if (this.parent().sidebar === true) {
2281 cls: 'dashboard-menu'
2287 if (this.form === true) {
2293 if (this.align === 'right') {
2294 cfg.cls += ' navbar-right';
2296 cfg.cls += ' navbar-left';
2300 if (this.align === 'right') {
2301 cfg.cls += ' navbar-right';
2305 cfg.cls += ' navbar-inverse';
2325 * @class Roo.bootstrap.Navbar.Item
2326 * @extends Roo.bootstrap.Component
2327 * Bootstrap Navbar.Button class
2328 * @cfg {String} href link to
2329 * @cfg {String} html content of button
2330 * @cfg {String} badge text inside badge
2331 * @cfg {String} glyphicon name of glyphicon
2332 * @cfg {String} icon name of font awesome icon
2333 * @cfg {Boolena} active Is item active
2334 * @cfg {Boolean} preventDefault (true | false) default false
2337 * Create a new Navbar Button
2338 * @param {Object} config The config object
2340 Roo.bootstrap.Navbar.Item = function(config){
2341 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2346 * The raw click event for the entire grid.
2347 * @param {Roo.EventObject} e
2353 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
2362 preventDefault : false,
2364 getAutoCreate : function(){
2366 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2368 if (this.parent().parent().sidebar === true) {
2381 cfg.cn[0].html = this.html;
2385 this.cls += ' active';
2389 cfg.cn[0].cls += ' dropdown-toggle';
2390 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2394 cfg.cn[0].tag = 'a',
2395 cfg.cn[0].href = this.href;
2398 if (this.glyphicon) {
2399 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2403 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2415 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2425 if (this.glyphicon) {
2426 if(cfg.html){cfg.html = ' ' + this.html};
2430 cls: 'glyphicon glyphicon-' + this.glyphicon
2435 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2440 cfg.cn[0].html += " <span class='caret'></span>";
2441 //}else if (!this.href) {
2442 // cfg.cn[0].tag='p';
2443 // cfg.cn[0].cls='navbar-text';
2446 cfg.cn[0].href=this.href||'#';
2447 cfg.cn[0].html=this.html;
2450 if (this.badge !== '') {
2453 cfg.cn[0].html + ' ',
2464 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2469 initEvents: function() {
2470 // Roo.log('init events?');
2471 // Roo.log(this.el.dom);
2472 this.el.select('a',true).on('click', this.onClick, this);
2475 onClick : function(e)
2477 if(this.preventDefault){
2481 if(this.fireEvent('click', this, e) === false){
2485 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2486 this.onTabsClick(e);
2490 onTabsClick : function(e)
2492 Roo.each(this.parent().el.select('.active',true).elements, function(v){
2493 v.removeClass('active');
2496 this.el.addClass('active');
2498 if(this.href && this.href.substring(0,1) == '#'){
2499 var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2501 Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2502 v.removeClass('active');
2505 tab.addClass('active');
2520 * @class Roo.bootstrap.Row
2521 * @extends Roo.bootstrap.Component
2522 * Bootstrap Row class (contains columns...)
2526 * @param {Object} config The config object
2529 Roo.bootstrap.Row = function(config){
2530 Roo.bootstrap.Row.superclass.constructor.call(this, config);
2533 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
2535 getAutoCreate : function(){
2554 * @class Roo.bootstrap.Element
2555 * @extends Roo.bootstrap.Component
2556 * Bootstrap Element class
2557 * @cfg {String} html contents of the element
2558 * @cfg {String} tag tag of the element
2559 * @cfg {String} cls class of the element
2562 * Create a new Element
2563 * @param {Object} config The config object
2566 Roo.bootstrap.Element = function(config){
2567 Roo.bootstrap.Element.superclass.constructor.call(this, config);
2570 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
2577 getAutoCreate : function(){
2602 * @class Roo.bootstrap.Pagination
2603 * @extends Roo.bootstrap.Component
2604 * Bootstrap Pagination class
2605 * @cfg {String} size xs | sm | md | lg
2606 * @cfg {Boolean} inverse false | true
2609 * Create a new Pagination
2610 * @param {Object} config The config object
2613 Roo.bootstrap.Pagination = function(config){
2614 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2617 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
2623 getAutoCreate : function(){
2629 cfg.cls += ' inverse';
2635 cfg.cls += " " + this.cls;
2653 * @class Roo.bootstrap.PaginationItem
2654 * @extends Roo.bootstrap.Component
2655 * Bootstrap PaginationItem class
2656 * @cfg {String} html text
2657 * @cfg {String} href the link
2658 * @cfg {Boolean} preventDefault (true | false) default true
2659 * @cfg {Boolean} active (true | false) default false
2663 * Create a new PaginationItem
2664 * @param {Object} config The config object
2668 Roo.bootstrap.PaginationItem = function(config){
2669 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2674 * The raw click event for the entire grid.
2675 * @param {Roo.EventObject} e
2681 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
2685 preventDefault: true,
2689 getAutoCreate : function(){
2695 href : this.href ? this.href : '#',
2696 html : this.html ? this.html : ''
2706 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2712 initEvents: function() {
2714 this.el.on('click', this.onClick, this);
2717 onClick : function(e)
2719 Roo.log('PaginationItem on click ');
2720 if(this.preventDefault){
2724 this.fireEvent('click', this, e);
2740 * @class Roo.bootstrap.Slider
2741 * @extends Roo.bootstrap.Component
2742 * Bootstrap Slider class
2745 * Create a new Slider
2746 * @param {Object} config The config object
2749 Roo.bootstrap.Slider = function(config){
2750 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2753 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
2755 getAutoCreate : function(){
2759 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2763 cls: 'ui-slider-handle ui-state-default ui-corner-all'
2781 * @class Roo.bootstrap.Table
2782 * @extends Roo.bootstrap.Component
2783 * Bootstrap Table class
2784 * @cfg {String} cls table class
2785 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2786 * @cfg {String} bgcolor Specifies the background color for a table
2787 * @cfg {Number} border Specifies whether the table cells should have borders or not
2788 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2789 * @cfg {Number} cellspacing Specifies the space between cells
2790 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2791 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2792 * @cfg {String} sortable Specifies that the table should be sortable
2793 * @cfg {String} summary Specifies a summary of the content of a table
2794 * @cfg {Number} width Specifies the width of a table
2796 * @cfg {boolean} striped Should the rows be alternative striped
2797 * @cfg {boolean} bordered Add borders to the table
2798 * @cfg {boolean} hover Add hover highlighting
2799 * @cfg {boolean} condensed Format condensed
2800 * @cfg {boolean} responsive Format condensed
2806 * Create a new Table
2807 * @param {Object} config The config object
2810 Roo.bootstrap.Table = function(config){
2811 Roo.bootstrap.Table.superclass.constructor.call(this, config);
2814 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2815 this.sm = this.selModel;
2816 this.sm.xmodule = this.xmodule || false;
2818 if (this.cm && typeof(this.cm.config) == 'undefined') {
2819 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2820 this.cm = this.colModel;
2821 this.cm.xmodule = this.xmodule || false;
2824 this.store= Roo.factory(this.store, Roo.data);
2825 this.ds = this.store;
2826 this.ds.xmodule = this.xmodule || false;
2831 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
2853 getAutoCreate : function(){
2854 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2863 cfg.cls += ' table-striped';
2866 cfg.cls += ' table-hover';
2868 if (this.bordered) {
2869 cfg.cls += ' table-bordered';
2871 if (this.condensed) {
2872 cfg.cls += ' table-condensed';
2874 if (this.responsive) {
2875 cfg.cls += ' table-responsive';
2882 cfg.cls+= ' ' +this.cls;
2885 // this lot should be simplifed...
2888 cfg.align=this.align;
2891 cfg.bgcolor=this.bgcolor;
2894 cfg.border=this.border;
2896 if (this.cellpadding) {
2897 cfg.cellpadding=this.cellpadding;
2899 if (this.cellspacing) {
2900 cfg.cellspacing=this.cellspacing;
2903 cfg.frame=this.frame;
2906 cfg.rules=this.rules;
2908 if (this.sortable) {
2909 cfg.sortable=this.sortable;
2912 cfg.summary=this.summary;
2915 cfg.width=this.width;
2918 if(this.store || this.cm){
2919 cfg.cn.push(this.renderHeader());
2920 cfg.cn.push(this.renderBody());
2921 cfg.cn.push(this.renderFooter());
2923 cfg.cls+= ' TableGrid';
2929 // initTableGrid : function()
2938 // var cm = this.cm;
2940 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2943 // html: cm.getColumnHeader(i)
2947 // cfg.push(header);
2954 initEvents : function()
2956 if(!this.store || !this.cm){
2960 Roo.log('initEvents with ds!!!!');
2962 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2963 // this.maskEl.enableDisplayMode("block");
2964 // this.maskEl.show();
2966 this.store.on('load', this.onLoad, this);
2967 this.store.on('beforeload', this.onBeforeLoad, this);
2975 renderHeader : function()
2984 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2987 html: cm.getColumnHeader(i)
2994 renderBody : function()
3004 renderFooter : function()
3016 Roo.log('ds onload');
3020 var tbody = this.el.select('tbody', true).first();
3024 if(this.store.getCount() > 0){
3025 this.store.data.each(function(d){
3031 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3032 var renderer = cm.getRenderer(i);
3036 if(typeof(renderer) !== 'undefined'){
3037 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3040 if(typeof(value) === 'object'){
3050 html: (typeof(value) === 'object') ? '' : value
3055 tbody.createChild(row);
3063 Roo.each(renders, function(r){
3064 _this.renderColumn(r);
3068 // if(this.loadMask){
3069 // this.maskEl.hide();
3073 onBeforeLoad : function()
3075 Roo.log('ds onBeforeLoad');
3079 // if(this.loadMask){
3080 // this.maskEl.show();
3086 this.el.select('tbody', true).first().dom.innerHTML = '';
3089 getSelectionModel : function(){
3091 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3093 return this.selModel;
3096 renderColumn : function(r)
3099 r.cfg.render(Roo.get(r.id));
3102 Roo.each(r.cfg.cn, function(c){
3107 _this.renderColumn(child);
3124 * @class Roo.bootstrap.TableCell
3125 * @extends Roo.bootstrap.Component
3126 * Bootstrap TableCell class
3127 * @cfg {String} html cell contain text
3128 * @cfg {String} cls cell class
3129 * @cfg {String} tag cell tag (td|th) default td
3130 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3131 * @cfg {String} align Aligns the content in a cell
3132 * @cfg {String} axis Categorizes cells
3133 * @cfg {String} bgcolor Specifies the background color of a cell
3134 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3135 * @cfg {Number} colspan Specifies the number of columns a cell should span
3136 * @cfg {String} headers Specifies one or more header cells a cell is related to
3137 * @cfg {Number} height Sets the height of a cell
3138 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3139 * @cfg {Number} rowspan Sets the number of rows a cell should span
3140 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3141 * @cfg {String} valign Vertical aligns the content in a cell
3142 * @cfg {Number} width Specifies the width of a cell
3145 * Create a new TableCell
3146 * @param {Object} config The config object
3149 Roo.bootstrap.TableCell = function(config){
3150 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3153 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3173 getAutoCreate : function(){
3174 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3194 cfg.align=this.align
3200 cfg.bgcolor=this.bgcolor
3203 cfg.charoff=this.charoff
3206 cfg.colspan=this.colspan
3209 cfg.headers=this.headers
3212 cfg.height=this.height
3215 cfg.nowrap=this.nowrap
3218 cfg.rowspan=this.rowspan
3221 cfg.scope=this.scope
3224 cfg.valign=this.valign
3227 cfg.width=this.width
3246 * @class Roo.bootstrap.TableRow
3247 * @extends Roo.bootstrap.Component
3248 * Bootstrap TableRow class
3249 * @cfg {String} cls row class
3250 * @cfg {String} align Aligns the content in a table row
3251 * @cfg {String} bgcolor Specifies a background color for a table row
3252 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3253 * @cfg {String} valign Vertical aligns the content in a table row
3256 * Create a new TableRow
3257 * @param {Object} config The config object
3260 Roo.bootstrap.TableRow = function(config){
3261 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3264 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3272 getAutoCreate : function(){
3273 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3283 cfg.align = this.align;
3286 cfg.bgcolor = this.bgcolor;
3289 cfg.charoff = this.charoff;
3292 cfg.valign = this.valign;
3310 * @class Roo.bootstrap.TableBody
3311 * @extends Roo.bootstrap.Component
3312 * Bootstrap TableBody class
3313 * @cfg {String} cls element class
3314 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3315 * @cfg {String} align Aligns the content inside the element
3316 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3317 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3320 * Create a new TableBody
3321 * @param {Object} config The config object
3324 Roo.bootstrap.TableBody = function(config){
3325 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3328 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3336 getAutoCreate : function(){
3337 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3351 cfg.align = this.align;
3354 cfg.charoff = this.charoff;
3357 cfg.valign = this.valign;
3364 // initEvents : function()
3371 // this.store = Roo.factory(this.store, Roo.data);
3372 // this.store.on('load', this.onLoad, this);
3374 // this.store.load();
3378 // onLoad: function ()
3380 // this.fireEvent('load', this);
3390 * Ext JS Library 1.1.1
3391 * Copyright(c) 2006-2007, Ext JS, LLC.
3393 * Originally Released Under LGPL - original licence link has changed is not relivant.
3396 * <script type="text/javascript">
3399 // as we use this in bootstrap.
3400 Roo.namespace('Roo.form');
3402 * @class Roo.form.Action
3403 * Internal Class used to handle form actions
3405 * @param {Roo.form.BasicForm} el The form element or its id
3406 * @param {Object} config Configuration options
3411 // define the action interface
3412 Roo.form.Action = function(form, options){
3414 this.options = options || {};
3417 * Client Validation Failed
3420 Roo.form.Action.CLIENT_INVALID = 'client';
3422 * Server Validation Failed
3425 Roo.form.Action.SERVER_INVALID = 'server';
3427 * Connect to Server Failed
3430 Roo.form.Action.CONNECT_FAILURE = 'connect';
3432 * Reading Data from Server Failed
3435 Roo.form.Action.LOAD_FAILURE = 'load';
3437 Roo.form.Action.prototype = {
3439 failureType : undefined,
3440 response : undefined,
3444 run : function(options){
3449 success : function(response){
3454 handleResponse : function(response){
3458 // default connection failure
3459 failure : function(response){
3461 this.response = response;
3462 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3463 this.form.afterAction(this, false);
3466 processResponse : function(response){
3467 this.response = response;
3468 if(!response.responseText){
3471 this.result = this.handleResponse(response);
3475 // utility functions used internally
3476 getUrl : function(appendParams){
3477 var url = this.options.url || this.form.url || this.form.el.dom.action;
3479 var p = this.getParams();
3481 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3487 getMethod : function(){
3488 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3491 getParams : function(){
3492 var bp = this.form.baseParams;
3493 var p = this.options.params;
3495 if(typeof p == "object"){
3496 p = Roo.urlEncode(Roo.applyIf(p, bp));
3497 }else if(typeof p == 'string' && bp){
3498 p += '&' + Roo.urlEncode(bp);
3501 p = Roo.urlEncode(bp);
3506 createCallback : function(){
3508 success: this.success,
3509 failure: this.failure,
3511 timeout: (this.form.timeout*1000),
3512 upload: this.form.fileUpload ? this.success : undefined
3517 Roo.form.Action.Submit = function(form, options){
3518 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3521 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3524 haveProgress : false,
3525 uploadComplete : false,
3527 // uploadProgress indicator.
3528 uploadProgress : function()
3530 if (!this.form.progressUrl) {
3534 if (!this.haveProgress) {
3535 Roo.MessageBox.progress("Uploading", "Uploading");
3537 if (this.uploadComplete) {
3538 Roo.MessageBox.hide();
3542 this.haveProgress = true;
3544 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3546 var c = new Roo.data.Connection();
3548 url : this.form.progressUrl,
3553 success : function(req){
3554 //console.log(data);
3558 rdata = Roo.decode(req.responseText)
3560 Roo.log("Invalid data from server..");
3564 if (!rdata || !rdata.success) {
3566 Roo.MessageBox.alert(Roo.encode(rdata));
3569 var data = rdata.data;
3571 if (this.uploadComplete) {
3572 Roo.MessageBox.hide();
3577 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3578 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3581 this.uploadProgress.defer(2000,this);
3584 failure: function(data) {
3585 Roo.log('progress url failed ');
3596 // run get Values on the form, so it syncs any secondary forms.
3597 this.form.getValues();
3599 var o = this.options;
3600 var method = this.getMethod();
3601 var isPost = method == 'POST';
3602 if(o.clientValidation === false || this.form.isValid()){
3604 if (this.form.progressUrl) {
3605 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3606 (new Date() * 1) + '' + Math.random());
3611 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3612 form:this.form.el.dom,
3613 url:this.getUrl(!isPost),
3615 params:isPost ? this.getParams() : null,
3616 isUpload: this.form.fileUpload
3619 this.uploadProgress();
3621 }else if (o.clientValidation !== false){ // client validation failed
3622 this.failureType = Roo.form.Action.CLIENT_INVALID;
3623 this.form.afterAction(this, false);
3627 success : function(response)
3629 this.uploadComplete= true;
3630 if (this.haveProgress) {
3631 Roo.MessageBox.hide();
3635 var result = this.processResponse(response);
3636 if(result === true || result.success){
3637 this.form.afterAction(this, true);
3641 this.form.markInvalid(result.errors);
3642 this.failureType = Roo.form.Action.SERVER_INVALID;
3644 this.form.afterAction(this, false);
3646 failure : function(response)
3648 this.uploadComplete= true;
3649 if (this.haveProgress) {
3650 Roo.MessageBox.hide();
3653 this.response = response;
3654 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3655 this.form.afterAction(this, false);
3658 handleResponse : function(response){
3659 if(this.form.errorReader){
3660 var rs = this.form.errorReader.read(response);
3663 for(var i = 0, len = rs.records.length; i < len; i++) {
3664 var r = rs.records[i];
3668 if(errors.length < 1){
3672 success : rs.success,
3678 ret = Roo.decode(response.responseText);
3682 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3692 Roo.form.Action.Load = function(form, options){
3693 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3694 this.reader = this.form.reader;
3697 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3702 Roo.Ajax.request(Roo.apply(
3703 this.createCallback(), {
3704 method:this.getMethod(),
3705 url:this.getUrl(false),
3706 params:this.getParams()
3710 success : function(response){
3712 var result = this.processResponse(response);
3713 if(result === true || !result.success || !result.data){
3714 this.failureType = Roo.form.Action.LOAD_FAILURE;
3715 this.form.afterAction(this, false);
3718 this.form.clearInvalid();
3719 this.form.setValues(result.data);
3720 this.form.afterAction(this, true);
3723 handleResponse : function(response){
3724 if(this.form.reader){
3725 var rs = this.form.reader.read(response);
3726 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3728 success : rs.success,
3732 return Roo.decode(response.responseText);
3736 Roo.form.Action.ACTION_TYPES = {
3737 'load' : Roo.form.Action.Load,
3738 'submit' : Roo.form.Action.Submit
3747 * @class Roo.bootstrap.Form
3748 * @extends Roo.bootstrap.Component
3749 * Bootstrap Form class
3750 * @cfg {String} method GET | POST (default POST)
3751 * @cfg {String} labelAlign top | left (default top)
3752 * @cfg {String} align left | right - for navbars
3757 * @param {Object} config The config object
3761 Roo.bootstrap.Form = function(config){
3762 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3765 * @event clientvalidation
3766 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3767 * @param {Form} this
3768 * @param {Boolean} valid true if the form has passed client-side validation
3770 clientvalidation: true,
3772 * @event beforeaction
3773 * Fires before any action is performed. Return false to cancel the action.
3774 * @param {Form} this
3775 * @param {Action} action The action to be performed
3779 * @event actionfailed
3780 * Fires when an action fails.
3781 * @param {Form} this
3782 * @param {Action} action The action that failed
3784 actionfailed : true,
3786 * @event actioncomplete
3787 * Fires when an action is completed.
3788 * @param {Form} this
3789 * @param {Action} action The action that completed
3791 actioncomplete : true
3796 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3799 * @cfg {String} method
3800 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3805 * The URL to use for form actions if one isn't supplied in the action options.
3808 * @cfg {Boolean} fileUpload
3809 * Set to true if this form is a file upload.
3813 * @cfg {Object} baseParams
3814 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3818 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3822 * @cfg {Sting} align (left|right) for navbar forms
3827 activeAction : null,
3830 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3831 * element by passing it or its id or mask the form itself by passing in true.
3834 waitMsgTarget : false,
3839 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3840 * element by passing it or its id or mask the form itself by passing in true.
3844 getAutoCreate : function(){
3848 method : this.method || 'POST',
3849 id : this.id || Roo.id(),
3852 if (this.parent().xtype.match(/^Nav/)) {
3853 cfg.cls = 'navbar-form navbar-' + this.align;
3857 if (this.labelAlign == 'left' ) {
3858 cfg.cls += ' form-horizontal';
3864 initEvents : function()
3866 this.el.on('submit', this.onSubmit, this);
3871 onSubmit : function(e){
3876 * Returns true if client-side validation on the form is successful.
3879 isValid : function(){
3880 var items = this.getItems();
3882 items.each(function(f){
3891 * Returns true if any fields in this form have changed since their original load.
3894 isDirty : function(){
3896 var items = this.getItems();
3897 items.each(function(f){
3907 * Performs a predefined action (submit or load) or custom actions you define on this form.
3908 * @param {String} actionName The name of the action type
3909 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3910 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3911 * accept other config options):
3913 Property Type Description
3914 ---------------- --------------- ----------------------------------------------------------------------------------
3915 url String The url for the action (defaults to the form's url)
3916 method String The form method to use (defaults to the form's method, or POST if not defined)
3917 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3918 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3919 validate the form on the client (defaults to false)
3921 * @return {BasicForm} this
3923 doAction : function(action, options){
3924 if(typeof action == 'string'){
3925 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3927 if(this.fireEvent('beforeaction', this, action) !== false){
3928 this.beforeAction(action);
3929 action.run.defer(100, action);
3935 beforeAction : function(action){
3936 var o = action.options;
3938 // not really supported yet.. ??
3940 //if(this.waitMsgTarget === true){
3941 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3942 //}else if(this.waitMsgTarget){
3943 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3944 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3946 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3952 afterAction : function(action, success){
3953 this.activeAction = null;
3954 var o = action.options;
3956 //if(this.waitMsgTarget === true){
3958 //}else if(this.waitMsgTarget){
3959 // this.waitMsgTarget.unmask();
3961 // Roo.MessageBox.updateProgress(1);
3962 // Roo.MessageBox.hide();
3969 Roo.callback(o.success, o.scope, [this, action]);
3970 this.fireEvent('actioncomplete', this, action);
3974 // failure condition..
3975 // we have a scenario where updates need confirming.
3976 // eg. if a locking scenario exists..
3977 // we look for { errors : { needs_confirm : true }} in the response.
3979 (typeof(action.result) != 'undefined') &&
3980 (typeof(action.result.errors) != 'undefined') &&
3981 (typeof(action.result.errors.needs_confirm) != 'undefined')
3984 Roo.log("not supported yet");
3987 Roo.MessageBox.confirm(
3988 "Change requires confirmation",
3989 action.result.errorMsg,
3994 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4004 Roo.callback(o.failure, o.scope, [this, action]);
4005 // show an error message if no failed handler is set..
4006 if (!this.hasListener('actionfailed')) {
4007 Roo.log("need to add dialog support");
4009 Roo.MessageBox.alert("Error",
4010 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4011 action.result.errorMsg :
4012 "Saving Failed, please check your entries or try again"
4017 this.fireEvent('actionfailed', this, action);
4022 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4023 * @param {String} id The value to search for
4026 findField : function(id){
4027 var items = this.getItems();
4028 var field = items.get(id);
4030 items.each(function(f){
4031 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4038 return field || null;
4041 * Mark fields in this form invalid in bulk.
4042 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4043 * @return {BasicForm} this
4045 markInvalid : function(errors){
4046 if(errors instanceof Array){
4047 for(var i = 0, len = errors.length; i < len; i++){
4048 var fieldError = errors[i];
4049 var f = this.findField(fieldError.id);
4051 f.markInvalid(fieldError.msg);
4057 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4058 field.markInvalid(errors[id]);
4062 //Roo.each(this.childForms || [], function (f) {
4063 // f.markInvalid(errors);
4070 * Set values for fields in this form in bulk.
4071 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4072 * @return {BasicForm} this
4074 setValues : function(values){
4075 if(values instanceof Array){ // array of objects
4076 for(var i = 0, len = values.length; i < len; i++){
4078 var f = this.findField(v.id);
4080 f.setValue(v.value);
4081 if(this.trackResetOnLoad){
4082 f.originalValue = f.getValue();
4086 }else{ // object hash
4089 if(typeof values[id] != 'function' && (field = this.findField(id))){
4091 if (field.setFromData &&
4093 field.displayField &&
4094 // combos' with local stores can
4095 // be queried via setValue()
4096 // to set their value..
4097 (field.store && !field.store.isLocal)
4101 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4102 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4103 field.setFromData(sd);
4106 field.setValue(values[id]);
4110 if(this.trackResetOnLoad){
4111 field.originalValue = field.getValue();
4117 //Roo.each(this.childForms || [], function (f) {
4118 // f.setValues(values);
4125 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4126 * they are returned as an array.
4127 * @param {Boolean} asString
4130 getValues : function(asString){
4131 //if (this.childForms) {
4132 // copy values from the child forms
4133 // Roo.each(this.childForms, function (f) {
4134 // this.setValues(f.getValues());
4140 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4141 if(asString === true){
4144 return Roo.urlDecode(fs);
4148 * Returns the fields in this form as an object with key/value pairs.
4149 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4152 getFieldValues : function(with_hidden)
4154 var items = this.getItems();
4156 items.each(function(f){
4160 var v = f.getValue();
4161 if (f.inputType =='radio') {
4162 if (typeof(ret[f.getName()]) == 'undefined') {
4163 ret[f.getName()] = ''; // empty..
4166 if (!f.el.dom.checked) {
4174 // not sure if this supported any more..
4175 if ((typeof(v) == 'object') && f.getRawValue) {
4176 v = f.getRawValue() ; // dates..
4178 // combo boxes where name != hiddenName...
4179 if (f.name != f.getName()) {
4180 ret[f.name] = f.getRawValue();
4182 ret[f.getName()] = v;
4189 * Clears all invalid messages in this form.
4190 * @return {BasicForm} this
4192 clearInvalid : function(){
4193 var items = this.getItems();
4195 items.each(function(f){
4206 * @return {BasicForm} this
4209 var items = this.getItems();
4210 items.each(function(f){
4214 Roo.each(this.childForms || [], function (f) {
4221 getItems : function()
4223 var r=new Roo.util.MixedCollection(false, function(o){
4224 return o.id || (o.id = Roo.id());
4226 var iter = function(el) {
4233 Roo.each(el.items,function(e) {
4252 * Ext JS Library 1.1.1
4253 * Copyright(c) 2006-2007, Ext JS, LLC.
4255 * Originally Released Under LGPL - original licence link has changed is not relivant.
4258 * <script type="text/javascript">
4261 * @class Roo.form.VTypes
4262 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4265 Roo.form.VTypes = function(){
4266 // closure these in so they are only created once.
4267 var alpha = /^[a-zA-Z_]+$/;
4268 var alphanum = /^[a-zA-Z0-9_]+$/;
4269 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4270 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4272 // All these messages and functions are configurable
4275 * The function used to validate email addresses
4276 * @param {String} value The email address
4278 'email' : function(v){
4279 return email.test(v);
4282 * The error text to display when the email validation function returns false
4285 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4287 * The keystroke filter mask to be applied on email input
4290 'emailMask' : /[a-z0-9_\.\-@]/i,
4293 * The function used to validate URLs
4294 * @param {String} value The URL
4296 'url' : function(v){
4300 * The error text to display when the url validation function returns false
4303 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4306 * The function used to validate alpha values
4307 * @param {String} value The value
4309 'alpha' : function(v){
4310 return alpha.test(v);
4313 * The error text to display when the alpha validation function returns false
4316 'alphaText' : 'This field should only contain letters and _',
4318 * The keystroke filter mask to be applied on alpha input
4321 'alphaMask' : /[a-z_]/i,
4324 * The function used to validate alphanumeric values
4325 * @param {String} value The value
4327 'alphanum' : function(v){
4328 return alphanum.test(v);
4331 * The error text to display when the alphanumeric validation function returns false
4334 'alphanumText' : 'This field should only contain letters, numbers and _',
4336 * The keystroke filter mask to be applied on alphanumeric input
4339 'alphanumMask' : /[a-z0-9_]/i
4349 * @class Roo.bootstrap.Input
4350 * @extends Roo.bootstrap.Component
4351 * Bootstrap Input class
4352 * @cfg {Boolean} disabled is it disabled
4353 * @cfg {String} fieldLabel - the label associated
4354 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4355 * @cfg {String} name name of the input
4356 * @cfg {string} fieldLabel - the label associated
4357 * @cfg {string} inputType - input / file submit ...
4358 * @cfg {string} placeholder - placeholder to put in text.
4359 * @cfg {string} before - input group add on before
4360 * @cfg {string} after - input group add on after
4361 * @cfg {string} size - (lg|sm) or leave empty..
4362 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4363 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4364 * @cfg {Number} md colspan out of 12 for computer-sized screens
4365 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4366 * @cfg {string} value default value of the input
4367 * @cfg {Number} labelWidth set the width of label (0-12)
4368 * @cfg {String} labelAlign (top|left)
4369 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4373 * Create a new Input
4374 * @param {Object} config The config object
4377 Roo.bootstrap.Input = function(config){
4378 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4383 * Fires when this field receives input focus.
4384 * @param {Roo.form.Field} this
4389 * Fires when this field loses input focus.
4390 * @param {Roo.form.Field} this
4395 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4396 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4397 * @param {Roo.form.Field} this
4398 * @param {Roo.EventObject} e The event object
4403 * Fires just before the field blurs if the field value has changed.
4404 * @param {Roo.form.Field} this
4405 * @param {Mixed} newValue The new value
4406 * @param {Mixed} oldValue The original value
4411 * Fires after the field has been marked as invalid.
4412 * @param {Roo.form.Field} this
4413 * @param {String} msg The validation message
4418 * Fires after the field has been validated with no errors.
4419 * @param {Roo.form.Field} this
4424 * Fires after the key up
4425 * @param {Roo.form.Field} this
4426 * @param {Roo.EventObject} e The event Object
4432 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4434 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4435 automatic validation (defaults to "keyup").
4437 validationEvent : "keyup",
4439 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4441 validateOnBlur : true,
4443 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4445 validationDelay : 250,
4447 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4449 focusClass : "x-form-focus", // not needed???
4453 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4455 invalidClass : "has-error",
4458 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4460 selectOnFocus : false,
4463 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4467 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4472 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4474 disableKeyFilter : false,
4477 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4481 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4485 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4487 blankText : "This field is required",
4490 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4494 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4496 maxLength : Number.MAX_VALUE,
4498 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4500 minLengthText : "The minimum length for this field is {0}",
4502 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4504 maxLengthText : "The maximum length for this field is {0}",
4508 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4509 * If available, this function will be called only after the basic validators all return true, and will be passed the
4510 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4514 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4515 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4516 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4520 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4543 parentLabelAlign : function()
4546 while (parent.parent()) {
4547 parent = parent.parent();
4548 if (typeof(parent.labelAlign) !='undefined') {
4549 return parent.labelAlign;
4556 getAutoCreate : function(){
4558 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4564 if(this.inputType != 'hidden'){
4565 cfg.cls = 'form-group' //input-group
4571 type : this.inputType,
4573 cls : 'form-control',
4574 placeholder : this.placeholder || ''
4578 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4579 input.maxLength = this.maxLength;
4582 if (this.disabled) {
4583 input.disabled=true;
4586 if (this.readOnly) {
4587 input.readonly=true;
4591 input.name = this.name;
4594 input.cls += ' input-' + this.size;
4597 ['xs','sm','md','lg'].map(function(size){
4598 if (settings[size]) {
4599 cfg.cls += ' col-' + size + '-' + settings[size];
4603 var inputblock = input;
4605 if (this.before || this.after) {
4608 cls : 'input-group',
4612 inputblock.cn.push({
4614 cls : 'input-group-addon',
4618 inputblock.cn.push(input);
4620 inputblock.cn.push({
4622 cls : 'input-group-addon',
4629 if (align ==='left' && this.fieldLabel.length) {
4630 Roo.log("left and has label");
4636 cls : 'control-label col-sm-' + this.labelWidth,
4637 html : this.fieldLabel
4641 cls : "col-sm-" + (12 - this.labelWidth),
4648 } else if ( this.fieldLabel.length) {
4654 //cls : 'input-group-addon',
4655 html : this.fieldLabel
4665 Roo.log(" no label && no align");
4674 Roo.log('input-parentType: ' + this.parentType);
4676 if (this.parentType === 'Navbar' && this.parent().bar) {
4677 cfg.cls += ' navbar-form';
4685 * return the real input element.
4687 inputEl: function ()
4689 return this.el.select('input.form-control',true).first();
4691 setDisabled : function(v)
4693 var i = this.inputEl().dom;
4695 i.removeAttribute('disabled');
4699 i.setAttribute('disabled','true');
4701 initEvents : function()
4704 this.inputEl().on("keydown" , this.fireKey, this);
4705 this.inputEl().on("focus", this.onFocus, this);
4706 this.inputEl().on("blur", this.onBlur, this);
4708 this.inputEl().relayEvent('keyup', this);
4710 // reference to original value for reset
4711 this.originalValue = this.getValue();
4712 //Roo.form.TextField.superclass.initEvents.call(this);
4713 if(this.validationEvent == 'keyup'){
4714 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4715 this.inputEl().on('keyup', this.filterValidation, this);
4717 else if(this.validationEvent !== false){
4718 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4721 if(this.selectOnFocus){
4722 this.on("focus", this.preFocus, this);
4725 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4726 this.inputEl().on("keypress", this.filterKeys, this);
4729 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4730 this.el.on("click", this.autoSize, this);
4733 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4734 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4738 filterValidation : function(e){
4739 if(!e.isNavKeyPress()){
4740 this.validationTask.delay(this.validationDelay);
4744 * Validates the field value
4745 * @return {Boolean} True if the value is valid, else false
4747 validate : function(){
4748 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4749 if(this.disabled || this.validateValue(this.getRawValue())){
4750 this.clearInvalid();
4758 * Validates a value according to the field's validation rules and marks the field as invalid
4759 * if the validation fails
4760 * @param {Mixed} value The value to validate
4761 * @return {Boolean} True if the value is valid, else false
4763 validateValue : function(value){
4764 if(value.length < 1) { // if it's blank
4765 if(this.allowBlank){
4766 this.clearInvalid();
4769 this.markInvalid(this.blankText);
4773 if(value.length < this.minLength){
4774 this.markInvalid(String.format(this.minLengthText, this.minLength));
4777 if(value.length > this.maxLength){
4778 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4782 var vt = Roo.form.VTypes;
4783 if(!vt[this.vtype](value, this)){
4784 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4788 if(typeof this.validator == "function"){
4789 var msg = this.validator(value);
4791 this.markInvalid(msg);
4795 if(this.regex && !this.regex.test(value)){
4796 this.markInvalid(this.regexText);
4805 fireKey : function(e){
4806 //Roo.log('field ' + e.getKey());
4807 if(e.isNavKeyPress()){
4808 this.fireEvent("specialkey", this, e);
4811 focus : function (selectText){
4813 this.inputEl().focus();
4814 if(selectText === true){
4815 this.inputEl().dom.select();
4821 onFocus : function(){
4822 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4823 // this.el.addClass(this.focusClass);
4826 this.hasFocus = true;
4827 this.startValue = this.getValue();
4828 this.fireEvent("focus", this);
4832 beforeBlur : Roo.emptyFn,
4836 onBlur : function(){
4838 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4839 //this.el.removeClass(this.focusClass);
4841 this.hasFocus = false;
4842 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4845 var v = this.getValue();
4846 if(String(v) !== String(this.startValue)){
4847 this.fireEvent('change', this, v, this.startValue);
4849 this.fireEvent("blur", this);
4853 * Resets the current field value to the originally loaded value and clears any validation messages
4856 this.setValue(this.originalValue);
4857 this.clearInvalid();
4860 * Returns the name of the field
4861 * @return {Mixed} name The name field
4863 getName: function(){
4867 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4868 * @return {Mixed} value The field value
4870 getValue : function(){
4871 return this.inputEl().getValue();
4874 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4875 * @return {Mixed} value The field value
4877 getRawValue : function(){
4878 var v = this.inputEl().getValue();
4884 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4885 * @param {Mixed} value The value to set
4887 setRawValue : function(v){
4888 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4891 selectText : function(start, end){
4892 var v = this.getRawValue();
4894 start = start === undefined ? 0 : start;
4895 end = end === undefined ? v.length : end;
4896 var d = this.inputEl().dom;
4897 if(d.setSelectionRange){
4898 d.setSelectionRange(start, end);
4899 }else if(d.createTextRange){
4900 var range = d.createTextRange();
4901 range.moveStart("character", start);
4902 range.moveEnd("character", v.length-end);
4909 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4910 * @param {Mixed} value The value to set
4912 setValue : function(v){
4915 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4921 processValue : function(value){
4922 if(this.stripCharsRe){
4923 var newValue = value.replace(this.stripCharsRe, '');
4924 if(newValue !== value){
4925 this.setRawValue(newValue);
4932 preFocus : function(){
4934 if(this.selectOnFocus){
4935 this.inputEl().dom.select();
4938 filterKeys : function(e){
4940 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4943 var c = e.getCharCode(), cc = String.fromCharCode(c);
4944 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4947 if(!this.maskRe.test(cc)){
4952 * Clear any invalid styles/messages for this field
4954 clearInvalid : function(){
4956 if(!this.el || this.preventMark){ // not rendered
4959 this.el.removeClass(this.invalidClass);
4961 switch(this.msgTarget){
4963 this.el.dom.qtip = '';
4966 this.el.dom.title = '';
4970 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4975 this.errorIcon.dom.qtip = '';
4976 this.errorIcon.hide();
4977 this.un('resize', this.alignErrorIcon, this);
4981 var t = Roo.getDom(this.msgTarget);
4983 t.style.display = 'none';
4987 this.fireEvent('valid', this);
4990 * Mark this field as invalid
4991 * @param {String} msg The validation message
4993 markInvalid : function(msg){
4994 if(!this.el || this.preventMark){ // not rendered
4997 this.el.addClass(this.invalidClass);
4999 msg = msg || this.invalidText;
5000 switch(this.msgTarget){
5002 this.el.dom.qtip = msg;
5003 this.el.dom.qclass = 'x-form-invalid-tip';
5004 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5005 Roo.QuickTips.enable();
5009 this.el.dom.title = msg;
5013 var elp = this.el.findParent('.x-form-element', 5, true);
5014 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5015 this.errorEl.setWidth(elp.getWidth(true)-20);
5017 this.errorEl.update(msg);
5018 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5021 if(!this.errorIcon){
5022 var elp = this.el.findParent('.x-form-element', 5, true);
5023 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5025 this.alignErrorIcon();
5026 this.errorIcon.dom.qtip = msg;
5027 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5028 this.errorIcon.show();
5029 this.on('resize', this.alignErrorIcon, this);
5032 var t = Roo.getDom(this.msgTarget);
5034 t.style.display = this.msgDisplay;
5038 this.fireEvent('invalid', this, msg);
5041 SafariOnKeyDown : function(event)
5043 // this is a workaround for a password hang bug on chrome/ webkit.
5045 var isSelectAll = false;
5047 if(this.inputEl().dom.selectionEnd > 0){
5048 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5050 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5051 event.preventDefault();
5056 if(isSelectAll){ // backspace and delete key
5058 event.preventDefault();
5059 // this is very hacky as keydown always get's upper case.
5061 var cc = String.fromCharCode(event.getCharCode());
5062 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5066 adjustWidth : function(tag, w){
5067 tag = tag.toLowerCase();
5068 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5069 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5073 if(tag == 'textarea'){
5076 }else if(Roo.isOpera){
5080 if(tag == 'textarea'){
5099 * @class Roo.bootstrap.TextArea
5100 * @extends Roo.bootstrap.Input
5101 * Bootstrap TextArea class
5102 * @cfg {Number} cols Specifies the visible width of a text area
5103 * @cfg {Number} rows Specifies the visible number of lines in a text area
5104 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5105 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5106 * @cfg {string} html text
5109 * Create a new TextArea
5110 * @param {Object} config The config object
5113 Roo.bootstrap.TextArea = function(config){
5114 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5118 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5128 getAutoCreate : function(){
5130 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5141 value : this.value || '',
5142 html: this.html || '',
5143 cls : 'form-control',
5144 placeholder : this.placeholder || ''
5148 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5149 input.maxLength = this.maxLength;
5153 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5157 input.cols = this.cols;
5160 if (this.readOnly) {
5161 input.readonly = true;
5165 input.name = this.name;
5169 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5173 ['xs','sm','md','lg'].map(function(size){
5174 if (settings[size]) {
5175 cfg.cls += ' col-' + size + '-' + settings[size];
5179 var inputblock = input;
5181 if (this.before || this.after) {
5184 cls : 'input-group',
5188 inputblock.cn.push({
5190 cls : 'input-group-addon',
5194 inputblock.cn.push(input);
5196 inputblock.cn.push({
5198 cls : 'input-group-addon',
5205 if (align ==='left' && this.fieldLabel.length) {
5206 Roo.log("left and has label");
5212 cls : 'control-label col-sm-' + this.labelWidth,
5213 html : this.fieldLabel
5217 cls : "col-sm-" + (12 - this.labelWidth),
5224 } else if ( this.fieldLabel.length) {
5230 //cls : 'input-group-addon',
5231 html : this.fieldLabel
5241 Roo.log(" no label && no align");
5251 if (this.disabled) {
5252 input.disabled=true;
5259 * return the real textarea element.
5261 inputEl: function ()
5263 return this.el.select('textarea.form-control',true).first();
5271 * trigger field - base class for combo..
5276 * @class Roo.bootstrap.TriggerField
5277 * @extends Roo.bootstrap.Input
5278 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5279 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5280 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5281 * for which you can provide a custom implementation. For example:
5283 var trigger = new Roo.bootstrap.TriggerField();
5284 trigger.onTriggerClick = myTriggerFn;
5285 trigger.applyTo('my-field');
5288 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5289 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5290 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5291 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5293 * Create a new TriggerField.
5294 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5295 * to the base TextField)
5297 Roo.bootstrap.TriggerField = function(config){
5298 this.mimicing = false;
5299 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5302 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5304 * @cfg {String} triggerClass A CSS class to apply to the trigger
5307 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5311 /** @cfg {Boolean} grow @hide */
5312 /** @cfg {Number} growMin @hide */
5313 /** @cfg {Number} growMax @hide */
5319 autoSize: Roo.emptyFn,
5326 actionMode : 'wrap',
5330 getAutoCreate : function(){
5332 var parent = this.parent();
5334 var align = this.parentLabelAlign();
5339 cls: 'form-group' //input-group
5346 type : this.inputType,
5347 cls : 'form-control',
5348 autocomplete: 'off',
5349 placeholder : this.placeholder || ''
5353 input.name = this.name;
5356 input.cls += ' input-' + this.size;
5359 if (this.disabled) {
5360 input.disabled=true;
5363 var inputblock = input;
5365 if (this.before || this.after) {
5368 cls : 'input-group',
5372 inputblock.cn.push({
5374 cls : 'input-group-addon',
5378 inputblock.cn.push(input);
5380 inputblock.cn.push({
5382 cls : 'input-group-addon',
5395 cls: 'form-hidden-field'
5403 Roo.log('multiple');
5411 cls: 'form-hidden-field'
5415 cls: 'select2-choices',
5419 cls: 'select2-search-field',
5432 cls: 'select2-container input-group',
5437 cls: 'typeahead typeahead-long dropdown-menu',
5438 style: 'display:none'
5446 cls : 'input-group-addon btn dropdown-toggle',
5454 cls: 'combobox-clear',
5468 combobox.cls += ' select2-container-multi';
5471 if (align ==='left' && this.fieldLabel.length) {
5473 Roo.log("left and has label");
5479 cls : 'control-label col-sm-' + this.labelWidth,
5480 html : this.fieldLabel
5484 cls : "col-sm-" + (12 - this.labelWidth),
5491 } else if ( this.fieldLabel.length) {
5497 //cls : 'input-group-addon',
5498 html : this.fieldLabel
5508 Roo.log(" no label && no align");
5515 ['xs','sm','md','lg'].map(function(size){
5516 if (settings[size]) {
5517 cfg.cls += ' col-' + size + '-' + settings[size];
5528 onResize : function(w, h){
5529 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5530 // if(typeof w == 'number'){
5531 // var x = w - this.trigger.getWidth();
5532 // this.inputEl().setWidth(this.adjustWidth('input', x));
5533 // this.trigger.setStyle('left', x+'px');
5538 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5541 getResizeEl : function(){
5542 return this.inputEl();
5546 getPositionEl : function(){
5547 return this.inputEl();
5551 alignErrorIcon : function(){
5552 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5556 initEvents : function(){
5558 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5559 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5561 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5562 if(this.hideTrigger){
5563 this.trigger.setDisplayed(false);
5565 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5569 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5572 //this.trigger.addClassOnOver('x-form-trigger-over');
5573 //this.trigger.addClassOnClick('x-form-trigger-click');
5576 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5581 initTrigger : function(){
5586 onDestroy : function(){
5588 this.trigger.removeAllListeners();
5589 // this.trigger.remove();
5592 // this.wrap.remove();
5594 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5598 onFocus : function(){
5599 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5602 this.wrap.addClass('x-trigger-wrap-focus');
5603 this.mimicing = true;
5604 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5605 if(this.monitorTab){
5606 this.el.on("keydown", this.checkTab, this);
5613 checkTab : function(e){
5614 if(e.getKey() == e.TAB){
5620 onBlur : function(){
5625 mimicBlur : function(e, t){
5627 if(!this.wrap.contains(t) && this.validateBlur()){
5634 triggerBlur : function(){
5635 this.mimicing = false;
5636 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5637 if(this.monitorTab){
5638 this.el.un("keydown", this.checkTab, this);
5640 //this.wrap.removeClass('x-trigger-wrap-focus');
5641 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5645 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5646 validateBlur : function(e, t){
5651 onDisable : function(){
5652 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5654 // this.wrap.addClass('x-item-disabled');
5659 onEnable : function(){
5660 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5662 // this.el.removeClass('x-item-disabled');
5667 onShow : function(){
5668 var ae = this.getActionEl();
5671 ae.dom.style.display = '';
5672 ae.dom.style.visibility = 'visible';
5678 onHide : function(){
5679 var ae = this.getActionEl();
5680 ae.dom.style.display = 'none';
5684 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5685 * by an implementing function.
5687 * @param {EventObject} e
5689 onTriggerClick : Roo.emptyFn
5693 * Ext JS Library 1.1.1
5694 * Copyright(c) 2006-2007, Ext JS, LLC.
5696 * Originally Released Under LGPL - original licence link has changed is not relivant.
5699 * <script type="text/javascript">
5704 * @class Roo.data.SortTypes
5706 * Defines the default sorting (casting?) comparison functions used when sorting data.
5708 Roo.data.SortTypes = {
5710 * Default sort that does nothing
5711 * @param {Mixed} s The value being converted
5712 * @return {Mixed} The comparison value
5719 * The regular expression used to strip tags
5723 stripTagsRE : /<\/?[^>]+>/gi,
5726 * Strips all HTML tags to sort on text only
5727 * @param {Mixed} s The value being converted
5728 * @return {String} The comparison value
5730 asText : function(s){
5731 return String(s).replace(this.stripTagsRE, "");
5735 * Strips all HTML tags to sort on text only - Case insensitive
5736 * @param {Mixed} s The value being converted
5737 * @return {String} The comparison value
5739 asUCText : function(s){
5740 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5744 * Case insensitive string
5745 * @param {Mixed} s The value being converted
5746 * @return {String} The comparison value
5748 asUCString : function(s) {
5749 return String(s).toUpperCase();
5754 * @param {Mixed} s The value being converted
5755 * @return {Number} The comparison value
5757 asDate : function(s) {
5761 if(s instanceof Date){
5764 return Date.parse(String(s));
5769 * @param {Mixed} s The value being converted
5770 * @return {Float} The comparison value
5772 asFloat : function(s) {
5773 var val = parseFloat(String(s).replace(/,/g, ""));
5774 if(isNaN(val)) val = 0;
5780 * @param {Mixed} s The value being converted
5781 * @return {Number} The comparison value
5783 asInt : function(s) {
5784 var val = parseInt(String(s).replace(/,/g, ""));
5785 if(isNaN(val)) val = 0;
5790 * Ext JS Library 1.1.1
5791 * Copyright(c) 2006-2007, Ext JS, LLC.
5793 * Originally Released Under LGPL - original licence link has changed is not relivant.
5796 * <script type="text/javascript">
5800 * @class Roo.data.Record
5801 * Instances of this class encapsulate both record <em>definition</em> information, and record
5802 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5803 * to access Records cached in an {@link Roo.data.Store} object.<br>
5805 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5806 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5809 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5811 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5812 * {@link #create}. The parameters are the same.
5813 * @param {Array} data An associative Array of data values keyed by the field name.
5814 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5815 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5816 * not specified an integer id is generated.
5818 Roo.data.Record = function(data, id){
5819 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5824 * Generate a constructor for a specific record layout.
5825 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5826 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5827 * Each field definition object may contain the following properties: <ul>
5828 * <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,
5829 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5830 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5831 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5832 * is being used, then this is a string containing the javascript expression to reference the data relative to
5833 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5834 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5835 * this may be omitted.</p></li>
5836 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5837 * <ul><li>auto (Default, implies no conversion)</li>
5842 * <li>date</li></ul></p></li>
5843 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5844 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5845 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5846 * by the Reader into an object that will be stored in the Record. It is passed the
5847 * following parameters:<ul>
5848 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5850 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5852 * <br>usage:<br><pre><code>
5853 var TopicRecord = Roo.data.Record.create(
5854 {name: 'title', mapping: 'topic_title'},
5855 {name: 'author', mapping: 'username'},
5856 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5857 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5858 {name: 'lastPoster', mapping: 'user2'},
5859 {name: 'excerpt', mapping: 'post_text'}
5862 var myNewRecord = new TopicRecord({
5863 title: 'Do my job please',
5866 lastPost: new Date(),
5867 lastPoster: 'Animal',
5868 excerpt: 'No way dude!'
5870 myStore.add(myNewRecord);
5875 Roo.data.Record.create = function(o){
5877 f.superclass.constructor.apply(this, arguments);
5879 Roo.extend(f, Roo.data.Record);
5880 var p = f.prototype;
5881 p.fields = new Roo.util.MixedCollection(false, function(field){
5884 for(var i = 0, len = o.length; i < len; i++){
5885 p.fields.add(new Roo.data.Field(o[i]));
5887 f.getField = function(name){
5888 return p.fields.get(name);
5893 Roo.data.Record.AUTO_ID = 1000;
5894 Roo.data.Record.EDIT = 'edit';
5895 Roo.data.Record.REJECT = 'reject';
5896 Roo.data.Record.COMMIT = 'commit';
5898 Roo.data.Record.prototype = {
5900 * Readonly flag - true if this record has been modified.
5909 join : function(store){
5914 * Set the named field to the specified value.
5915 * @param {String} name The name of the field to set.
5916 * @param {Object} value The value to set the field to.
5918 set : function(name, value){
5919 if(this.data[name] == value){
5926 if(typeof this.modified[name] == 'undefined'){
5927 this.modified[name] = this.data[name];
5929 this.data[name] = value;
5930 if(!this.editing && this.store){
5931 this.store.afterEdit(this);
5936 * Get the value of the named field.
5937 * @param {String} name The name of the field to get the value of.
5938 * @return {Object} The value of the field.
5940 get : function(name){
5941 return this.data[name];
5945 beginEdit : function(){
5946 this.editing = true;
5951 cancelEdit : function(){
5952 this.editing = false;
5953 delete this.modified;
5957 endEdit : function(){
5958 this.editing = false;
5959 if(this.dirty && this.store){
5960 this.store.afterEdit(this);
5965 * Usually called by the {@link Roo.data.Store} which owns the Record.
5966 * Rejects all changes made to the Record since either creation, or the last commit operation.
5967 * Modified fields are reverted to their original values.
5969 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5970 * of reject operations.
5972 reject : function(){
5973 var m = this.modified;
5975 if(typeof m[n] != "function"){
5976 this.data[n] = m[n];
5980 delete this.modified;
5981 this.editing = false;
5983 this.store.afterReject(this);
5988 * Usually called by the {@link Roo.data.Store} which owns the Record.
5989 * Commits all changes made to the Record since either creation, or the last commit operation.
5991 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5992 * of commit operations.
5994 commit : function(){
5996 delete this.modified;
5997 this.editing = false;
5999 this.store.afterCommit(this);
6004 hasError : function(){
6005 return this.error != null;
6009 clearError : function(){
6014 * Creates a copy of this record.
6015 * @param {String} id (optional) A new record id if you don't want to use this record's id
6018 copy : function(newId) {
6019 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6023 * Ext JS Library 1.1.1
6024 * Copyright(c) 2006-2007, Ext JS, LLC.
6026 * Originally Released Under LGPL - original licence link has changed is not relivant.
6029 * <script type="text/javascript">
6035 * @class Roo.data.Store
6036 * @extends Roo.util.Observable
6037 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6038 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6040 * 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
6041 * has no knowledge of the format of the data returned by the Proxy.<br>
6043 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6044 * instances from the data object. These records are cached and made available through accessor functions.
6046 * Creates a new Store.
6047 * @param {Object} config A config object containing the objects needed for the Store to access data,
6048 * and read the data into Records.
6050 Roo.data.Store = function(config){
6051 this.data = new Roo.util.MixedCollection(false);
6052 this.data.getKey = function(o){
6055 this.baseParams = {};
6062 "multisort" : "_multisort"
6065 if(config && config.data){
6066 this.inlineData = config.data;
6070 Roo.apply(this, config);
6072 if(this.reader){ // reader passed
6073 this.reader = Roo.factory(this.reader, Roo.data);
6074 this.reader.xmodule = this.xmodule || false;
6075 if(!this.recordType){
6076 this.recordType = this.reader.recordType;
6078 if(this.reader.onMetaChange){
6079 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6083 if(this.recordType){
6084 this.fields = this.recordType.prototype.fields;
6090 * @event datachanged
6091 * Fires when the data cache has changed, and a widget which is using this Store
6092 * as a Record cache should refresh its view.
6093 * @param {Store} this
6098 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6099 * @param {Store} this
6100 * @param {Object} meta The JSON metadata
6105 * Fires when Records have been added to the Store
6106 * @param {Store} this
6107 * @param {Roo.data.Record[]} records The array of Records added
6108 * @param {Number} index The index at which the record(s) were added
6113 * Fires when a Record has been removed from the Store
6114 * @param {Store} this
6115 * @param {Roo.data.Record} record The Record that was removed
6116 * @param {Number} index The index at which the record was removed
6121 * Fires when a Record has been updated
6122 * @param {Store} this
6123 * @param {Roo.data.Record} record The Record that was updated
6124 * @param {String} operation The update operation being performed. Value may be one of:
6126 Roo.data.Record.EDIT
6127 Roo.data.Record.REJECT
6128 Roo.data.Record.COMMIT
6134 * Fires when the data cache has been cleared.
6135 * @param {Store} this
6140 * Fires before a request is made for a new data object. If the beforeload handler returns false
6141 * the load action will be canceled.
6142 * @param {Store} this
6143 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6147 * @event beforeloadadd
6148 * Fires after a new set of Records has been loaded.
6149 * @param {Store} this
6150 * @param {Roo.data.Record[]} records The Records that were loaded
6151 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6153 beforeloadadd : true,
6156 * Fires after a new set of Records has been loaded, before they are added to the store.
6157 * @param {Store} this
6158 * @param {Roo.data.Record[]} records The Records that were loaded
6159 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6160 * @params {Object} return from reader
6164 * @event loadexception
6165 * Fires if an exception occurs in the Proxy during loading.
6166 * Called with the signature of the Proxy's "loadexception" event.
6167 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6170 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6171 * @param {Object} load options
6172 * @param {Object} jsonData from your request (normally this contains the Exception)
6174 loadexception : true
6178 this.proxy = Roo.factory(this.proxy, Roo.data);
6179 this.proxy.xmodule = this.xmodule || false;
6180 this.relayEvents(this.proxy, ["loadexception"]);
6182 this.sortToggle = {};
6183 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6185 Roo.data.Store.superclass.constructor.call(this);
6187 if(this.inlineData){
6188 this.loadData(this.inlineData);
6189 delete this.inlineData;
6193 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6195 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6196 * without a remote query - used by combo/forms at present.
6200 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6203 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6206 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6207 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6210 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6211 * on any HTTP request
6214 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6217 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6221 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6222 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6227 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6228 * loaded or when a record is removed. (defaults to false).
6230 pruneModifiedRecords : false,
6236 * Add Records to the Store and fires the add event.
6237 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6239 add : function(records){
6240 records = [].concat(records);
6241 for(var i = 0, len = records.length; i < len; i++){
6242 records[i].join(this);
6244 var index = this.data.length;
6245 this.data.addAll(records);
6246 this.fireEvent("add", this, records, index);
6250 * Remove a Record from the Store and fires the remove event.
6251 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6253 remove : function(record){
6254 var index = this.data.indexOf(record);
6255 this.data.removeAt(index);
6256 if(this.pruneModifiedRecords){
6257 this.modified.remove(record);
6259 this.fireEvent("remove", this, record, index);
6263 * Remove all Records from the Store and fires the clear event.
6265 removeAll : function(){
6267 if(this.pruneModifiedRecords){
6270 this.fireEvent("clear", this);
6274 * Inserts Records to the Store at the given index and fires the add event.
6275 * @param {Number} index The start index at which to insert the passed Records.
6276 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6278 insert : function(index, records){
6279 records = [].concat(records);
6280 for(var i = 0, len = records.length; i < len; i++){
6281 this.data.insert(index, records[i]);
6282 records[i].join(this);
6284 this.fireEvent("add", this, records, index);
6288 * Get the index within the cache of the passed Record.
6289 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6290 * @return {Number} The index of the passed Record. Returns -1 if not found.
6292 indexOf : function(record){
6293 return this.data.indexOf(record);
6297 * Get the index within the cache of the Record with the passed id.
6298 * @param {String} id The id of the Record to find.
6299 * @return {Number} The index of the Record. Returns -1 if not found.
6301 indexOfId : function(id){
6302 return this.data.indexOfKey(id);
6306 * Get the Record with the specified id.
6307 * @param {String} id The id of the Record to find.
6308 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6310 getById : function(id){
6311 return this.data.key(id);
6315 * Get the Record at the specified index.
6316 * @param {Number} index The index of the Record to find.
6317 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6319 getAt : function(index){
6320 return this.data.itemAt(index);
6324 * Returns a range of Records between specified indices.
6325 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6326 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6327 * @return {Roo.data.Record[]} An array of Records
6329 getRange : function(start, end){
6330 return this.data.getRange(start, end);
6334 storeOptions : function(o){
6335 o = Roo.apply({}, o);
6338 this.lastOptions = o;
6342 * Loads the Record cache from the configured Proxy using the configured Reader.
6344 * If using remote paging, then the first load call must specify the <em>start</em>
6345 * and <em>limit</em> properties in the options.params property to establish the initial
6346 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6348 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6349 * and this call will return before the new data has been loaded. Perform any post-processing
6350 * in a callback function, or in a "load" event handler.</strong>
6352 * @param {Object} options An object containing properties which control loading options:<ul>
6353 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6354 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6355 * passed the following arguments:<ul>
6356 * <li>r : Roo.data.Record[]</li>
6357 * <li>options: Options object from the load call</li>
6358 * <li>success: Boolean success indicator</li></ul></li>
6359 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6360 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6363 load : function(options){
6364 options = options || {};
6365 if(this.fireEvent("beforeload", this, options) !== false){
6366 this.storeOptions(options);
6367 var p = Roo.apply(options.params || {}, this.baseParams);
6368 // if meta was not loaded from remote source.. try requesting it.
6369 if (!this.reader.metaFromRemote) {
6372 if(this.sortInfo && this.remoteSort){
6373 var pn = this.paramNames;
6374 p[pn["sort"]] = this.sortInfo.field;
6375 p[pn["dir"]] = this.sortInfo.direction;
6377 if (this.multiSort) {
6378 var pn = this.paramNames;
6379 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6382 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6387 * Reloads the Record cache from the configured Proxy using the configured Reader and
6388 * the options from the last load operation performed.
6389 * @param {Object} options (optional) An object containing properties which may override the options
6390 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6391 * the most recently used options are reused).
6393 reload : function(options){
6394 this.load(Roo.applyIf(options||{}, this.lastOptions));
6398 // Called as a callback by the Reader during a load operation.
6399 loadRecords : function(o, options, success){
6400 if(!o || success === false){
6401 if(success !== false){
6402 this.fireEvent("load", this, [], options, o);
6404 if(options.callback){
6405 options.callback.call(options.scope || this, [], options, false);
6409 // if data returned failure - throw an exception.
6410 if (o.success === false) {
6411 // show a message if no listener is registered.
6412 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6413 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6415 // loadmask wil be hooked into this..
6416 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6419 var r = o.records, t = o.totalRecords || r.length;
6421 this.fireEvent("beforeloadadd", this, r, options, o);
6423 if(!options || options.add !== true){
6424 if(this.pruneModifiedRecords){
6427 for(var i = 0, len = r.length; i < len; i++){
6431 this.data = this.snapshot;
6432 delete this.snapshot;
6435 this.data.addAll(r);
6436 this.totalLength = t;
6438 this.fireEvent("datachanged", this);
6440 this.totalLength = Math.max(t, this.data.length+r.length);
6443 this.fireEvent("load", this, r, options, o);
6444 if(options.callback){
6445 options.callback.call(options.scope || this, r, options, true);
6451 * Loads data from a passed data block. A Reader which understands the format of the data
6452 * must have been configured in the constructor.
6453 * @param {Object} data The data block from which to read the Records. The format of the data expected
6454 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6455 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6457 loadData : function(o, append){
6458 var r = this.reader.readRecords(o);
6459 this.loadRecords(r, {add: append}, true);
6463 * Gets the number of cached records.
6465 * <em>If using paging, this may not be the total size of the dataset. If the data object
6466 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6467 * the data set size</em>
6469 getCount : function(){
6470 return this.data.length || 0;
6474 * Gets the total number of records in the dataset as returned by the server.
6476 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6477 * the dataset size</em>
6479 getTotalCount : function(){
6480 return this.totalLength || 0;
6484 * Returns the sort state of the Store as an object with two properties:
6486 field {String} The name of the field by which the Records are sorted
6487 direction {String} The sort order, "ASC" or "DESC"
6490 getSortState : function(){
6491 return this.sortInfo;
6495 applySort : function(){
6496 if(this.sortInfo && !this.remoteSort){
6497 var s = this.sortInfo, f = s.field;
6498 var st = this.fields.get(f).sortType;
6499 var fn = function(r1, r2){
6500 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6501 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6503 this.data.sort(s.direction, fn);
6504 if(this.snapshot && this.snapshot != this.data){
6505 this.snapshot.sort(s.direction, fn);
6511 * Sets the default sort column and order to be used by the next load operation.
6512 * @param {String} fieldName The name of the field to sort by.
6513 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6515 setDefaultSort : function(field, dir){
6516 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6521 * If remote sorting is used, the sort is performed on the server, and the cache is
6522 * reloaded. If local sorting is used, the cache is sorted internally.
6523 * @param {String} fieldName The name of the field to sort by.
6524 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6526 sort : function(fieldName, dir){
6527 var f = this.fields.get(fieldName);
6529 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6531 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6532 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6537 this.sortToggle[f.name] = dir;
6538 this.sortInfo = {field: f.name, direction: dir};
6539 if(!this.remoteSort){
6541 this.fireEvent("datachanged", this);
6543 this.load(this.lastOptions);
6548 * Calls the specified function for each of the Records in the cache.
6549 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6550 * Returning <em>false</em> aborts and exits the iteration.
6551 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6553 each : function(fn, scope){
6554 this.data.each(fn, scope);
6558 * Gets all records modified since the last commit. Modified records are persisted across load operations
6559 * (e.g., during paging).
6560 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6562 getModifiedRecords : function(){
6563 return this.modified;
6567 createFilterFn : function(property, value, anyMatch){
6568 if(!value.exec){ // not a regex
6569 value = String(value);
6570 if(value.length == 0){
6573 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6576 return value.test(r.data[property]);
6581 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6582 * @param {String} property A field on your records
6583 * @param {Number} start The record index to start at (defaults to 0)
6584 * @param {Number} end The last record index to include (defaults to length - 1)
6585 * @return {Number} The sum
6587 sum : function(property, start, end){
6588 var rs = this.data.items, v = 0;
6590 end = (end || end === 0) ? end : rs.length-1;
6592 for(var i = start; i <= end; i++){
6593 v += (rs[i].data[property] || 0);
6599 * Filter the records by a specified property.
6600 * @param {String} field A field on your records
6601 * @param {String/RegExp} value Either a string that the field
6602 * should start with or a RegExp to test against the field
6603 * @param {Boolean} anyMatch True to match any part not just the beginning
6605 filter : function(property, value, anyMatch){
6606 var fn = this.createFilterFn(property, value, anyMatch);
6607 return fn ? this.filterBy(fn) : this.clearFilter();
6611 * Filter by a function. The specified function will be called with each
6612 * record in this data source. If the function returns true the record is included,
6613 * otherwise it is filtered.
6614 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6615 * @param {Object} scope (optional) The scope of the function (defaults to this)
6617 filterBy : function(fn, scope){
6618 this.snapshot = this.snapshot || this.data;
6619 this.data = this.queryBy(fn, scope||this);
6620 this.fireEvent("datachanged", this);
6624 * Query the records by a specified property.
6625 * @param {String} field A field on your records
6626 * @param {String/RegExp} value Either a string that the field
6627 * should start with or a RegExp to test against the field
6628 * @param {Boolean} anyMatch True to match any part not just the beginning
6629 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6631 query : function(property, value, anyMatch){
6632 var fn = this.createFilterFn(property, value, anyMatch);
6633 return fn ? this.queryBy(fn) : this.data.clone();
6637 * Query by a function. The specified function will be called with each
6638 * record in this data source. If the function returns true the record is included
6640 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6641 * @param {Object} scope (optional) The scope of the function (defaults to this)
6642 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6644 queryBy : function(fn, scope){
6645 var data = this.snapshot || this.data;
6646 return data.filterBy(fn, scope||this);
6650 * Collects unique values for a particular dataIndex from this store.
6651 * @param {String} dataIndex The property to collect
6652 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6653 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6654 * @return {Array} An array of the unique values
6656 collect : function(dataIndex, allowNull, bypassFilter){
6657 var d = (bypassFilter === true && this.snapshot) ?
6658 this.snapshot.items : this.data.items;
6659 var v, sv, r = [], l = {};
6660 for(var i = 0, len = d.length; i < len; i++){
6661 v = d[i].data[dataIndex];
6663 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6672 * Revert to a view of the Record cache with no filtering applied.
6673 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6675 clearFilter : function(suppressEvent){
6676 if(this.snapshot && this.snapshot != this.data){
6677 this.data = this.snapshot;
6678 delete this.snapshot;
6679 if(suppressEvent !== true){
6680 this.fireEvent("datachanged", this);
6686 afterEdit : function(record){
6687 if(this.modified.indexOf(record) == -1){
6688 this.modified.push(record);
6690 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6694 afterReject : function(record){
6695 this.modified.remove(record);
6696 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6700 afterCommit : function(record){
6701 this.modified.remove(record);
6702 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6706 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6707 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6709 commitChanges : function(){
6710 var m = this.modified.slice(0);
6712 for(var i = 0, len = m.length; i < len; i++){
6718 * Cancel outstanding changes on all changed records.
6720 rejectChanges : function(){
6721 var m = this.modified.slice(0);
6723 for(var i = 0, len = m.length; i < len; i++){
6728 onMetaChange : function(meta, rtype, o){
6729 this.recordType = rtype;
6730 this.fields = rtype.prototype.fields;
6731 delete this.snapshot;
6732 this.sortInfo = meta.sortInfo || this.sortInfo;
6734 this.fireEvent('metachange', this, this.reader.meta);
6737 moveIndex : function(data, type)
6739 var index = this.indexOf(data);
6741 var newIndex = index + type;
6745 this.insert(newIndex, data);
6750 * Ext JS Library 1.1.1
6751 * Copyright(c) 2006-2007, Ext JS, LLC.
6753 * Originally Released Under LGPL - original licence link has changed is not relivant.
6756 * <script type="text/javascript">
6760 * @class Roo.data.SimpleStore
6761 * @extends Roo.data.Store
6762 * Small helper class to make creating Stores from Array data easier.
6763 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6764 * @cfg {Array} fields An array of field definition objects, or field name strings.
6765 * @cfg {Array} data The multi-dimensional array of data
6767 * @param {Object} config
6769 Roo.data.SimpleStore = function(config){
6770 Roo.data.SimpleStore.superclass.constructor.call(this, {
6772 reader: new Roo.data.ArrayReader({
6775 Roo.data.Record.create(config.fields)
6777 proxy : new Roo.data.MemoryProxy(config.data)
6781 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6783 * Ext JS Library 1.1.1
6784 * Copyright(c) 2006-2007, Ext JS, LLC.
6786 * Originally Released Under LGPL - original licence link has changed is not relivant.
6789 * <script type="text/javascript">
6794 * @extends Roo.data.Store
6795 * @class Roo.data.JsonStore
6796 * Small helper class to make creating Stores for JSON data easier. <br/>
6798 var store = new Roo.data.JsonStore({
6799 url: 'get-images.php',
6801 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6804 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6805 * JsonReader and HttpProxy (unless inline data is provided).</b>
6806 * @cfg {Array} fields An array of field definition objects, or field name strings.
6808 * @param {Object} config
6810 Roo.data.JsonStore = function(c){
6811 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6812 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6813 reader: new Roo.data.JsonReader(c, c.fields)
6816 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6818 * Ext JS Library 1.1.1
6819 * Copyright(c) 2006-2007, Ext JS, LLC.
6821 * Originally Released Under LGPL - original licence link has changed is not relivant.
6824 * <script type="text/javascript">
6828 Roo.data.Field = function(config){
6829 if(typeof config == "string"){
6830 config = {name: config};
6832 Roo.apply(this, config);
6838 var st = Roo.data.SortTypes;
6839 // named sortTypes are supported, here we look them up
6840 if(typeof this.sortType == "string"){
6841 this.sortType = st[this.sortType];
6844 // set default sortType for strings and dates
6848 this.sortType = st.asUCString;
6851 this.sortType = st.asDate;
6854 this.sortType = st.none;
6859 var stripRe = /[\$,%]/g;
6861 // prebuilt conversion function for this field, instead of
6862 // switching every time we're reading a value
6864 var cv, dateFormat = this.dateFormat;
6869 cv = function(v){ return v; };
6872 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6876 return v !== undefined && v !== null && v !== '' ?
6877 parseInt(String(v).replace(stripRe, ""), 10) : '';
6882 return v !== undefined && v !== null && v !== '' ?
6883 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6888 cv = function(v){ return v === true || v === "true" || v == 1; };
6895 if(v instanceof Date){
6899 if(dateFormat == "timestamp"){
6900 return new Date(v*1000);
6902 return Date.parseDate(v, dateFormat);
6904 var parsed = Date.parse(v);
6905 return parsed ? new Date(parsed) : null;
6914 Roo.data.Field.prototype = {
6922 * Ext JS Library 1.1.1
6923 * Copyright(c) 2006-2007, Ext JS, LLC.
6925 * Originally Released Under LGPL - original licence link has changed is not relivant.
6928 * <script type="text/javascript">
6931 // Base class for reading structured data from a data source. This class is intended to be
6932 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6935 * @class Roo.data.DataReader
6936 * Base class for reading structured data from a data source. This class is intended to be
6937 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6940 Roo.data.DataReader = function(meta, recordType){
6944 this.recordType = recordType instanceof Array ?
6945 Roo.data.Record.create(recordType) : recordType;
6948 Roo.data.DataReader.prototype = {
6950 * Create an empty record
6951 * @param {Object} data (optional) - overlay some values
6952 * @return {Roo.data.Record} record created.
6954 newRow : function(d) {
6956 this.recordType.prototype.fields.each(function(c) {
6958 case 'int' : da[c.name] = 0; break;
6959 case 'date' : da[c.name] = new Date(); break;
6960 case 'float' : da[c.name] = 0.0; break;
6961 case 'boolean' : da[c.name] = false; break;
6962 default : da[c.name] = ""; break;
6966 return new this.recordType(Roo.apply(da, d));
6971 * Ext JS Library 1.1.1
6972 * Copyright(c) 2006-2007, Ext JS, LLC.
6974 * Originally Released Under LGPL - original licence link has changed is not relivant.
6977 * <script type="text/javascript">
6981 * @class Roo.data.DataProxy
6982 * @extends Roo.data.Observable
6983 * This class is an abstract base class for implementations which provide retrieval of
6984 * unformatted data objects.<br>
6986 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6987 * (of the appropriate type which knows how to parse the data object) to provide a block of
6988 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6990 * Custom implementations must implement the load method as described in
6991 * {@link Roo.data.HttpProxy#load}.
6993 Roo.data.DataProxy = function(){
6997 * Fires before a network request is made to retrieve a data object.
6998 * @param {Object} This DataProxy object.
6999 * @param {Object} params The params parameter to the load function.
7004 * Fires before the load method's callback is called.
7005 * @param {Object} This DataProxy object.
7006 * @param {Object} o The data object.
7007 * @param {Object} arg The callback argument object passed to the load function.
7011 * @event loadexception
7012 * Fires if an Exception occurs during data retrieval.
7013 * @param {Object} This DataProxy object.
7014 * @param {Object} o The data object.
7015 * @param {Object} arg The callback argument object passed to the load function.
7016 * @param {Object} e The Exception.
7018 loadexception : true
7020 Roo.data.DataProxy.superclass.constructor.call(this);
7023 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7026 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7030 * Ext JS Library 1.1.1
7031 * Copyright(c) 2006-2007, Ext JS, LLC.
7033 * Originally Released Under LGPL - original licence link has changed is not relivant.
7036 * <script type="text/javascript">
7039 * @class Roo.data.MemoryProxy
7040 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7041 * to the Reader when its load method is called.
7043 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7045 Roo.data.MemoryProxy = function(data){
7049 Roo.data.MemoryProxy.superclass.constructor.call(this);
7053 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7055 * Load data from the requested source (in this case an in-memory
7056 * data object passed to the constructor), read the data object into
7057 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7058 * process that block using the passed callback.
7059 * @param {Object} params This parameter is not used by the MemoryProxy class.
7060 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7061 * object into a block of Roo.data.Records.
7062 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7063 * The function must be passed <ul>
7064 * <li>The Record block object</li>
7065 * <li>The "arg" argument from the load function</li>
7066 * <li>A boolean success indicator</li>
7068 * @param {Object} scope The scope in which to call the callback
7069 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7071 load : function(params, reader, callback, scope, arg){
7072 params = params || {};
7075 result = reader.readRecords(this.data);
7077 this.fireEvent("loadexception", this, arg, null, e);
7078 callback.call(scope, null, arg, false);
7081 callback.call(scope, result, arg, true);
7085 update : function(params, records){
7090 * Ext JS Library 1.1.1
7091 * Copyright(c) 2006-2007, Ext JS, LLC.
7093 * Originally Released Under LGPL - original licence link has changed is not relivant.
7096 * <script type="text/javascript">
7099 * @class Roo.data.HttpProxy
7100 * @extends Roo.data.DataProxy
7101 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7102 * configured to reference a certain URL.<br><br>
7104 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7105 * from which the running page was served.<br><br>
7107 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7109 * Be aware that to enable the browser to parse an XML document, the server must set
7110 * the Content-Type header in the HTTP response to "text/xml".
7112 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7113 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7114 * will be used to make the request.
7116 Roo.data.HttpProxy = function(conn){
7117 Roo.data.HttpProxy.superclass.constructor.call(this);
7118 // is conn a conn config or a real conn?
7120 this.useAjax = !conn || !conn.events;
7124 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7125 // thse are take from connection...
7128 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7131 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7132 * extra parameters to each request made by this object. (defaults to undefined)
7135 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7136 * to each request made by this object. (defaults to undefined)
7139 * @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)
7142 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7145 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7151 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7155 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7156 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7157 * a finer-grained basis than the DataProxy events.
7159 getConnection : function(){
7160 return this.useAjax ? Roo.Ajax : this.conn;
7164 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7165 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7166 * process that block using the passed callback.
7167 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7168 * for the request to the remote server.
7169 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7170 * object into a block of Roo.data.Records.
7171 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7172 * The function must be passed <ul>
7173 * <li>The Record block object</li>
7174 * <li>The "arg" argument from the load function</li>
7175 * <li>A boolean success indicator</li>
7177 * @param {Object} scope The scope in which to call the callback
7178 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7180 load : function(params, reader, callback, scope, arg){
7181 if(this.fireEvent("beforeload", this, params) !== false){
7183 params : params || {},
7185 callback : callback,
7190 callback : this.loadResponse,
7194 Roo.applyIf(o, this.conn);
7195 if(this.activeRequest){
7196 Roo.Ajax.abort(this.activeRequest);
7198 this.activeRequest = Roo.Ajax.request(o);
7200 this.conn.request(o);
7203 callback.call(scope||this, null, arg, false);
7208 loadResponse : function(o, success, response){
7209 delete this.activeRequest;
7211 this.fireEvent("loadexception", this, o, response);
7212 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7217 result = o.reader.read(response);
7219 this.fireEvent("loadexception", this, o, response, e);
7220 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7224 this.fireEvent("load", this, o, o.request.arg);
7225 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7229 update : function(dataSet){
7234 updateResponse : function(dataSet){
7239 * Ext JS Library 1.1.1
7240 * Copyright(c) 2006-2007, Ext JS, LLC.
7242 * Originally Released Under LGPL - original licence link has changed is not relivant.
7245 * <script type="text/javascript">
7249 * @class Roo.data.ScriptTagProxy
7250 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7251 * other than the originating domain of the running page.<br><br>
7253 * <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
7254 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7256 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7257 * source code that is used as the source inside a <script> tag.<br><br>
7259 * In order for the browser to process the returned data, the server must wrap the data object
7260 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7261 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7262 * depending on whether the callback name was passed:
7265 boolean scriptTag = false;
7266 String cb = request.getParameter("callback");
7269 response.setContentType("text/javascript");
7271 response.setContentType("application/x-json");
7273 Writer out = response.getWriter();
7275 out.write(cb + "(");
7277 out.print(dataBlock.toJsonString());
7284 * @param {Object} config A configuration object.
7286 Roo.data.ScriptTagProxy = function(config){
7287 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7288 Roo.apply(this, config);
7289 this.head = document.getElementsByTagName("head")[0];
7292 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7294 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7296 * @cfg {String} url The URL from which to request the data object.
7299 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7303 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7304 * the server the name of the callback function set up by the load call to process the returned data object.
7305 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7306 * javascript output which calls this named function passing the data object as its only parameter.
7308 callbackParam : "callback",
7310 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7311 * name to the request.
7316 * Load data from the configured URL, read the data object into
7317 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7318 * process that block using the passed callback.
7319 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7320 * for the request to the remote server.
7321 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7322 * object into a block of Roo.data.Records.
7323 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7324 * The function must be passed <ul>
7325 * <li>The Record block object</li>
7326 * <li>The "arg" argument from the load function</li>
7327 * <li>A boolean success indicator</li>
7329 * @param {Object} scope The scope in which to call the callback
7330 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7332 load : function(params, reader, callback, scope, arg){
7333 if(this.fireEvent("beforeload", this, params) !== false){
7335 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7338 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7340 url += "&_dc=" + (new Date().getTime());
7342 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7345 cb : "stcCallback"+transId,
7346 scriptId : "stcScript"+transId,
7350 callback : callback,
7356 window[trans.cb] = function(o){
7357 conn.handleResponse(o, trans);
7360 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7362 if(this.autoAbort !== false){
7366 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7368 var script = document.createElement("script");
7369 script.setAttribute("src", url);
7370 script.setAttribute("type", "text/javascript");
7371 script.setAttribute("id", trans.scriptId);
7372 this.head.appendChild(script);
7376 callback.call(scope||this, null, arg, false);
7381 isLoading : function(){
7382 return this.trans ? true : false;
7386 * Abort the current server request.
7389 if(this.isLoading()){
7390 this.destroyTrans(this.trans);
7395 destroyTrans : function(trans, isLoaded){
7396 this.head.removeChild(document.getElementById(trans.scriptId));
7397 clearTimeout(trans.timeoutId);
7399 window[trans.cb] = undefined;
7401 delete window[trans.cb];
7404 // if hasn't been loaded, wait for load to remove it to prevent script error
7405 window[trans.cb] = function(){
7406 window[trans.cb] = undefined;
7408 delete window[trans.cb];
7415 handleResponse : function(o, trans){
7417 this.destroyTrans(trans, true);
7420 result = trans.reader.readRecords(o);
7422 this.fireEvent("loadexception", this, o, trans.arg, e);
7423 trans.callback.call(trans.scope||window, null, trans.arg, false);
7426 this.fireEvent("load", this, o, trans.arg);
7427 trans.callback.call(trans.scope||window, result, trans.arg, true);
7431 handleFailure : function(trans){
7433 this.destroyTrans(trans, false);
7434 this.fireEvent("loadexception", this, null, trans.arg);
7435 trans.callback.call(trans.scope||window, null, trans.arg, false);
7439 * Ext JS Library 1.1.1
7440 * Copyright(c) 2006-2007, Ext JS, LLC.
7442 * Originally Released Under LGPL - original licence link has changed is not relivant.
7445 * <script type="text/javascript">
7449 * @class Roo.data.JsonReader
7450 * @extends Roo.data.DataReader
7451 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7452 * based on mappings in a provided Roo.data.Record constructor.
7454 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7455 * in the reply previously.
7460 var RecordDef = Roo.data.Record.create([
7461 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7462 {name: 'occupation'} // This field will use "occupation" as the mapping.
7464 var myReader = new Roo.data.JsonReader({
7465 totalProperty: "results", // The property which contains the total dataset size (optional)
7466 root: "rows", // The property which contains an Array of row objects
7467 id: "id" // The property within each row object that provides an ID for the record (optional)
7471 * This would consume a JSON file like this:
7473 { 'results': 2, 'rows': [
7474 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7475 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7478 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7479 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7480 * paged from the remote server.
7481 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7482 * @cfg {String} root name of the property which contains the Array of row objects.
7483 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7485 * Create a new JsonReader
7486 * @param {Object} meta Metadata configuration options
7487 * @param {Object} recordType Either an Array of field definition objects,
7488 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7490 Roo.data.JsonReader = function(meta, recordType){
7493 // set some defaults:
7495 totalProperty: 'total',
7496 successProperty : 'success',
7501 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7503 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7506 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7507 * Used by Store query builder to append _requestMeta to params.
7510 metaFromRemote : false,
7512 * This method is only used by a DataProxy which has retrieved data from a remote server.
7513 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7514 * @return {Object} data A data block which is used by an Roo.data.Store object as
7515 * a cache of Roo.data.Records.
7517 read : function(response){
7518 var json = response.responseText;
7520 var o = /* eval:var:o */ eval("("+json+")");
7522 throw {message: "JsonReader.read: Json object not found"};
7528 this.metaFromRemote = true;
7529 this.meta = o.metaData;
7530 this.recordType = Roo.data.Record.create(o.metaData.fields);
7531 this.onMetaChange(this.meta, this.recordType, o);
7533 return this.readRecords(o);
7536 // private function a store will implement
7537 onMetaChange : function(meta, recordType, o){
7544 simpleAccess: function(obj, subsc) {
7551 getJsonAccessor: function(){
7553 return function(expr) {
7555 return(re.test(expr))
7556 ? new Function("obj", "return obj." + expr)
7566 * Create a data block containing Roo.data.Records from an XML document.
7567 * @param {Object} o An object which contains an Array of row objects in the property specified
7568 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7569 * which contains the total size of the dataset.
7570 * @return {Object} data A data block which is used by an Roo.data.Store object as
7571 * a cache of Roo.data.Records.
7573 readRecords : function(o){
7575 * After any data loads, the raw JSON data is available for further custom processing.
7579 var s = this.meta, Record = this.recordType,
7580 f = Record.prototype.fields, fi = f.items, fl = f.length;
7582 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7584 if(s.totalProperty) {
7585 this.getTotal = this.getJsonAccessor(s.totalProperty);
7587 if(s.successProperty) {
7588 this.getSuccess = this.getJsonAccessor(s.successProperty);
7590 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7592 var g = this.getJsonAccessor(s.id);
7593 this.getId = function(rec) {
7595 return (r === undefined || r === "") ? null : r;
7598 this.getId = function(){return null;};
7601 for(var jj = 0; jj < fl; jj++){
7603 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7604 this.ef[jj] = this.getJsonAccessor(map);
7608 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7609 if(s.totalProperty){
7610 var vt = parseInt(this.getTotal(o), 10);
7615 if(s.successProperty){
7616 var vs = this.getSuccess(o);
7617 if(vs === false || vs === 'false'){
7622 for(var i = 0; i < c; i++){
7625 var id = this.getId(n);
7626 for(var j = 0; j < fl; j++){
7628 var v = this.ef[j](n);
7630 Roo.log('missing convert for ' + f.name);
7634 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7636 var record = new Record(values, id);
7638 records[i] = record;
7644 totalRecords : totalRecords
7649 * Ext JS Library 1.1.1
7650 * Copyright(c) 2006-2007, Ext JS, LLC.
7652 * Originally Released Under LGPL - original licence link has changed is not relivant.
7655 * <script type="text/javascript">
7659 * @class Roo.data.ArrayReader
7660 * @extends Roo.data.DataReader
7661 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7662 * Each element of that Array represents a row of data fields. The
7663 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7664 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7668 var RecordDef = Roo.data.Record.create([
7669 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7670 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7672 var myReader = new Roo.data.ArrayReader({
7673 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7677 * This would consume an Array like this:
7679 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7681 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7683 * Create a new JsonReader
7684 * @param {Object} meta Metadata configuration options.
7685 * @param {Object} recordType Either an Array of field definition objects
7686 * as specified to {@link Roo.data.Record#create},
7687 * or an {@link Roo.data.Record} object
7688 * created using {@link Roo.data.Record#create}.
7690 Roo.data.ArrayReader = function(meta, recordType){
7691 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7694 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7696 * Create a data block containing Roo.data.Records from an XML document.
7697 * @param {Object} o An Array of row objects which represents the dataset.
7698 * @return {Object} data A data block which is used by an Roo.data.Store object as
7699 * a cache of Roo.data.Records.
7701 readRecords : function(o){
7702 var sid = this.meta ? this.meta.id : null;
7703 var recordType = this.recordType, fields = recordType.prototype.fields;
7706 for(var i = 0; i < root.length; i++){
7709 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7710 for(var j = 0, jlen = fields.length; j < jlen; j++){
7711 var f = fields.items[j];
7712 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7713 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7717 var record = new recordType(values, id);
7719 records[records.length] = record;
7723 totalRecords : records.length
7732 * @class Roo.bootstrap.ComboBox
7733 * @extends Roo.bootstrap.TriggerField
7734 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7735 * @cfg {Boolean} append (true|false) default false
7737 * Create a new ComboBox.
7738 * @param {Object} config Configuration options
7740 Roo.bootstrap.ComboBox = function(config){
7741 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7745 * Fires when the dropdown list is expanded
7746 * @param {Roo.bootstrap.ComboBox} combo This combo box
7751 * Fires when the dropdown list is collapsed
7752 * @param {Roo.bootstrap.ComboBox} combo This combo box
7756 * @event beforeselect
7757 * Fires before a list item is selected. Return false to cancel the selection.
7758 * @param {Roo.bootstrap.ComboBox} combo This combo box
7759 * @param {Roo.data.Record} record The data record returned from the underlying store
7760 * @param {Number} index The index of the selected item in the dropdown list
7762 'beforeselect' : true,
7765 * Fires when a list item is selected
7766 * @param {Roo.bootstrap.ComboBox} combo This combo box
7767 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7768 * @param {Number} index The index of the selected item in the dropdown list
7772 * @event beforequery
7773 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7774 * The event object passed has these properties:
7775 * @param {Roo.bootstrap.ComboBox} combo This combo box
7776 * @param {String} query The query
7777 * @param {Boolean} forceAll true to force "all" query
7778 * @param {Boolean} cancel true to cancel the query
7779 * @param {Object} e The query event object
7781 'beforequery': true,
7784 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7785 * @param {Roo.bootstrap.ComboBox} combo This combo box
7790 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7791 * @param {Roo.bootstrap.ComboBox} combo This combo box
7792 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7797 * Fires when the remove value from the combobox array
7798 * @param {Roo.bootstrap.ComboBox} combo This combo box
7805 this.selectedIndex = -1;
7806 if(this.mode == 'local'){
7807 if(config.queryDelay === undefined){
7808 this.queryDelay = 10;
7810 if(config.minChars === undefined){
7816 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7819 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7820 * rendering into an Roo.Editor, defaults to false)
7823 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7824 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7827 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7830 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7831 * the dropdown list (defaults to undefined, with no header element)
7835 * @cfg {String/Roo.Template} tpl The template to use to render the output
7839 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7841 listWidth: undefined,
7843 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7844 * mode = 'remote' or 'text' if mode = 'local')
7846 displayField: undefined,
7848 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7849 * mode = 'remote' or 'value' if mode = 'local').
7850 * Note: use of a valueField requires the user make a selection
7851 * in order for a value to be mapped.
7853 valueField: undefined,
7857 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7858 * field's data value (defaults to the underlying DOM element's name)
7860 hiddenName: undefined,
7862 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7866 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7868 selectedClass: 'active',
7871 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7875 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7876 * anchor positions (defaults to 'tl-bl')
7878 listAlign: 'tl-bl?',
7880 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7884 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7885 * query specified by the allQuery config option (defaults to 'query')
7887 triggerAction: 'query',
7889 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7890 * (defaults to 4, does not apply if editable = false)
7894 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7895 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7899 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7900 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7904 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7905 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7909 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7910 * when editable = true (defaults to false)
7912 selectOnFocus:false,
7914 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7916 queryParam: 'query',
7918 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7919 * when mode = 'remote' (defaults to 'Loading...')
7921 loadingText: 'Loading...',
7923 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7927 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7931 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7932 * traditional select (defaults to true)
7936 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7940 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7944 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7945 * listWidth has a higher value)
7949 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7950 * allow the user to set arbitrary text into the field (defaults to false)
7952 forceSelection:false,
7954 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7955 * if typeAhead = true (defaults to 250)
7957 typeAheadDelay : 250,
7959 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7960 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7962 valueNotFoundText : undefined,
7964 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7969 * @cfg {Boolean} disableClear Disable showing of clear button.
7971 disableClear : false,
7973 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
7975 alwaysQuery : false,
7978 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
7992 // element that contains real text value.. (when hidden is used..)
7995 initEvents: function(){
7998 throw "can not find store for combo";
8000 this.store = Roo.factory(this.store, Roo.data);
8004 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8007 if(this.hiddenName){
8009 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8011 this.hiddenField.dom.value =
8012 this.hiddenValue !== undefined ? this.hiddenValue :
8013 this.value !== undefined ? this.value : '';
8015 // prevent input submission
8016 this.el.dom.removeAttribute('name');
8017 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8022 // this.el.dom.setAttribute('autocomplete', 'off');
8025 var cls = 'x-combo-list';
8026 this.list = this.el.select('ul.dropdown-menu',true).first();
8028 //this.list = new Roo.Layer({
8029 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8032 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8033 this.list.setWidth(lw);
8035 this.list.on('mouseover', this.onViewOver, this);
8036 this.list.on('mousemove', this.onViewMove, this);
8038 this.list.on('scroll', this.onViewScroll, this);
8041 this.list.swallowEvent('mousewheel');
8042 this.assetHeight = 0;
8045 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8046 this.assetHeight += this.header.getHeight();
8049 this.innerList = this.list.createChild({cls:cls+'-inner'});
8050 this.innerList.on('mouseover', this.onViewOver, this);
8051 this.innerList.on('mousemove', this.onViewMove, this);
8052 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8054 if(this.allowBlank && !this.pageSize && !this.disableClear){
8055 this.footer = this.list.createChild({cls:cls+'-ft'});
8056 this.pageTb = new Roo.Toolbar(this.footer);
8060 this.footer = this.list.createChild({cls:cls+'-ft'});
8061 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8062 {pageSize: this.pageSize});
8066 if (this.pageTb && this.allowBlank && !this.disableClear) {
8068 this.pageTb.add(new Roo.Toolbar.Fill(), {
8069 cls: 'x-btn-icon x-btn-clear',
8075 _this.onSelect(false, -1);
8080 this.assetHeight += this.footer.getHeight();
8085 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8088 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8089 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8091 //this.view.wrapEl.setDisplayed(false);
8092 this.view.on('click', this.onViewClick, this);
8096 this.store.on('beforeload', this.onBeforeLoad, this);
8097 this.store.on('load', this.onLoad, this);
8098 this.store.on('loadexception', this.onLoadException, this);
8101 this.resizer = new Roo.Resizable(this.list, {
8102 pinned:true, handles:'se'
8104 this.resizer.on('resize', function(r, w, h){
8105 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8107 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8108 this.restrictHeight();
8110 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8114 this.editable = true;
8115 this.setEditable(false);
8120 if (typeof(this.events.add.listeners) != 'undefined') {
8122 this.addicon = this.wrap.createChild(
8123 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8125 this.addicon.on('click', function(e) {
8126 this.fireEvent('add', this);
8129 if (typeof(this.events.edit.listeners) != 'undefined') {
8131 this.editicon = this.wrap.createChild(
8132 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8134 this.editicon.setStyle('margin-left', '40px');
8136 this.editicon.on('click', function(e) {
8138 // we fire even if inothing is selected..
8139 this.fireEvent('edit', this, this.lastData );
8145 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8147 this.inKeyMode = true;
8151 "down" : function(e){
8152 if(!this.isExpanded()){
8153 this.onTriggerClick();
8155 this.inKeyMode = true;
8160 "enter" : function(e){
8165 "esc" : function(e){
8169 "tab" : function(e){
8172 if(this.fireEvent("specialkey", this, e)){
8173 this.onViewClick(false);
8181 doRelay : function(foo, bar, hname){
8182 if(hname == 'down' || this.scope.isExpanded()){
8183 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8192 this.queryDelay = Math.max(this.queryDelay || 10,
8193 this.mode == 'local' ? 10 : 250);
8196 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8199 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8201 if(this.editable !== false){
8202 this.inputEl().on("keyup", this.onKeyUp, this);
8204 if(this.forceSelection){
8205 this.on('blur', this.doForce, this);
8209 this.choices = this.el.select('ul.select2-choices', true).first();
8210 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8214 onDestroy : function(){
8216 this.view.setStore(null);
8217 this.view.el.removeAllListeners();
8218 this.view.el.remove();
8219 this.view.purgeListeners();
8222 this.list.dom.innerHTML = '';
8225 this.store.un('beforeload', this.onBeforeLoad, this);
8226 this.store.un('load', this.onLoad, this);
8227 this.store.un('loadexception', this.onLoadException, this);
8229 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8233 fireKey : function(e){
8234 if(e.isNavKeyPress() && !this.list.isVisible()){
8235 this.fireEvent("specialkey", this, e);
8240 onResize: function(w, h){
8241 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8243 // if(typeof w != 'number'){
8244 // // we do not handle it!?!?
8247 // var tw = this.trigger.getWidth();
8248 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8249 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8251 // this.inputEl().setWidth( this.adjustWidth('input', x));
8253 // //this.trigger.setStyle('left', x+'px');
8255 // if(this.list && this.listWidth === undefined){
8256 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8257 // this.list.setWidth(lw);
8258 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8266 * Allow or prevent the user from directly editing the field text. If false is passed,
8267 * the user will only be able to select from the items defined in the dropdown list. This method
8268 * is the runtime equivalent of setting the 'editable' config option at config time.
8269 * @param {Boolean} value True to allow the user to directly edit the field text
8271 setEditable : function(value){
8272 if(value == this.editable){
8275 this.editable = value;
8277 this.inputEl().dom.setAttribute('readOnly', true);
8278 this.inputEl().on('mousedown', this.onTriggerClick, this);
8279 this.inputEl().addClass('x-combo-noedit');
8281 this.inputEl().dom.setAttribute('readOnly', false);
8282 this.inputEl().un('mousedown', this.onTriggerClick, this);
8283 this.inputEl().removeClass('x-combo-noedit');
8289 onBeforeLoad : function(combo,opts){
8294 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8296 this.restrictHeight();
8297 this.selectedIndex = -1;
8301 onLoad : function(){
8303 this.hasQuery = false;
8309 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8310 this.loading.hide();
8313 if(this.store.getCount() > 0){
8315 this.restrictHeight();
8316 if(this.lastQuery == this.allQuery){
8318 this.inputEl().dom.select();
8320 if(!this.selectByValue(this.value, true)){
8321 this.select(0, true);
8325 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8326 this.taTask.delay(this.typeAheadDelay);
8330 this.onEmptyResults();
8336 onLoadException : function()
8338 this.hasQuery = false;
8340 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8341 this.loading.hide();
8345 Roo.log(this.store.reader.jsonData);
8346 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8348 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8354 onTypeAhead : function(){
8355 if(this.store.getCount() > 0){
8356 var r = this.store.getAt(0);
8357 var newValue = r.data[this.displayField];
8358 var len = newValue.length;
8359 var selStart = this.getRawValue().length;
8361 if(selStart != len){
8362 this.setRawValue(newValue);
8363 this.selectText(selStart, newValue.length);
8369 onSelect : function(record, index){
8371 if(this.fireEvent('beforeselect', this, record, index) !== false){
8373 this.setFromData(index > -1 ? record.data : false);
8376 this.fireEvent('select', this, record, index);
8381 * Returns the currently selected field value or empty string if no value is set.
8382 * @return {String} value The selected value
8384 getValue : function(){
8387 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8390 if(this.valueField){
8391 return typeof this.value != 'undefined' ? this.value : '';
8393 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8398 * Clears any text/value currently set in the field
8400 clearValue : function(){
8401 if(this.hiddenField){
8402 this.hiddenField.dom.value = '';
8405 this.setRawValue('');
8406 this.lastSelectionText = '';
8411 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8412 * will be displayed in the field. If the value does not match the data value of an existing item,
8413 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8414 * Otherwise the field will be blank (although the value will still be set).
8415 * @param {String} value The value to match
8417 setValue : function(v){
8424 if(this.valueField){
8425 var r = this.findRecord(this.valueField, v);
8427 text = r.data[this.displayField];
8428 }else if(this.valueNotFoundText !== undefined){
8429 text = this.valueNotFoundText;
8432 this.lastSelectionText = text;
8433 if(this.hiddenField){
8434 this.hiddenField.dom.value = v;
8436 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8440 * @property {Object} the last set data for the element
8445 * Sets the value of the field based on a object which is related to the record format for the store.
8446 * @param {Object} value the value to set as. or false on reset?
8448 setFromData : function(o){
8455 var dv = ''; // display value
8456 var vv = ''; // value value..
8458 if (this.displayField) {
8459 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8461 // this is an error condition!!!
8462 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8465 if(this.valueField){
8466 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8469 if(this.hiddenField){
8470 this.hiddenField.dom.value = vv;
8472 this.lastSelectionText = dv;
8473 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8477 // no hidden field.. - we store the value in 'value', but still display
8478 // display field!!!!
8479 this.lastSelectionText = dv;
8480 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8487 // overridden so that last data is reset..
8488 this.setValue(this.originalValue);
8489 this.clearInvalid();
8490 this.lastData = false;
8492 this.view.clearSelections();
8496 findRecord : function(prop, value){
8498 if(this.store.getCount() > 0){
8499 this.store.each(function(r){
8500 if(r.data[prop] == value){
8512 // returns hidden if it's set..
8513 if (!this.rendered) {return ''};
8514 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8518 onViewMove : function(e, t){
8519 this.inKeyMode = false;
8523 onViewOver : function(e, t){
8524 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8527 var item = this.view.findItemFromChild(t);
8529 var index = this.view.indexOf(item);
8530 this.select(index, false);
8535 onViewClick : function(doFocus)
8537 var index = this.view.getSelectedIndexes()[0];
8538 var r = this.store.getAt(index);
8540 this.onSelect(r, index);
8542 if(doFocus !== false && !this.blockFocus){
8543 this.inputEl().focus();
8548 restrictHeight : function(){
8549 //this.innerList.dom.style.height = '';
8550 //var inner = this.innerList.dom;
8551 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8552 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8553 //this.list.beginUpdate();
8554 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8555 this.list.alignTo(this.inputEl(), this.listAlign);
8556 //this.list.endUpdate();
8560 onEmptyResults : function(){
8565 * Returns true if the dropdown list is expanded, else false.
8567 isExpanded : function(){
8568 return this.list.isVisible();
8572 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8573 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8574 * @param {String} value The data value of the item to select
8575 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8576 * selected item if it is not currently in view (defaults to true)
8577 * @return {Boolean} True if the value matched an item in the list, else false
8579 selectByValue : function(v, scrollIntoView){
8580 if(v !== undefined && v !== null){
8581 var r = this.findRecord(this.valueField || this.displayField, v);
8583 this.select(this.store.indexOf(r), scrollIntoView);
8591 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8592 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8593 * @param {Number} index The zero-based index of the list item to select
8594 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8595 * selected item if it is not currently in view (defaults to true)
8597 select : function(index, scrollIntoView){
8598 this.selectedIndex = index;
8599 this.view.select(index);
8600 if(scrollIntoView !== false){
8601 var el = this.view.getNode(index);
8603 //this.innerList.scrollChildIntoView(el, false);
8610 selectNext : function(){
8611 var ct = this.store.getCount();
8613 if(this.selectedIndex == -1){
8615 }else if(this.selectedIndex < ct-1){
8616 this.select(this.selectedIndex+1);
8622 selectPrev : function(){
8623 var ct = this.store.getCount();
8625 if(this.selectedIndex == -1){
8627 }else if(this.selectedIndex != 0){
8628 this.select(this.selectedIndex-1);
8634 onKeyUp : function(e){
8635 if(this.editable !== false && !e.isSpecialKey()){
8636 this.lastKey = e.getKey();
8637 this.dqTask.delay(this.queryDelay);
8642 validateBlur : function(){
8643 return !this.list || !this.list.isVisible();
8647 initQuery : function(){
8648 this.doQuery(this.getRawValue());
8652 doForce : function(){
8653 if(this.el.dom.value.length > 0){
8655 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8661 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8662 * query allowing the query action to be canceled if needed.
8663 * @param {String} query The SQL query to execute
8664 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8665 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8666 * saved in the current store (defaults to false)
8668 doQuery : function(q, forceAll){
8670 if(q === undefined || q === null){
8679 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8684 forceAll = qe.forceAll;
8685 if(forceAll === true || (q.length >= this.minChars)){
8687 this.hasQuery = true;
8689 if(this.lastQuery != q || this.alwaysQuery){
8691 if(this.mode == 'local'){
8692 this.selectedIndex = -1;
8694 this.store.clearFilter();
8696 this.store.filter(this.displayField, q);
8700 this.store.baseParams[this.queryParam] = q;
8702 var options = {params : this.getParams(q)};
8706 options.params.start = this.page * this.pageSize;
8709 this.store.load(options);
8713 this.selectedIndex = -1;
8718 this.loadNext = false;
8722 getParams : function(q){
8724 //p[this.queryParam] = q;
8728 p.limit = this.pageSize;
8734 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8736 collapse : function(){
8737 if(!this.isExpanded()){
8742 Roo.get(document).un('mousedown', this.collapseIf, this);
8743 Roo.get(document).un('mousewheel', this.collapseIf, this);
8744 if (!this.editable) {
8745 Roo.get(document).un('keydown', this.listKeyPress, this);
8747 this.fireEvent('collapse', this);
8751 collapseIf : function(e){
8752 var in_combo = e.within(this.el);
8753 var in_list = e.within(this.list);
8755 if (in_combo || in_list) {
8756 //e.stopPropagation();
8765 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8767 expand : function(){
8769 if(this.isExpanded() || !this.hasFocus){
8773 this.list.alignTo(this.inputEl(), this.listAlign);
8775 Roo.get(document).on('mousedown', this.collapseIf, this);
8776 Roo.get(document).on('mousewheel', this.collapseIf, this);
8777 if (!this.editable) {
8778 Roo.get(document).on('keydown', this.listKeyPress, this);
8781 this.fireEvent('expand', this);
8785 // Implements the default empty TriggerField.onTriggerClick function
8786 onTriggerClick : function()
8788 Roo.log('trigger click');
8795 this.loadNext = false;
8797 if(this.isExpanded()){
8799 if (!this.blockFocus) {
8800 this.inputEl().focus();
8804 this.hasFocus = true;
8805 if(this.triggerAction == 'all') {
8806 this.doQuery(this.allQuery, true);
8808 this.doQuery(this.getRawValue());
8810 if (!this.blockFocus) {
8811 this.inputEl().focus();
8815 listKeyPress : function(e)
8817 //Roo.log('listkeypress');
8818 // scroll to first matching element based on key pres..
8819 if (e.isSpecialKey()) {
8822 var k = String.fromCharCode(e.getKey()).toUpperCase();
8825 var csel = this.view.getSelectedNodes();
8826 var cselitem = false;
8828 var ix = this.view.indexOf(csel[0]);
8829 cselitem = this.store.getAt(ix);
8830 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8836 this.store.each(function(v) {
8838 // start at existing selection.
8839 if (cselitem.id == v.id) {
8845 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8846 match = this.store.indexOf(v);
8852 if (match === false) {
8853 return true; // no more action?
8856 this.view.select(match);
8857 var sn = Roo.get(this.view.getSelectedNodes()[0])
8858 //sn.scrollIntoView(sn.dom.parentNode, false);
8861 onViewScroll : function(e, t){
8863 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8867 this.hasQuery = true;
8869 this.loading = this.list.select('.loading', true).first();
8871 if(this.loading === null){
8872 this.list.createChild({
8874 cls: 'loading select2-more-results select2-active',
8875 html: 'Loading more results...'
8878 this.loading = this.list.select('.loading', true).first();
8880 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8882 this.loading.hide();
8885 this.loading.show();
8890 this.loadNext = true;
8892 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8897 addItem : function(o)
8899 var dv = ''; // display value
8901 if (this.displayField) {
8902 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8904 // this is an error condition!!!
8905 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8912 var choice = this.choices.createChild({
8914 cls: 'select2-search-choice',
8923 cls: 'select2-search-choice-close',
8928 }, this.searchField);
8930 var close = choice.select('a.select2-search-choice-close', true).first()
8932 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8939 this.inputEl().dom.value = '';
8943 onRemoveItem : function(e, _self, o)
8945 Roo.log('remove item');
8946 var index = this.item.indexOf(o.data) * 1;
8949 Roo.log('not this item?!');
8953 this.item.splice(index, 1);
8958 this.fireEvent('remove', this);
8962 syncValue : function()
8964 if(!this.item.length){
8971 Roo.each(this.item, function(i){
8972 if(_this.valueField){
8973 value.push(i[_this.valueField]);
8980 this.value = value.join(',');
8982 if(this.hiddenField){
8983 this.hiddenField.dom.value = this.value;
8987 clearItem : function()
8995 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9005 * @cfg {Boolean} grow
9009 * @cfg {Number} growMin
9013 * @cfg {Number} growMax
9023 * Ext JS Library 1.1.1
9024 * Copyright(c) 2006-2007, Ext JS, LLC.
9026 * Originally Released Under LGPL - original licence link has changed is not relivant.
9029 * <script type="text/javascript">
9034 * @extends Roo.util.Observable
9035 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9036 * This class also supports single and multi selection modes. <br>
9037 * Create a data model bound view:
9039 var store = new Roo.data.Store(...);
9041 var view = new Roo.View({
9043 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9046 selectedClass: "ydataview-selected",
9050 // listen for node click?
9051 view.on("click", function(vw, index, node, e){
9052 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9056 dataModel.load("foobar.xml");
9058 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9060 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9061 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9063 * Note: old style constructor is still suported (container, template, config)
9067 * @param {Object} config The config object
9070 Roo.View = function(config, depreciated_tpl, depreciated_config){
9072 if (typeof(depreciated_tpl) == 'undefined') {
9073 // new way.. - universal constructor.
9074 Roo.apply(this, config);
9075 this.el = Roo.get(this.el);
9078 this.el = Roo.get(config);
9079 this.tpl = depreciated_tpl;
9080 Roo.apply(this, depreciated_config);
9082 this.wrapEl = this.el.wrap().wrap();
9083 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9086 if(typeof(this.tpl) == "string"){
9087 this.tpl = new Roo.Template(this.tpl);
9089 // support xtype ctors..
9090 this.tpl = new Roo.factory(this.tpl, Roo);
9102 * @event beforeclick
9103 * Fires before a click is processed. Returns false to cancel the default action.
9104 * @param {Roo.View} this
9105 * @param {Number} index The index of the target node
9106 * @param {HTMLElement} node The target node
9107 * @param {Roo.EventObject} e The raw event object
9109 "beforeclick" : true,
9112 * Fires when a template node is clicked.
9113 * @param {Roo.View} this
9114 * @param {Number} index The index of the target node
9115 * @param {HTMLElement} node The target node
9116 * @param {Roo.EventObject} e The raw event object
9121 * Fires when a template node is double clicked.
9122 * @param {Roo.View} this
9123 * @param {Number} index The index of the target node
9124 * @param {HTMLElement} node The target node
9125 * @param {Roo.EventObject} e The raw event object
9129 * @event contextmenu
9130 * Fires when a template node is right clicked.
9131 * @param {Roo.View} this
9132 * @param {Number} index The index of the target node
9133 * @param {HTMLElement} node The target node
9134 * @param {Roo.EventObject} e The raw event object
9136 "contextmenu" : true,
9138 * @event selectionchange
9139 * Fires when the selected nodes change.
9140 * @param {Roo.View} this
9141 * @param {Array} selections Array of the selected nodes
9143 "selectionchange" : true,
9146 * @event beforeselect
9147 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9148 * @param {Roo.View} this
9149 * @param {HTMLElement} node The node to be selected
9150 * @param {Array} selections Array of currently selected nodes
9152 "beforeselect" : true,
9154 * @event preparedata
9155 * Fires on every row to render, to allow you to change the data.
9156 * @param {Roo.View} this
9157 * @param {Object} data to be rendered (change this)
9159 "preparedata" : true
9167 "click": this.onClick,
9168 "dblclick": this.onDblClick,
9169 "contextmenu": this.onContextMenu,
9173 this.selections = [];
9175 this.cmp = new Roo.CompositeElementLite([]);
9177 this.store = Roo.factory(this.store, Roo.data);
9178 this.setStore(this.store, true);
9181 if ( this.footer && this.footer.xtype) {
9183 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9185 this.footer.dataSource = this.store
9186 this.footer.container = fctr;
9187 this.footer = Roo.factory(this.footer, Roo);
9188 fctr.insertFirst(this.el);
9190 // this is a bit insane - as the paging toolbar seems to detach the el..
9191 // dom.parentNode.parentNode.parentNode
9192 // they get detached?
9196 Roo.View.superclass.constructor.call(this);
9201 Roo.extend(Roo.View, Roo.util.Observable, {
9204 * @cfg {Roo.data.Store} store Data store to load data from.
9209 * @cfg {String|Roo.Element} el The container element.
9214 * @cfg {String|Roo.Template} tpl The template used by this View
9218 * @cfg {String} dataName the named area of the template to use as the data area
9219 * Works with domtemplates roo-name="name"
9223 * @cfg {String} selectedClass The css class to add to selected nodes
9225 selectedClass : "x-view-selected",
9227 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9232 * @cfg {String} text to display on mask (default Loading)
9236 * @cfg {Boolean} multiSelect Allow multiple selection
9238 multiSelect : false,
9240 * @cfg {Boolean} singleSelect Allow single selection
9242 singleSelect: false,
9245 * @cfg {Boolean} toggleSelect - selecting
9247 toggleSelect : false,
9250 * Returns the element this view is bound to.
9251 * @return {Roo.Element}
9260 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9262 refresh : function(){
9266 // if we are using something like 'domtemplate', then
9267 // the what gets used is:
9268 // t.applySubtemplate(NAME, data, wrapping data..)
9269 // the outer template then get' applied with
9270 // the store 'extra data'
9271 // and the body get's added to the
9272 // roo-name="data" node?
9273 // <span class='roo-tpl-{name}'></span> ?????
9277 this.clearSelections();
9280 var records = this.store.getRange();
9281 if(records.length < 1) {
9283 // is this valid?? = should it render a template??
9285 this.el.update(this.emptyText);
9289 if (this.dataName) {
9290 this.el.update(t.apply(this.store.meta)); //????
9291 el = this.el.child('.roo-tpl-' + this.dataName);
9294 for(var i = 0, len = records.length; i < len; i++){
9295 var data = this.prepareData(records[i].data, i, records[i]);
9296 this.fireEvent("preparedata", this, data, i, records[i]);
9297 html[html.length] = Roo.util.Format.trim(
9299 t.applySubtemplate(this.dataName, data, this.store.meta) :
9306 el.update(html.join(""));
9307 this.nodes = el.dom.childNodes;
9308 this.updateIndexes(0);
9313 * Function to override to reformat the data that is sent to
9314 * the template for each node.
9315 * DEPRICATED - use the preparedata event handler.
9316 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9317 * a JSON object for an UpdateManager bound view).
9319 prepareData : function(data, index, record)
9321 this.fireEvent("preparedata", this, data, index, record);
9325 onUpdate : function(ds, record){
9326 Roo.log('on update');
9327 this.clearSelections();
9328 var index = this.store.indexOf(record);
9329 var n = this.nodes[index];
9330 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9331 n.parentNode.removeChild(n);
9332 this.updateIndexes(index, index);
9338 onAdd : function(ds, records, index)
9340 Roo.log(['on Add', ds, records, index] );
9341 this.clearSelections();
9342 if(this.nodes.length == 0){
9346 var n = this.nodes[index];
9347 for(var i = 0, len = records.length; i < len; i++){
9348 var d = this.prepareData(records[i].data, i, records[i]);
9350 this.tpl.insertBefore(n, d);
9353 this.tpl.append(this.el, d);
9356 this.updateIndexes(index);
9359 onRemove : function(ds, record, index){
9360 Roo.log('onRemove');
9361 this.clearSelections();
9362 var el = this.dataName ?
9363 this.el.child('.roo-tpl-' + this.dataName) :
9366 el.dom.removeChild(this.nodes[index]);
9367 this.updateIndexes(index);
9371 * Refresh an individual node.
9372 * @param {Number} index
9374 refreshNode : function(index){
9375 this.onUpdate(this.store, this.store.getAt(index));
9378 updateIndexes : function(startIndex, endIndex){
9379 var ns = this.nodes;
9380 startIndex = startIndex || 0;
9381 endIndex = endIndex || ns.length - 1;
9382 for(var i = startIndex; i <= endIndex; i++){
9383 ns[i].nodeIndex = i;
9388 * Changes the data store this view uses and refresh the view.
9389 * @param {Store} store
9391 setStore : function(store, initial){
9392 if(!initial && this.store){
9393 this.store.un("datachanged", this.refresh);
9394 this.store.un("add", this.onAdd);
9395 this.store.un("remove", this.onRemove);
9396 this.store.un("update", this.onUpdate);
9397 this.store.un("clear", this.refresh);
9398 this.store.un("beforeload", this.onBeforeLoad);
9399 this.store.un("load", this.onLoad);
9400 this.store.un("loadexception", this.onLoad);
9404 store.on("datachanged", this.refresh, this);
9405 store.on("add", this.onAdd, this);
9406 store.on("remove", this.onRemove, this);
9407 store.on("update", this.onUpdate, this);
9408 store.on("clear", this.refresh, this);
9409 store.on("beforeload", this.onBeforeLoad, this);
9410 store.on("load", this.onLoad, this);
9411 store.on("loadexception", this.onLoad, this);
9419 * onbeforeLoad - masks the loading area.
9422 onBeforeLoad : function(store,opts)
9424 Roo.log('onBeforeLoad');
9428 this.el.mask(this.mask ? this.mask : "Loading" );
9430 onLoad : function ()
9437 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9438 * @param {HTMLElement} node
9439 * @return {HTMLElement} The template node
9441 findItemFromChild : function(node){
9442 var el = this.dataName ?
9443 this.el.child('.roo-tpl-' + this.dataName,true) :
9446 if(!node || node.parentNode == el){
9449 var p = node.parentNode;
9450 while(p && p != el){
9451 if(p.parentNode == el){
9460 onClick : function(e){
9461 var item = this.findItemFromChild(e.getTarget());
9463 var index = this.indexOf(item);
9464 if(this.onItemClick(item, index, e) !== false){
9465 this.fireEvent("click", this, index, item, e);
9468 this.clearSelections();
9473 onContextMenu : function(e){
9474 var item = this.findItemFromChild(e.getTarget());
9476 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9481 onDblClick : function(e){
9482 var item = this.findItemFromChild(e.getTarget());
9484 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9488 onItemClick : function(item, index, e)
9490 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9493 if (this.toggleSelect) {
9494 var m = this.isSelected(item) ? 'unselect' : 'select';
9497 _t[m](item, true, false);
9500 if(this.multiSelect || this.singleSelect){
9501 if(this.multiSelect && e.shiftKey && this.lastSelection){
9502 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9504 this.select(item, this.multiSelect && e.ctrlKey);
9505 this.lastSelection = item;
9513 * Get the number of selected nodes.
9516 getSelectionCount : function(){
9517 return this.selections.length;
9521 * Get the currently selected nodes.
9522 * @return {Array} An array of HTMLElements
9524 getSelectedNodes : function(){
9525 return this.selections;
9529 * Get the indexes of the selected nodes.
9532 getSelectedIndexes : function(){
9533 var indexes = [], s = this.selections;
9534 for(var i = 0, len = s.length; i < len; i++){
9535 indexes.push(s[i].nodeIndex);
9541 * Clear all selections
9542 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9544 clearSelections : function(suppressEvent){
9545 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9546 this.cmp.elements = this.selections;
9547 this.cmp.removeClass(this.selectedClass);
9548 this.selections = [];
9550 this.fireEvent("selectionchange", this, this.selections);
9556 * Returns true if the passed node is selected
9557 * @param {HTMLElement/Number} node The node or node index
9560 isSelected : function(node){
9561 var s = this.selections;
9565 node = this.getNode(node);
9566 return s.indexOf(node) !== -1;
9571 * @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
9572 * @param {Boolean} keepExisting (optional) true to keep existing selections
9573 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9575 select : function(nodeInfo, keepExisting, suppressEvent){
9576 if(nodeInfo instanceof Array){
9578 this.clearSelections(true);
9580 for(var i = 0, len = nodeInfo.length; i < len; i++){
9581 this.select(nodeInfo[i], true, true);
9585 var node = this.getNode(nodeInfo);
9586 if(!node || this.isSelected(node)){
9587 return; // already selected.
9590 this.clearSelections(true);
9592 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9593 Roo.fly(node).addClass(this.selectedClass);
9594 this.selections.push(node);
9596 this.fireEvent("selectionchange", this, this.selections);
9604 * @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
9605 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9606 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9608 unselect : function(nodeInfo, keepExisting, suppressEvent)
9610 if(nodeInfo instanceof Array){
9611 Roo.each(this.selections, function(s) {
9612 this.unselect(s, nodeInfo);
9616 var node = this.getNode(nodeInfo);
9617 if(!node || !this.isSelected(node)){
9618 Roo.log("not selected");
9619 return; // not selected.
9623 Roo.each(this.selections, function(s) {
9625 Roo.fly(node).removeClass(this.selectedClass);
9632 this.selections= ns;
9633 this.fireEvent("selectionchange", this, this.selections);
9637 * Gets a template node.
9638 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9639 * @return {HTMLElement} The node or null if it wasn't found
9641 getNode : function(nodeInfo){
9642 if(typeof nodeInfo == "string"){
9643 return document.getElementById(nodeInfo);
9644 }else if(typeof nodeInfo == "number"){
9645 return this.nodes[nodeInfo];
9651 * Gets a range template nodes.
9652 * @param {Number} startIndex
9653 * @param {Number} endIndex
9654 * @return {Array} An array of nodes
9656 getNodes : function(start, end){
9657 var ns = this.nodes;
9659 end = typeof end == "undefined" ? ns.length - 1 : end;
9662 for(var i = start; i <= end; i++){
9666 for(var i = start; i >= end; i--){
9674 * Finds the index of the passed node
9675 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9676 * @return {Number} The index of the node or -1
9678 indexOf : function(node){
9679 node = this.getNode(node);
9680 if(typeof node.nodeIndex == "number"){
9681 return node.nodeIndex;
9683 var ns = this.nodes;
9684 for(var i = 0, len = ns.length; i < len; i++){
9695 * based on jquery fullcalendar
9699 Roo.bootstrap = Roo.bootstrap || {};
9701 * @class Roo.bootstrap.Calendar
9702 * @extends Roo.bootstrap.Component
9703 * Bootstrap Calendar class
9704 * @cfg {Boolean} loadMask (true|false) default false
9707 * Create a new Container
9708 * @param {Object} config The config object
9713 Roo.bootstrap.Calendar = function(config){
9714 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9718 * Fires when a date is selected
9719 * @param {DatePicker} this
9720 * @param {Date} date The selected date
9724 * @event monthchange
9725 * Fires when the displayed month changes
9726 * @param {DatePicker} this
9727 * @param {Date} date The selected month
9729 'monthchange': true,
9732 * Fires when mouse over an event
9733 * @param {Calendar} this
9734 * @param {event} Event
9739 * Fires when the mouse leaves an
9740 * @param {Calendar} this
9746 * Fires when the mouse click an
9747 * @param {Calendar} this
9756 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9759 * @cfg {Number} startDay
9760 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9766 getAutoCreate : function(){
9769 var fc_button = function(name, corner, style, content ) {
9770 return Roo.apply({},{
9772 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9774 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9777 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9785 style : 'width:100%',
9792 cls : 'fc-header-left',
9794 fc_button('prev', 'left', 'arrow', '‹' ),
9795 fc_button('next', 'right', 'arrow', '›' ),
9796 { tag: 'span', cls: 'fc-header-space' },
9797 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9805 cls : 'fc-header-center',
9809 cls: 'fc-header-title',
9812 html : 'month / year'
9820 cls : 'fc-header-right',
9822 /* fc_button('month', 'left', '', 'month' ),
9823 fc_button('week', '', '', 'week' ),
9824 fc_button('day', 'right', '', 'day' )
9836 var cal_heads = function() {
9838 // fixme - handle this.
9840 for (var i =0; i < Date.dayNames.length; i++) {
9841 var d = Date.dayNames[i];
9844 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9845 html : d.substring(0,3)
9849 ret[0].cls += ' fc-first';
9850 ret[6].cls += ' fc-last';
9853 var cal_cell = function(n) {
9856 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9861 cls: 'fc-day-number',
9865 cls: 'fc-day-content',
9869 style: 'position: relative;' // height: 17px;
9881 var cal_rows = function() {
9884 for (var r = 0; r < 6; r++) {
9891 for (var i =0; i < Date.dayNames.length; i++) {
9892 var d = Date.dayNames[i];
9893 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9896 row.cn[0].cls+=' fc-first';
9897 row.cn[0].cn[0].style = 'min-height:90px';
9898 row.cn[6].cls+=' fc-last';
9902 ret[0].cls += ' fc-first';
9903 ret[4].cls += ' fc-prev-last';
9904 ret[5].cls += ' fc-last';
9911 cls: 'fc-border-separate',
9912 style : 'width:100%',
9920 cls : 'fc-first fc-last',
9939 style : "position: relative;",
9942 cls : 'fc-view fc-view-month fc-grid',
9943 style : 'position: relative',
9944 unselectable : 'on',
9947 cls : 'fc-event-container',
9948 style : 'position:absolute;z-index:8;top:0;left:0;'
9966 initEvents : function()
9969 throw "can not find store for calendar";
9975 style: "text-align:center",
9979 style: "background-color:white;width:50%;margin:250 auto",
9983 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9994 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9996 var size = this.el.select('.fc-content', true).first().getSize();
9997 this.maskEl.setSize(size.width, size.height);
9998 this.maskEl.enableDisplayMode("block");
10000 this.maskEl.hide();
10003 this.store = Roo.factory(this.store, Roo.data);
10004 this.store.on('load', this.onLoad, this);
10005 this.store.on('beforeload', this.onBeforeLoad, this);
10009 this.cells = this.el.select('.fc-day',true);
10010 //Roo.log(this.cells);
10011 this.textNodes = this.el.query('.fc-day-number');
10012 this.cells.addClassOnOver('fc-state-hover');
10014 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10015 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10016 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10017 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10019 this.on('monthchange', this.onMonthChange, this);
10021 this.update(new Date().clearTime());
10024 resize : function() {
10025 var sz = this.el.getSize();
10027 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10028 this.el.select('.fc-day-content div',true).setHeight(34);
10033 showPrevMonth : function(e){
10034 this.update(this.activeDate.add("mo", -1));
10036 showToday : function(e){
10037 this.update(new Date().clearTime());
10040 showNextMonth : function(e){
10041 this.update(this.activeDate.add("mo", 1));
10045 showPrevYear : function(){
10046 this.update(this.activeDate.add("y", -1));
10050 showNextYear : function(){
10051 this.update(this.activeDate.add("y", 1));
10056 update : function(date)
10058 var vd = this.activeDate;
10059 this.activeDate = date;
10060 // if(vd && this.el){
10061 // var t = date.getTime();
10062 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10063 // Roo.log('using add remove');
10065 // this.fireEvent('monthchange', this, date);
10067 // this.cells.removeClass("fc-state-highlight");
10068 // this.cells.each(function(c){
10069 // if(c.dateValue == t){
10070 // c.addClass("fc-state-highlight");
10071 // setTimeout(function(){
10072 // try{c.dom.firstChild.focus();}catch(e){}
10082 var days = date.getDaysInMonth();
10084 var firstOfMonth = date.getFirstDateOfMonth();
10085 var startingPos = firstOfMonth.getDay()-this.startDay;
10087 if(startingPos < this.startDay){
10091 var pm = date.add(Date.MONTH, -1);
10092 var prevStart = pm.getDaysInMonth()-startingPos;
10094 this.cells = this.el.select('.fc-day',true);
10095 this.textNodes = this.el.query('.fc-day-number');
10096 this.cells.addClassOnOver('fc-state-hover');
10098 var cells = this.cells.elements;
10099 var textEls = this.textNodes;
10101 Roo.each(cells, function(cell){
10102 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10105 days += startingPos;
10107 // convert everything to numbers so it's fast
10108 var day = 86400000;
10109 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10112 //Roo.log(prevStart);
10114 var today = new Date().clearTime().getTime();
10115 var sel = date.clearTime().getTime();
10116 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10117 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10118 var ddMatch = this.disabledDatesRE;
10119 var ddText = this.disabledDatesText;
10120 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10121 var ddaysText = this.disabledDaysText;
10122 var format = this.format;
10124 var setCellClass = function(cal, cell){
10126 //Roo.log('set Cell Class');
10128 var t = d.getTime();
10132 cell.dateValue = t;
10134 cell.className += " fc-today";
10135 cell.className += " fc-state-highlight";
10136 cell.title = cal.todayText;
10139 // disable highlight in other month..
10140 //cell.className += " fc-state-highlight";
10145 cell.className = " fc-state-disabled";
10146 cell.title = cal.minText;
10150 cell.className = " fc-state-disabled";
10151 cell.title = cal.maxText;
10155 if(ddays.indexOf(d.getDay()) != -1){
10156 cell.title = ddaysText;
10157 cell.className = " fc-state-disabled";
10160 if(ddMatch && format){
10161 var fvalue = d.dateFormat(format);
10162 if(ddMatch.test(fvalue)){
10163 cell.title = ddText.replace("%0", fvalue);
10164 cell.className = " fc-state-disabled";
10168 if (!cell.initialClassName) {
10169 cell.initialClassName = cell.dom.className;
10172 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10177 for(; i < startingPos; i++) {
10178 textEls[i].innerHTML = (++prevStart);
10179 d.setDate(d.getDate()+1);
10181 cells[i].className = "fc-past fc-other-month";
10182 setCellClass(this, cells[i]);
10187 for(; i < days; i++){
10188 intDay = i - startingPos + 1;
10189 textEls[i].innerHTML = (intDay);
10190 d.setDate(d.getDate()+1);
10192 cells[i].className = ''; // "x-date-active";
10193 setCellClass(this, cells[i]);
10197 for(; i < 42; i++) {
10198 textEls[i].innerHTML = (++extraDays);
10199 d.setDate(d.getDate()+1);
10201 cells[i].className = "fc-future fc-other-month";
10202 setCellClass(this, cells[i]);
10205 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10207 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10209 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10210 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10212 if(totalRows != 6){
10213 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10214 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10217 this.fireEvent('monthchange', this, date);
10221 if(!this.internalRender){
10222 var main = this.el.dom.firstChild;
10223 var w = main.offsetWidth;
10224 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10225 Roo.fly(main).setWidth(w);
10226 this.internalRender = true;
10227 // opera does not respect the auto grow header center column
10228 // then, after it gets a width opera refuses to recalculate
10229 // without a second pass
10230 if(Roo.isOpera && !this.secondPass){
10231 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10232 this.secondPass = true;
10233 this.update.defer(10, this, [date]);
10240 findCell : function(dt) {
10241 dt = dt.clearTime().getTime();
10243 this.cells.each(function(c){
10244 //Roo.log("check " +c.dateValue + '?=' + dt);
10245 if(c.dateValue == dt){
10255 findCells : function(ev) {
10256 var s = ev.start.clone().clearTime().getTime();
10258 var e= ev.end.clone().clearTime().getTime();
10261 this.cells.each(function(c){
10262 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10264 if(c.dateValue > e){
10267 if(c.dateValue < s){
10276 findBestRow: function(cells)
10280 for (var i =0 ; i < cells.length;i++) {
10281 ret = Math.max(cells[i].rows || 0,ret);
10288 addItem : function(ev)
10290 // look for vertical location slot in
10291 var cells = this.findCells(ev);
10293 ev.row = this.findBestRow(cells);
10295 // work out the location.
10299 for(var i =0; i < cells.length; i++) {
10307 if (crow.start.getY() == cells[i].getY()) {
10309 crow.end = cells[i];
10325 for (var i = 0; i < cells.length;i++) {
10326 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10330 this.calevents.push(ev);
10333 clearEvents: function() {
10335 if(!this.calevents){
10339 Roo.each(this.cells.elements, function(c){
10343 Roo.each(this.calevents, function(e) {
10344 Roo.each(e.els, function(el) {
10345 el.un('mouseenter' ,this.onEventEnter, this);
10346 el.un('mouseleave' ,this.onEventLeave, this);
10353 renderEvents: function()
10355 // first make sure there is enough space..
10357 this.cells.each(function(c) {
10359 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10362 for (var e = 0; e < this.calevents.length; e++) {
10363 var ev = this.calevents[e];
10364 var cells = ev.cells;
10365 var rows = ev.rows;
10367 for(var i =0; i < rows.length; i++) {
10370 // how many rows should it span..
10373 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10374 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10376 unselectable : "on",
10379 cls: 'fc-event-inner',
10383 // cls: 'fc-event-time',
10384 // html : cells.length > 1 ? '' : ev.time
10388 cls: 'fc-event-title',
10389 html : String.format('{0}', ev.title)
10396 cls: 'ui-resizable-handle ui-resizable-e',
10397 html : '  '
10403 cfg.cls += ' fc-event-start';
10405 if ((i+1) == rows.length) {
10406 cfg.cls += ' fc-event-end';
10409 var ctr = this.el.select('.fc-event-container',true).first();
10410 var cg = ctr.createChild(cfg);
10412 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10413 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10414 cg.on('click', this.onEventClick, this, ev);
10418 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10419 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10421 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10422 cg.setWidth(ebox.right - sbox.x -2);
10430 onEventEnter: function (e, el,event,d) {
10431 this.fireEvent('evententer', this, el, event);
10434 onEventLeave: function (e, el,event,d) {
10435 this.fireEvent('eventleave', this, el, event);
10438 onEventClick: function (e, el,event,d) {
10439 this.fireEvent('eventclick', this, el, event);
10442 onMonthChange: function () {
10446 onLoad: function ()
10448 this.calevents = [];
10451 if(this.store.getCount() > 0){
10452 this.store.data.each(function(d){
10455 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10456 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10457 time : d.data.start_time,
10458 title : d.data.title,
10459 description : d.data.description,
10460 venue : d.data.venue
10465 this.renderEvents();
10468 this.maskEl.hide();
10472 onBeforeLoad: function()
10474 this.clearEvents();
10477 this.maskEl.show();
10491 * @class Roo.bootstrap.Popover
10492 * @extends Roo.bootstrap.Component
10493 * Bootstrap Popover class
10494 * @cfg {String} html contents of the popover (or false to use children..)
10495 * @cfg {String} title of popover (or false to hide)
10496 * @cfg {String} placement how it is placed
10497 * @cfg {String} trigger click || hover (or false to trigger manually)
10498 * @cfg {String} over what (parent or false to trigger manually.)
10501 * Create a new Popover
10502 * @param {Object} config The config object
10505 Roo.bootstrap.Popover = function(config){
10506 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10509 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10511 title: 'Fill in a title',
10514 placement : 'right',
10515 trigger : 'hover', // hover
10519 can_build_overlaid : false,
10521 getChildContainer : function()
10523 return this.el.select('.popover-content',true).first();
10526 getAutoCreate : function(){
10527 Roo.log('make popover?');
10529 cls : 'popover roo-dynamic',
10530 style: 'display:block',
10536 cls : 'popover-inner',
10540 cls: 'popover-title',
10544 cls : 'popover-content',
10555 setTitle: function(str)
10557 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10559 setContent: function(str)
10561 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10563 // as it get's added to the bottom of the page.
10564 onRender : function(ct, position)
10566 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10568 var cfg = Roo.apply({}, this.getAutoCreate());
10572 cfg.cls += ' ' + this.cls;
10575 cfg.style = this.style;
10577 Roo.log("adding to ")
10578 this.el = Roo.get(document.body).createChild(cfg, position);
10584 initEvents : function()
10586 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10587 this.el.enableDisplayMode('block');
10589 if (this.over === false) {
10592 if (this.triggers === false) {
10595 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10596 var triggers = this.trigger ? this.trigger.split(' ') : [];
10597 Roo.each(triggers, function(trigger) {
10599 if (trigger == 'click') {
10600 on_el.on('click', this.toggle, this);
10601 } else if (trigger != 'manual') {
10602 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10603 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10605 on_el.on(eventIn ,this.enter, this);
10606 on_el.on(eventOut, this.leave, this);
10617 toggle : function () {
10618 this.hoverState == 'in' ? this.leave() : this.enter();
10621 enter : function () {
10624 clearTimeout(this.timeout);
10626 this.hoverState = 'in'
10628 if (!this.delay || !this.delay.show) {
10633 this.timeout = setTimeout(function () {
10634 if (_t.hoverState == 'in') {
10637 }, this.delay.show)
10639 leave : function() {
10640 clearTimeout(this.timeout);
10642 this.hoverState = 'out'
10644 if (!this.delay || !this.delay.hide) {
10649 this.timeout = setTimeout(function () {
10650 if (_t.hoverState == 'out') {
10653 }, this.delay.hide)
10656 show : function (on_el)
10659 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10662 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10663 if (this.html !== false) {
10664 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10666 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10667 if (!this.title.length) {
10668 this.el.select('.popover-title',true).hide();
10671 var placement = typeof this.placement == 'function' ?
10672 this.placement.call(this, this.el, on_el) :
10675 var autoToken = /\s?auto?\s?/i;
10676 var autoPlace = autoToken.test(placement);
10678 placement = placement.replace(autoToken, '') || 'top';
10682 //this.el.setXY([0,0]);
10684 this.el.dom.style.display='block';
10685 this.el.addClass(placement);
10687 //this.el.appendTo(on_el);
10689 var p = this.getPosition();
10690 var box = this.el.getBox();
10695 var align = Roo.bootstrap.Popover.alignment[placement]
10696 this.el.alignTo(on_el, align[0],align[1]);
10697 //var arrow = this.el.select('.arrow',true).first();
10698 //arrow.set(align[2],
10700 this.el.addClass('in');
10701 this.hoverState = null;
10703 if (this.el.hasClass('fade')) {
10710 this.el.setXY([0,0]);
10711 this.el.removeClass('in');
10718 Roo.bootstrap.Popover.alignment = {
10719 'left' : ['r-l', [-10,0], 'right'],
10720 'right' : ['l-r', [10,0], 'left'],
10721 'bottom' : ['t-b', [0,10], 'top'],
10722 'top' : [ 'b-t', [0,-10], 'bottom']
10733 * @class Roo.bootstrap.Progress
10734 * @extends Roo.bootstrap.Component
10735 * Bootstrap Progress class
10736 * @cfg {Boolean} striped striped of the progress bar
10737 * @cfg {Boolean} active animated of the progress bar
10741 * Create a new Progress
10742 * @param {Object} config The config object
10745 Roo.bootstrap.Progress = function(config){
10746 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10749 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10754 getAutoCreate : function(){
10762 cfg.cls += ' progress-striped';
10766 cfg.cls += ' active';
10785 * @class Roo.bootstrap.ProgressBar
10786 * @extends Roo.bootstrap.Component
10787 * Bootstrap ProgressBar class
10788 * @cfg {Number} aria_valuenow aria-value now
10789 * @cfg {Number} aria_valuemin aria-value min
10790 * @cfg {Number} aria_valuemax aria-value max
10791 * @cfg {String} label label for the progress bar
10792 * @cfg {String} panel (success | info | warning | danger )
10793 * @cfg {String} role role of the progress bar
10794 * @cfg {String} sr_only text
10798 * Create a new ProgressBar
10799 * @param {Object} config The config object
10802 Roo.bootstrap.ProgressBar = function(config){
10803 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10806 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10810 aria_valuemax : 100,
10816 getAutoCreate : function()
10821 cls: 'progress-bar',
10822 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10834 cfg.role = this.role;
10837 if(this.aria_valuenow){
10838 cfg['aria-valuenow'] = this.aria_valuenow;
10841 if(this.aria_valuemin){
10842 cfg['aria-valuemin'] = this.aria_valuemin;
10845 if(this.aria_valuemax){
10846 cfg['aria-valuemax'] = this.aria_valuemax;
10849 if(this.label && !this.sr_only){
10850 cfg.html = this.label;
10854 cfg.cls += ' progress-bar-' + this.panel;
10860 update : function(aria_valuenow)
10862 this.aria_valuenow = aria_valuenow;
10864 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10879 * @class Roo.bootstrap.TabPanel
10880 * @extends Roo.bootstrap.Component
10881 * Bootstrap TabPanel class
10882 * @cfg {Boolean} active panel active
10883 * @cfg {String} html panel content
10884 * @cfg {String} tabId tab relate id
10888 * Create a new TabPanel
10889 * @param {Object} config The config object
10892 Roo.bootstrap.TabPanel = function(config){
10893 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10896 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10902 getAutoCreate : function(){
10906 html: this.html || ''
10910 cfg.cls += ' active';
10914 cfg.tabId = this.tabId;
10932 * @class Roo.bootstrap.DateField
10933 * @extends Roo.bootstrap.Input
10934 * Bootstrap DateField class
10935 * @cfg {Number} weekStart default 0
10936 * @cfg {Number} weekStart default 0
10937 * @cfg {Number} viewMode default empty, (months|years)
10938 * @cfg {Number} minViewMode default empty, (months|years)
10939 * @cfg {Number} startDate default -Infinity
10940 * @cfg {Number} endDate default Infinity
10941 * @cfg {Boolean} todayHighlight default false
10942 * @cfg {Boolean} todayBtn default false
10943 * @cfg {Boolean} calendarWeeks default false
10944 * @cfg {Object} daysOfWeekDisabled default empty
10946 * @cfg {Boolean} keyboardNavigation default true
10947 * @cfg {String} language default en
10950 * Create a new DateField
10951 * @param {Object} config The config object
10954 Roo.bootstrap.DateField = function(config){
10955 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10959 * Fires when this field show.
10960 * @param {Roo.bootstrap.DateField} this
10961 * @param {Mixed} date The date value
10966 * Fires when this field hide.
10967 * @param {Roo.bootstrap.DateField} this
10968 * @param {Mixed} date The date value
10973 * Fires when select a date.
10974 * @param {Roo.bootstrap.DateField} this
10975 * @param {Mixed} date The date value
10981 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
10984 * @cfg {String} format
10985 * The default date format string which can be overriden for localization support. The format must be
10986 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10990 * @cfg {String} altFormats
10991 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10992 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10994 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11002 todayHighlight : false,
11008 keyboardNavigation: true,
11010 calendarWeeks: false,
11012 startDate: -Infinity,
11016 daysOfWeekDisabled: [],
11020 UTCDate: function()
11022 return new Date(Date.UTC.apply(Date, arguments));
11025 UTCToday: function()
11027 var today = new Date();
11028 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11031 getDate: function() {
11032 var d = this.getUTCDate();
11033 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11036 getUTCDate: function() {
11040 setDate: function(d) {
11041 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11044 setUTCDate: function(d) {
11046 this.setValue(this.formatDate(this.date));
11049 onRender: function(ct, position)
11052 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11054 this.language = this.language || 'en';
11055 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11056 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11058 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11059 this.format = this.format || 'm/d/y';
11060 this.isInline = false;
11061 this.isInput = true;
11062 this.component = this.el.select('.add-on', true).first() || false;
11063 this.component = (this.component && this.component.length === 0) ? false : this.component;
11064 this.hasInput = this.component && this.inputEL().length;
11066 if (typeof(this.minViewMode === 'string')) {
11067 switch (this.minViewMode) {
11069 this.minViewMode = 1;
11072 this.minViewMode = 2;
11075 this.minViewMode = 0;
11080 if (typeof(this.viewMode === 'string')) {
11081 switch (this.viewMode) {
11094 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11096 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11098 this.picker().on('mousedown', this.onMousedown, this);
11099 this.picker().on('click', this.onClick, this);
11101 this.picker().addClass('datepicker-dropdown');
11103 this.startViewMode = this.viewMode;
11106 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11107 if(!this.calendarWeeks){
11112 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11113 v.attr('colspan', function(i, val){
11114 return parseInt(val) + 1;
11119 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11121 this.setStartDate(this.startDate);
11122 this.setEndDate(this.endDate);
11124 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11131 if(this.isInline) {
11136 picker : function()
11138 return this.el.select('.datepicker', true).first();
11141 fillDow: function()
11143 var dowCnt = this.weekStart;
11152 if(this.calendarWeeks){
11160 while (dowCnt < this.weekStart + 7) {
11164 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11168 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11171 fillMonths: function()
11174 var months = this.picker().select('>.datepicker-months td', true).first();
11176 months.dom.innerHTML = '';
11182 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11185 months.createChild(month);
11190 update: function(){
11192 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11194 if (this.date < this.startDate) {
11195 this.viewDate = new Date(this.startDate);
11196 } else if (this.date > this.endDate) {
11197 this.viewDate = new Date(this.endDate);
11199 this.viewDate = new Date(this.date);
11206 var d = new Date(this.viewDate),
11207 year = d.getUTCFullYear(),
11208 month = d.getUTCMonth(),
11209 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11210 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11211 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11212 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11213 currentDate = this.date && this.date.valueOf(),
11214 today = this.UTCToday();
11216 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11218 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11220 // this.picker.select('>tfoot th.today').
11221 // .text(dates[this.language].today)
11222 // .toggle(this.todayBtn !== false);
11224 this.updateNavArrows();
11227 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11229 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11231 prevMonth.setUTCDate(day);
11233 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11235 var nextMonth = new Date(prevMonth);
11237 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11239 nextMonth = nextMonth.valueOf();
11241 var fillMonths = false;
11243 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11245 while(prevMonth.valueOf() < nextMonth) {
11248 if (prevMonth.getUTCDay() === this.weekStart) {
11250 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11258 if(this.calendarWeeks){
11259 // ISO 8601: First week contains first thursday.
11260 // ISO also states week starts on Monday, but we can be more abstract here.
11262 // Start of current week: based on weekstart/current date
11263 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11264 // Thursday of this week
11265 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11266 // First Thursday of year, year from thursday
11267 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11268 // Calendar week: ms between thursdays, div ms per day, div 7 days
11269 calWeek = (th - yth) / 864e5 / 7 + 1;
11271 fillMonths.cn.push({
11279 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11281 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11284 if (this.todayHighlight &&
11285 prevMonth.getUTCFullYear() == today.getFullYear() &&
11286 prevMonth.getUTCMonth() == today.getMonth() &&
11287 prevMonth.getUTCDate() == today.getDate()) {
11288 clsName += ' today';
11291 if (currentDate && prevMonth.valueOf() === currentDate) {
11292 clsName += ' active';
11295 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11296 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11297 clsName += ' disabled';
11300 fillMonths.cn.push({
11302 cls: 'day ' + clsName,
11303 html: prevMonth.getDate()
11306 prevMonth.setDate(prevMonth.getDate()+1);
11309 var currentYear = this.date && this.date.getUTCFullYear();
11310 var currentMonth = this.date && this.date.getUTCMonth();
11312 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11314 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11315 v.removeClass('active');
11317 if(currentYear === year && k === currentMonth){
11318 v.addClass('active');
11321 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11322 v.addClass('disabled');
11328 year = parseInt(year/10, 10) * 10;
11330 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11332 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11335 for (var i = -1; i < 11; i++) {
11336 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11338 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11346 showMode: function(dir) {
11348 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11350 Roo.each(this.picker().select('>div',true).elements, function(v){
11351 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11354 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11359 if(this.isInline) return;
11361 this.picker().removeClass(['bottom', 'top']);
11363 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11365 * place to the top of element!
11369 this.picker().addClass('top');
11370 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11375 this.picker().addClass('bottom');
11377 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11380 parseDate : function(value){
11381 if(!value || value instanceof Date){
11384 var v = Date.parseDate(value, this.format);
11385 if (!v && this.useIso) {
11386 v = Date.parseDate(value, 'Y-m-d');
11388 if(!v && this.altFormats){
11389 if(!this.altFormatsArray){
11390 this.altFormatsArray = this.altFormats.split("|");
11392 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11393 v = Date.parseDate(value, this.altFormatsArray[i]);
11399 formatDate : function(date, fmt){
11400 return (!date || !(date instanceof Date)) ?
11401 date : date.dateFormat(fmt || this.format);
11404 onFocus : function()
11406 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11410 onBlur : function()
11412 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11418 this.picker().show();
11422 this.fireEvent('show', this, this.date);
11427 if(this.isInline) return;
11428 this.picker().hide();
11429 this.viewMode = this.startViewMode;
11432 this.fireEvent('hide', this, this.date);
11436 onMousedown: function(e){
11437 e.stopPropagation();
11438 e.preventDefault();
11441 keyup: function(e){
11442 Roo.bootstrap.DateField.superclass.keyup.call(this);
11447 setValue: function(v){
11448 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11450 this.fireEvent('select', this, this.date);
11454 fireKey: function(e){
11455 if (!this.picker().isVisible()){
11456 if (e.keyCode == 27) // allow escape to hide and re-show picker
11460 var dateChanged = false,
11462 newDate, newViewDate;
11466 e.preventDefault();
11470 if (!this.keyboardNavigation) break;
11471 dir = e.keyCode == 37 ? -1 : 1;
11474 newDate = this.moveYear(this.date, dir);
11475 newViewDate = this.moveYear(this.viewDate, dir);
11476 } else if (e.shiftKey){
11477 newDate = this.moveMonth(this.date, dir);
11478 newViewDate = this.moveMonth(this.viewDate, dir);
11480 newDate = new Date(this.date);
11481 newDate.setUTCDate(this.date.getUTCDate() + dir);
11482 newViewDate = new Date(this.viewDate);
11483 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11485 if (this.dateWithinRange(newDate)){
11486 this.date = newDate;
11487 this.viewDate = newViewDate;
11488 this.setValue(this.formatDate(this.date));
11490 e.preventDefault();
11491 dateChanged = true;
11496 if (!this.keyboardNavigation) break;
11497 dir = e.keyCode == 38 ? -1 : 1;
11499 newDate = this.moveYear(this.date, dir);
11500 newViewDate = this.moveYear(this.viewDate, dir);
11501 } else if (e.shiftKey){
11502 newDate = this.moveMonth(this.date, dir);
11503 newViewDate = this.moveMonth(this.viewDate, dir);
11505 newDate = new Date(this.date);
11506 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11507 newViewDate = new Date(this.viewDate);
11508 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11510 if (this.dateWithinRange(newDate)){
11511 this.date = newDate;
11512 this.viewDate = newViewDate;
11513 this.setValue(this.formatDate(this.date));
11515 e.preventDefault();
11516 dateChanged = true;
11520 this.setValue(this.formatDate(this.date));
11522 e.preventDefault();
11525 this.setValue(this.formatDate(this.date));
11532 onClick: function(e) {
11533 e.stopPropagation();
11534 e.preventDefault();
11536 var target = e.getTarget();
11538 if(target.nodeName.toLowerCase() === 'i'){
11539 target = Roo.get(target).dom.parentNode;
11542 var nodeName = target.nodeName;
11543 var className = target.className;
11544 var html = target.innerHTML;
11546 switch(nodeName.toLowerCase()) {
11548 switch(className) {
11554 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11555 switch(this.viewMode){
11557 this.viewDate = this.moveMonth(this.viewDate, dir);
11561 this.viewDate = this.moveYear(this.viewDate, dir);
11567 var date = new Date();
11568 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11570 this.setValue(this.formatDate(this.date));
11576 if (className.indexOf('disabled') === -1) {
11577 this.viewDate.setUTCDate(1);
11578 if (className.indexOf('month') !== -1) {
11579 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11581 var year = parseInt(html, 10) || 0;
11582 this.viewDate.setUTCFullYear(year);
11591 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11592 var day = parseInt(html, 10) || 1;
11593 var year = this.viewDate.getUTCFullYear(),
11594 month = this.viewDate.getUTCMonth();
11596 if (className.indexOf('old') !== -1) {
11603 } else if (className.indexOf('new') !== -1) {
11611 this.date = this.UTCDate(year, month, day,0,0,0,0);
11612 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11614 this.setValue(this.formatDate(this.date));
11621 setStartDate: function(startDate){
11622 this.startDate = startDate || -Infinity;
11623 if (this.startDate !== -Infinity) {
11624 this.startDate = this.parseDate(this.startDate);
11627 this.updateNavArrows();
11630 setEndDate: function(endDate){
11631 this.endDate = endDate || Infinity;
11632 if (this.endDate !== Infinity) {
11633 this.endDate = this.parseDate(this.endDate);
11636 this.updateNavArrows();
11639 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11640 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11641 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11642 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11644 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11645 return parseInt(d, 10);
11648 this.updateNavArrows();
11651 updateNavArrows: function() {
11652 var d = new Date(this.viewDate),
11653 year = d.getUTCFullYear(),
11654 month = d.getUTCMonth();
11656 Roo.each(this.picker().select('.prev', true).elements, function(v){
11658 switch (this.viewMode) {
11661 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11667 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11674 Roo.each(this.picker().select('.next', true).elements, function(v){
11676 switch (this.viewMode) {
11679 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11685 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11693 moveMonth: function(date, dir){
11694 if (!dir) return date;
11695 var new_date = new Date(date.valueOf()),
11696 day = new_date.getUTCDate(),
11697 month = new_date.getUTCMonth(),
11698 mag = Math.abs(dir),
11700 dir = dir > 0 ? 1 : -1;
11703 // If going back one month, make sure month is not current month
11704 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11706 return new_date.getUTCMonth() == month;
11708 // If going forward one month, make sure month is as expected
11709 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11711 return new_date.getUTCMonth() != new_month;
11713 new_month = month + dir;
11714 new_date.setUTCMonth(new_month);
11715 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11716 if (new_month < 0 || new_month > 11)
11717 new_month = (new_month + 12) % 12;
11719 // For magnitudes >1, move one month at a time...
11720 for (var i=0; i<mag; i++)
11721 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11722 new_date = this.moveMonth(new_date, dir);
11723 // ...then reset the day, keeping it in the new month
11724 new_month = new_date.getUTCMonth();
11725 new_date.setUTCDate(day);
11727 return new_month != new_date.getUTCMonth();
11730 // Common date-resetting loop -- if date is beyond end of month, make it
11733 new_date.setUTCDate(--day);
11734 new_date.setUTCMonth(new_month);
11739 moveYear: function(date, dir){
11740 return this.moveMonth(date, dir*12);
11743 dateWithinRange: function(date){
11744 return date >= this.startDate && date <= this.endDate;
11748 remove: function() {
11749 this.picker().remove();
11754 Roo.apply(Roo.bootstrap.DateField, {
11765 html: '<i class="icon-arrow-left"/>'
11775 html: '<i class="icon-arrow-right"/>'
11817 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11818 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11819 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11820 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11821 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11834 navFnc: 'FullYear',
11839 navFnc: 'FullYear',
11844 Roo.apply(Roo.bootstrap.DateField, {
11848 cls: 'datepicker dropdown-menu',
11852 cls: 'datepicker-days',
11856 cls: 'table-condensed',
11858 Roo.bootstrap.DateField.head,
11862 Roo.bootstrap.DateField.footer
11869 cls: 'datepicker-months',
11873 cls: 'table-condensed',
11875 Roo.bootstrap.DateField.head,
11876 Roo.bootstrap.DateField.content,
11877 Roo.bootstrap.DateField.footer
11884 cls: 'datepicker-years',
11888 cls: 'table-condensed',
11890 Roo.bootstrap.DateField.head,
11891 Roo.bootstrap.DateField.content,
11892 Roo.bootstrap.DateField.footer
11911 * @class Roo.bootstrap.TimeField
11912 * @extends Roo.bootstrap.Input
11913 * Bootstrap DateField class
11917 * Create a new TimeField
11918 * @param {Object} config The config object
11921 Roo.bootstrap.TimeField = function(config){
11922 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11926 * Fires when this field show.
11927 * @param {Roo.bootstrap.DateField} this
11928 * @param {Mixed} date The date value
11933 * Fires when this field hide.
11934 * @param {Roo.bootstrap.DateField} this
11935 * @param {Mixed} date The date value
11940 * Fires when select a date.
11941 * @param {Roo.bootstrap.DateField} this
11942 * @param {Mixed} date The date value
11948 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
11951 * @cfg {String} format
11952 * The default time format string which can be overriden for localization support. The format must be
11953 * valid according to {@link Date#parseDate} (defaults to 'H:i').
11957 onRender: function(ct, position)
11960 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11962 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11964 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11966 this.pop = this.picker().select('>.datepicker-time',true).first();
11967 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
11969 this.picker().on('mousedown', this.onMousedown, this);
11970 this.picker().on('click', this.onClick, this);
11972 this.picker().addClass('datepicker-dropdown');
11977 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11978 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11979 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11980 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11981 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11982 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11986 fireKey: function(e){
11987 if (!this.picker().isVisible()){
11988 if (e.keyCode == 27) // allow escape to hide and re-show picker
11993 e.preventDefault();
12001 this.onTogglePeriod();
12004 this.onIncrementMinutes();
12007 this.onDecrementMinutes();
12016 onClick: function(e) {
12017 e.stopPropagation();
12018 e.preventDefault();
12021 picker : function()
12023 return this.el.select('.datepicker', true).first();
12026 fillTime: function()
12028 var time = this.pop.select('tbody', true).first();
12030 time.dom.innerHTML = '';
12045 cls: 'hours-up glyphicon glyphicon-chevron-up'
12065 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12086 cls: 'timepicker-hour',
12101 cls: 'timepicker-minute',
12116 cls: 'btn btn-primary period',
12138 cls: 'hours-down glyphicon glyphicon-chevron-down'
12158 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12176 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12183 var hours = this.time.getHours();
12184 var minutes = this.time.getMinutes();
12197 hours = hours - 12;
12201 hours = '0' + hours;
12205 minutes = '0' + minutes;
12208 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12209 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12210 this.pop.select('button', true).first().dom.innerHTML = period;
12216 this.picker().removeClass(['bottom', 'top']);
12218 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12220 * place to the top of element!
12224 this.picker().addClass('top');
12225 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12230 this.picker().addClass('bottom');
12232 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12235 onFocus : function()
12237 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12241 onBlur : function()
12243 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12249 this.picker().show();
12254 this.fireEvent('show', this, this.date);
12259 this.picker().hide();
12262 this.fireEvent('hide', this, this.date);
12265 setTime : function()
12268 this.setValue(this.time.format(this.format));
12270 this.fireEvent('select', this, this.date);
12275 onMousedown: function(e){
12276 e.stopPropagation();
12277 e.preventDefault();
12280 onIncrementHours: function()
12282 Roo.log('onIncrementHours');
12283 this.time = this.time.add(Date.HOUR, 1);
12288 onDecrementHours: function()
12290 Roo.log('onDecrementHours');
12291 this.time = this.time.add(Date.HOUR, -1);
12295 onIncrementMinutes: function()
12297 Roo.log('onIncrementMinutes');
12298 this.time = this.time.add(Date.MINUTE, 1);
12302 onDecrementMinutes: function()
12304 Roo.log('onDecrementMinutes');
12305 this.time = this.time.add(Date.MINUTE, -1);
12309 onTogglePeriod: function()
12311 Roo.log('onTogglePeriod');
12312 this.time = this.time.add(Date.HOUR, 12);
12319 Roo.apply(Roo.bootstrap.TimeField, {
12349 cls: 'btn btn-info ok',
12361 Roo.apply(Roo.bootstrap.TimeField, {
12365 cls: 'datepicker dropdown-menu',
12369 cls: 'datepicker-time',
12373 cls: 'table-condensed',
12375 Roo.bootstrap.TimeField.content,
12376 Roo.bootstrap.TimeField.footer
12395 * @class Roo.bootstrap.CheckBox
12396 * @extends Roo.bootstrap.Input
12397 * Bootstrap CheckBox class
12399 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12400 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12401 * @cfg {String} boxLabel The text that appears beside the checkbox
12402 * @cfg {Boolean} checked initnal the element
12405 * Create a new CheckBox
12406 * @param {Object} config The config object
12409 Roo.bootstrap.CheckBox = function(config){
12410 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12415 * Fires when the element is checked or unchecked.
12416 * @param {Roo.bootstrap.CheckBox} this This input
12417 * @param {Boolean} checked The new checked value
12423 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12425 inputType: 'checkbox',
12431 getAutoCreate : function()
12433 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12439 cfg.cls = 'form-group' //input-group
12444 type : this.inputType,
12445 value : (!this.checked) ? this.valueOff : this.inputValue,
12447 placeholder : this.placeholder || ''
12451 if (this.disabled) {
12452 input.disabled=true;
12456 input.checked = this.checked;
12460 input.name = this.name;
12464 input.cls += ' input-' + this.size;
12468 ['xs','sm','md','lg'].map(function(size){
12469 if (settings[size]) {
12470 cfg.cls += ' col-' + size + '-' + settings[size];
12474 var inputblock = input;
12476 if (this.before || this.after) {
12479 cls : 'input-group',
12483 inputblock.cn.push({
12485 cls : 'input-group-addon',
12489 inputblock.cn.push(input);
12491 inputblock.cn.push({
12493 cls : 'input-group-addon',
12500 if (align ==='left' && this.fieldLabel.length) {
12501 Roo.log("left and has label");
12507 cls : 'control-label col-md-' + this.labelWidth,
12508 html : this.fieldLabel
12512 cls : "col-md-" + (12 - this.labelWidth),
12519 } else if ( this.fieldLabel.length) {
12524 tag: this.boxLabel ? 'span' : 'label',
12526 cls: 'control-label box-input-label',
12527 //cls : 'input-group-addon',
12528 html : this.fieldLabel
12538 Roo.log(" no label && no align");
12553 html: this.boxLabel
12562 * return the real input element.
12564 inputEl: function ()
12566 return this.el.select('input.form-box',true).first();
12569 initEvents : function()
12571 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12573 this.inputEl().on('click', this.onClick, this);
12577 onClick : function()
12579 this.setChecked(!this.checked);
12582 setChecked : function(state,suppressEvent)
12584 this.checked = state;
12586 this.inputEl().dom.checked = state;
12588 if(suppressEvent !== true){
12589 this.fireEvent('check', this, state);
12592 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12596 setValue : function(v,suppressEvent)
12598 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12612 * @class Roo.bootstrap.Radio
12613 * @extends Roo.bootstrap.CheckBox
12614 * Bootstrap Radio class
12617 * Create a new Radio
12618 * @param {Object} config The config object
12621 Roo.bootstrap.Radio = function(config){
12622 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12626 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12628 inputType: 'radio',
12632 getAutoCreate : function()
12634 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12640 cfg.cls = 'form-group' //input-group
12645 type : this.inputType,
12646 value : (!this.checked) ? this.valueOff : this.inputValue,
12648 placeholder : this.placeholder || ''
12652 if (this.disabled) {
12653 input.disabled=true;
12657 input.checked = this.checked;
12661 input.name = this.name;
12665 input.cls += ' input-' + this.size;
12669 ['xs','sm','md','lg'].map(function(size){
12670 if (settings[size]) {
12671 cfg.cls += ' col-' + size + '-' + settings[size];
12675 var inputblock = input;
12677 if (this.before || this.after) {
12680 cls : 'input-group',
12684 inputblock.cn.push({
12686 cls : 'input-group-addon',
12690 inputblock.cn.push(input);
12692 inputblock.cn.push({
12694 cls : 'input-group-addon',
12701 if (align ==='left' && this.fieldLabel.length) {
12702 Roo.log("left and has label");
12708 cls : 'control-label col-md-' + this.labelWidth,
12709 html : this.fieldLabel
12713 cls : "col-md-" + (12 - this.labelWidth),
12720 } else if ( this.fieldLabel.length) {
12727 cls: 'control-label box-input-label',
12728 //cls : 'input-group-addon',
12729 html : this.fieldLabel
12739 Roo.log(" no label && no align");
12754 html: this.boxLabel
12762 onClick : function()
12764 this.setChecked(true);
12767 setChecked : function(state,suppressEvent)
12770 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12771 v.dom.checked = false;
12775 this.checked = state;
12776 this.inputEl().dom.checked = state;
12778 if(suppressEvent !== true){
12779 this.fireEvent('check', this, state);
12782 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12786 getGroupValue : function()
12789 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12790 if(v.dom.checked == true){
12791 value = v.dom.value;
12799 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12800 * @return {Mixed} value The field value
12802 getValue : function(){
12803 return this.getGroupValue();
12809 //<script type="text/javascript">
12812 * Based Ext JS Library 1.1.1
12813 * Copyright(c) 2006-2007, Ext JS, LLC.
12819 * @class Roo.HtmlEditorCore
12820 * @extends Roo.Component
12821 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12823 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12826 Roo.HtmlEditorCore = function(config){
12829 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12832 * @event initialize
12833 * Fires when the editor is fully initialized (including the iframe)
12834 * @param {Roo.HtmlEditorCore} this
12839 * Fires when the editor is first receives the focus. Any insertion must wait
12840 * until after this event.
12841 * @param {Roo.HtmlEditorCore} this
12845 * @event beforesync
12846 * Fires before the textarea is updated with content from the editor iframe. Return false
12847 * to cancel the sync.
12848 * @param {Roo.HtmlEditorCore} this
12849 * @param {String} html
12853 * @event beforepush
12854 * Fires before the iframe editor is updated with content from the textarea. Return false
12855 * to cancel the push.
12856 * @param {Roo.HtmlEditorCore} this
12857 * @param {String} html
12862 * Fires when the textarea is updated with content from the editor iframe.
12863 * @param {Roo.HtmlEditorCore} this
12864 * @param {String} html
12869 * Fires when the iframe editor is updated with content from the textarea.
12870 * @param {Roo.HtmlEditorCore} this
12871 * @param {String} html
12876 * @event editorevent
12877 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12878 * @param {Roo.HtmlEditorCore} this
12886 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12890 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12896 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12901 * @cfg {Number} height (in pixels)
12905 * @cfg {Number} width (in pixels)
12910 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12913 stylesheets: false,
12918 // private properties
12919 validationEvent : false,
12921 initialized : false,
12923 sourceEditMode : false,
12924 onFocus : Roo.emptyFn,
12926 hideMode:'offsets',
12932 * Protected method that will not generally be called directly. It
12933 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12934 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12936 getDocMarkup : function(){
12939 Roo.log(this.stylesheets);
12941 // inherit styels from page...??
12942 if (this.stylesheets === false) {
12944 Roo.get(document.head).select('style').each(function(node) {
12945 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12948 Roo.get(document.head).select('link').each(function(node) {
12949 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12952 } else if (!this.stylesheets.length) {
12954 st = '<style type="text/css">' +
12955 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12958 Roo.each(this.stylesheets, function(s) {
12959 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12964 st += '<style type="text/css">' +
12965 'IMG { cursor: pointer } ' +
12969 return '<html><head>' + st +
12970 //<style type="text/css">' +
12971 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12973 ' </head><body class="roo-htmleditor-body"></body></html>';
12977 onRender : function(ct, position)
12980 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12981 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12984 this.el.dom.style.border = '0 none';
12985 this.el.dom.setAttribute('tabIndex', -1);
12986 this.el.addClass('x-hidden hide');
12990 if(Roo.isIE){ // fix IE 1px bogus margin
12991 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
12995 this.frameId = Roo.id();
12999 var iframe = this.owner.wrap.createChild({
13001 cls: 'form-control', // bootstrap..
13003 name: this.frameId,
13004 frameBorder : 'no',
13005 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13010 this.iframe = iframe.dom;
13012 this.assignDocWin();
13014 this.doc.designMode = 'on';
13017 this.doc.write(this.getDocMarkup());
13021 var task = { // must defer to wait for browser to be ready
13023 //console.log("run task?" + this.doc.readyState);
13024 this.assignDocWin();
13025 if(this.doc.body || this.doc.readyState == 'complete'){
13027 this.doc.designMode="on";
13031 Roo.TaskMgr.stop(task);
13032 this.initEditor.defer(10, this);
13039 Roo.TaskMgr.start(task);
13046 onResize : function(w, h)
13048 Roo.log('resize: ' +w + ',' + h );
13049 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13053 if(typeof w == 'number'){
13055 this.iframe.style.width = w + 'px';
13057 if(typeof h == 'number'){
13059 this.iframe.style.height = h + 'px';
13061 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13068 * Toggles the editor between standard and source edit mode.
13069 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13071 toggleSourceEdit : function(sourceEditMode){
13073 this.sourceEditMode = sourceEditMode === true;
13075 if(this.sourceEditMode){
13077 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13080 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13081 //this.iframe.className = '';
13084 //this.setSize(this.owner.wrap.getSize());
13085 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13092 * Protected method that will not generally be called directly. If you need/want
13093 * custom HTML cleanup, this is the method you should override.
13094 * @param {String} html The HTML to be cleaned
13095 * return {String} The cleaned HTML
13097 cleanHtml : function(html){
13098 html = String(html);
13099 if(html.length > 5){
13100 if(Roo.isSafari){ // strip safari nonsense
13101 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13104 if(html == ' '){
13111 * HTML Editor -> Textarea
13112 * Protected method that will not generally be called directly. Syncs the contents
13113 * of the editor iframe with the textarea.
13115 syncValue : function(){
13116 if(this.initialized){
13117 var bd = (this.doc.body || this.doc.documentElement);
13118 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13119 var html = bd.innerHTML;
13121 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13122 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13124 html = '<div style="'+m[0]+'">' + html + '</div>';
13127 html = this.cleanHtml(html);
13128 // fix up the special chars.. normaly like back quotes in word...
13129 // however we do not want to do this with chinese..
13130 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13131 var cc = b.charCodeAt();
13133 (cc >= 0x4E00 && cc < 0xA000 ) ||
13134 (cc >= 0x3400 && cc < 0x4E00 ) ||
13135 (cc >= 0xf900 && cc < 0xfb00 )
13141 if(this.owner.fireEvent('beforesync', this, html) !== false){
13142 this.el.dom.value = html;
13143 this.owner.fireEvent('sync', this, html);
13149 * Protected method that will not generally be called directly. Pushes the value of the textarea
13150 * into the iframe editor.
13152 pushValue : function(){
13153 if(this.initialized){
13154 var v = this.el.dom.value.trim();
13156 // if(v.length < 1){
13160 if(this.owner.fireEvent('beforepush', this, v) !== false){
13161 var d = (this.doc.body || this.doc.documentElement);
13163 this.cleanUpPaste();
13164 this.el.dom.value = d.innerHTML;
13165 this.owner.fireEvent('push', this, v);
13171 deferFocus : function(){
13172 this.focus.defer(10, this);
13176 focus : function(){
13177 if(this.win && !this.sourceEditMode){
13184 assignDocWin: function()
13186 var iframe = this.iframe;
13189 this.doc = iframe.contentWindow.document;
13190 this.win = iframe.contentWindow;
13192 if (!Roo.get(this.frameId)) {
13195 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13196 this.win = Roo.get(this.frameId).dom.contentWindow;
13201 initEditor : function(){
13202 //console.log("INIT EDITOR");
13203 this.assignDocWin();
13207 this.doc.designMode="on";
13209 this.doc.write(this.getDocMarkup());
13212 var dbody = (this.doc.body || this.doc.documentElement);
13213 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13214 // this copies styles from the containing element into thsi one..
13215 // not sure why we need all of this..
13216 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13217 ss['background-attachment'] = 'fixed'; // w3c
13218 dbody.bgProperties = 'fixed'; // ie
13219 Roo.DomHelper.applyStyles(dbody, ss);
13220 Roo.EventManager.on(this.doc, {
13221 //'mousedown': this.onEditorEvent,
13222 'mouseup': this.onEditorEvent,
13223 'dblclick': this.onEditorEvent,
13224 'click': this.onEditorEvent,
13225 'keyup': this.onEditorEvent,
13230 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13232 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13233 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13235 this.initialized = true;
13237 this.owner.fireEvent('initialize', this);
13242 onDestroy : function(){
13248 //for (var i =0; i < this.toolbars.length;i++) {
13249 // // fixme - ask toolbars for heights?
13250 // this.toolbars[i].onDestroy();
13253 //this.wrap.dom.innerHTML = '';
13254 //this.wrap.remove();
13259 onFirstFocus : function(){
13261 this.assignDocWin();
13264 this.activated = true;
13267 if(Roo.isGecko){ // prevent silly gecko errors
13269 var s = this.win.getSelection();
13270 if(!s.focusNode || s.focusNode.nodeType != 3){
13271 var r = s.getRangeAt(0);
13272 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13277 this.execCmd('useCSS', true);
13278 this.execCmd('styleWithCSS', false);
13281 this.owner.fireEvent('activate', this);
13285 adjustFont: function(btn){
13286 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13287 //if(Roo.isSafari){ // safari
13290 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13291 if(Roo.isSafari){ // safari
13292 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13293 v = (v < 10) ? 10 : v;
13294 v = (v > 48) ? 48 : v;
13295 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13300 v = Math.max(1, v+adjust);
13302 this.execCmd('FontSize', v );
13305 onEditorEvent : function(e){
13306 this.owner.fireEvent('editorevent', this, e);
13307 // this.updateToolbar();
13308 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13311 insertTag : function(tg)
13313 // could be a bit smarter... -> wrap the current selected tRoo..
13314 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13316 range = this.createRange(this.getSelection());
13317 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13318 wrappingNode.appendChild(range.extractContents());
13319 range.insertNode(wrappingNode);
13326 this.execCmd("formatblock", tg);
13330 insertText : function(txt)
13334 var range = this.createRange();
13335 range.deleteContents();
13336 //alert(Sender.getAttribute('label'));
13338 range.insertNode(this.doc.createTextNode(txt));
13344 * Executes a Midas editor command on the editor document and performs necessary focus and
13345 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13346 * @param {String} cmd The Midas command
13347 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13349 relayCmd : function(cmd, value){
13351 this.execCmd(cmd, value);
13352 this.owner.fireEvent('editorevent', this);
13353 //this.updateToolbar();
13354 this.owner.deferFocus();
13358 * Executes a Midas editor command directly on the editor document.
13359 * For visual commands, you should use {@link #relayCmd} instead.
13360 * <b>This should only be called after the editor is initialized.</b>
13361 * @param {String} cmd The Midas command
13362 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13364 execCmd : function(cmd, value){
13365 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13372 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13374 * @param {String} text | dom node..
13376 insertAtCursor : function(text)
13381 if(!this.activated){
13387 var r = this.doc.selection.createRange();
13398 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13402 // from jquery ui (MIT licenced)
13404 var win = this.win;
13406 if (win.getSelection && win.getSelection().getRangeAt) {
13407 range = win.getSelection().getRangeAt(0);
13408 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13409 range.insertNode(node);
13410 } else if (win.document.selection && win.document.selection.createRange) {
13411 // no firefox support
13412 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13413 win.document.selection.createRange().pasteHTML(txt);
13415 // no firefox support
13416 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13417 this.execCmd('InsertHTML', txt);
13426 mozKeyPress : function(e){
13428 var c = e.getCharCode(), cmd;
13431 c = String.fromCharCode(c).toLowerCase();
13445 this.cleanUpPaste.defer(100, this);
13453 e.preventDefault();
13461 fixKeys : function(){ // load time branching for fastest keydown performance
13463 return function(e){
13464 var k = e.getKey(), r;
13467 r = this.doc.selection.createRange();
13470 r.pasteHTML('    ');
13477 r = this.doc.selection.createRange();
13479 var target = r.parentElement();
13480 if(!target || target.tagName.toLowerCase() != 'li'){
13482 r.pasteHTML('<br />');
13488 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13489 this.cleanUpPaste.defer(100, this);
13495 }else if(Roo.isOpera){
13496 return function(e){
13497 var k = e.getKey();
13501 this.execCmd('InsertHTML','    ');
13504 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13505 this.cleanUpPaste.defer(100, this);
13510 }else if(Roo.isSafari){
13511 return function(e){
13512 var k = e.getKey();
13516 this.execCmd('InsertText','\t');
13520 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13521 this.cleanUpPaste.defer(100, this);
13529 getAllAncestors: function()
13531 var p = this.getSelectedNode();
13534 a.push(p); // push blank onto stack..
13535 p = this.getParentElement();
13539 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13543 a.push(this.doc.body);
13547 lastSelNode : false,
13550 getSelection : function()
13552 this.assignDocWin();
13553 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13556 getSelectedNode: function()
13558 // this may only work on Gecko!!!
13560 // should we cache this!!!!
13565 var range = this.createRange(this.getSelection()).cloneRange();
13568 var parent = range.parentElement();
13570 var testRange = range.duplicate();
13571 testRange.moveToElementText(parent);
13572 if (testRange.inRange(range)) {
13575 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13578 parent = parent.parentElement;
13583 // is ancestor a text element.
13584 var ac = range.commonAncestorContainer;
13585 if (ac.nodeType == 3) {
13586 ac = ac.parentNode;
13589 var ar = ac.childNodes;
13592 var other_nodes = [];
13593 var has_other_nodes = false;
13594 for (var i=0;i<ar.length;i++) {
13595 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13598 // fullly contained node.
13600 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13605 // probably selected..
13606 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13607 other_nodes.push(ar[i]);
13611 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13616 has_other_nodes = true;
13618 if (!nodes.length && other_nodes.length) {
13619 nodes= other_nodes;
13621 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13627 createRange: function(sel)
13629 // this has strange effects when using with
13630 // top toolbar - not sure if it's a great idea.
13631 //this.editor.contentWindow.focus();
13632 if (typeof sel != "undefined") {
13634 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13636 return this.doc.createRange();
13639 return this.doc.createRange();
13642 getParentElement: function()
13645 this.assignDocWin();
13646 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13648 var range = this.createRange(sel);
13651 var p = range.commonAncestorContainer;
13652 while (p.nodeType == 3) { // text node
13663 * Range intersection.. the hard stuff...
13667 * [ -- selected range --- ]
13671 * if end is before start or hits it. fail.
13672 * if start is after end or hits it fail.
13674 * if either hits (but other is outside. - then it's not
13680 // @see http://www.thismuchiknow.co.uk/?p=64.
13681 rangeIntersectsNode : function(range, node)
13683 var nodeRange = node.ownerDocument.createRange();
13685 nodeRange.selectNode(node);
13687 nodeRange.selectNodeContents(node);
13690 var rangeStartRange = range.cloneRange();
13691 rangeStartRange.collapse(true);
13693 var rangeEndRange = range.cloneRange();
13694 rangeEndRange.collapse(false);
13696 var nodeStartRange = nodeRange.cloneRange();
13697 nodeStartRange.collapse(true);
13699 var nodeEndRange = nodeRange.cloneRange();
13700 nodeEndRange.collapse(false);
13702 return rangeStartRange.compareBoundaryPoints(
13703 Range.START_TO_START, nodeEndRange) == -1 &&
13704 rangeEndRange.compareBoundaryPoints(
13705 Range.START_TO_START, nodeStartRange) == 1;
13709 rangeCompareNode : function(range, node)
13711 var nodeRange = node.ownerDocument.createRange();
13713 nodeRange.selectNode(node);
13715 nodeRange.selectNodeContents(node);
13719 range.collapse(true);
13721 nodeRange.collapse(true);
13723 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13724 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13726 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13728 var nodeIsBefore = ss == 1;
13729 var nodeIsAfter = ee == -1;
13731 if (nodeIsBefore && nodeIsAfter)
13733 if (!nodeIsBefore && nodeIsAfter)
13734 return 1; //right trailed.
13736 if (nodeIsBefore && !nodeIsAfter)
13737 return 2; // left trailed.
13742 // private? - in a new class?
13743 cleanUpPaste : function()
13745 // cleans up the whole document..
13746 Roo.log('cleanuppaste');
13747 this.cleanUpChildren(this.doc.body);
13748 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13749 if (clean != this.doc.body.innerHTML) {
13750 this.doc.body.innerHTML = clean;
13755 cleanWordChars : function(input) {// change the chars to hex code
13756 var he = Roo.HtmlEditorCore;
13758 var output = input;
13759 Roo.each(he.swapCodes, function(sw) {
13760 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13762 output = output.replace(swapper, sw[1]);
13769 cleanUpChildren : function (n)
13771 if (!n.childNodes.length) {
13774 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13775 this.cleanUpChild(n.childNodes[i]);
13782 cleanUpChild : function (node)
13785 //console.log(node);
13786 if (node.nodeName == "#text") {
13787 // clean up silly Windows -- stuff?
13790 if (node.nodeName == "#comment") {
13791 node.parentNode.removeChild(node);
13792 // clean up silly Windows -- stuff?
13796 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
13798 node.parentNode.removeChild(node);
13803 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13805 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13806 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13808 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13809 // remove_keep_children = true;
13812 if (remove_keep_children) {
13813 this.cleanUpChildren(node);
13814 // inserts everything just before this node...
13815 while (node.childNodes.length) {
13816 var cn = node.childNodes[0];
13817 node.removeChild(cn);
13818 node.parentNode.insertBefore(cn, node);
13820 node.parentNode.removeChild(node);
13824 if (!node.attributes || !node.attributes.length) {
13825 this.cleanUpChildren(node);
13829 function cleanAttr(n,v)
13832 if (v.match(/^\./) || v.match(/^\//)) {
13835 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13838 if (v.match(/^#/)) {
13841 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13842 node.removeAttribute(n);
13846 function cleanStyle(n,v)
13848 if (v.match(/expression/)) { //XSS?? should we even bother..
13849 node.removeAttribute(n);
13852 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13853 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13856 var parts = v.split(/;/);
13859 Roo.each(parts, function(p) {
13860 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13864 var l = p.split(':').shift().replace(/\s+/g,'');
13865 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13868 if ( cblack.indexOf(l) > -1) {
13869 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13870 //node.removeAttribute(n);
13874 // only allow 'c whitelisted system attributes'
13875 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13876 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13877 //node.removeAttribute(n);
13887 if (clean.length) {
13888 node.setAttribute(n, clean.join(';'));
13890 node.removeAttribute(n);
13896 for (var i = node.attributes.length-1; i > -1 ; i--) {
13897 var a = node.attributes[i];
13900 if (a.name.toLowerCase().substr(0,2)=='on') {
13901 node.removeAttribute(a.name);
13904 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13905 node.removeAttribute(a.name);
13908 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13909 cleanAttr(a.name,a.value); // fixme..
13912 if (a.name == 'style') {
13913 cleanStyle(a.name,a.value);
13916 /// clean up MS crap..
13917 // tecnically this should be a list of valid class'es..
13920 if (a.name == 'class') {
13921 if (a.value.match(/^Mso/)) {
13922 node.className = '';
13925 if (a.value.match(/body/)) {
13926 node.className = '';
13937 this.cleanUpChildren(node);
13943 // hide stuff that is not compatible
13957 * @event specialkey
13961 * @cfg {String} fieldClass @hide
13964 * @cfg {String} focusClass @hide
13967 * @cfg {String} autoCreate @hide
13970 * @cfg {String} inputType @hide
13973 * @cfg {String} invalidClass @hide
13976 * @cfg {String} invalidText @hide
13979 * @cfg {String} msgFx @hide
13982 * @cfg {String} validateOnBlur @hide
13986 Roo.HtmlEditorCore.white = [
13987 'area', 'br', 'img', 'input', 'hr', 'wbr',
13989 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
13990 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
13991 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
13992 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
13993 'table', 'ul', 'xmp',
13995 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
13998 'dir', 'menu', 'ol', 'ul', 'dl',
14004 Roo.HtmlEditorCore.black = [
14005 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14007 'base', 'basefont', 'bgsound', 'blink', 'body',
14008 'frame', 'frameset', 'head', 'html', 'ilayer',
14009 'iframe', 'layer', 'link', 'meta', 'object',
14010 'script', 'style' ,'title', 'xml' // clean later..
14012 Roo.HtmlEditorCore.clean = [
14013 'script', 'style', 'title', 'xml'
14015 Roo.HtmlEditorCore.remove = [
14020 Roo.HtmlEditorCore.ablack = [
14024 Roo.HtmlEditorCore.aclean = [
14025 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14029 Roo.HtmlEditorCore.pwhite= [
14030 'http', 'https', 'mailto'
14033 // white listed style attributes.
14034 Roo.HtmlEditorCore.cwhite= [
14035 // 'text-align', /// default is to allow most things..
14041 // black listed style attributes.
14042 Roo.HtmlEditorCore.cblack= [
14043 // 'font-size' -- this can be set by the project
14047 Roo.HtmlEditorCore.swapCodes =[
14066 * @class Roo.bootstrap.HtmlEditor
14067 * @extends Roo.bootstrap.TextArea
14068 * Bootstrap HtmlEditor class
14071 * Create a new HtmlEditor
14072 * @param {Object} config The config object
14075 Roo.bootstrap.HtmlEditor = function(config){
14076 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14077 if (!this.toolbars) {
14078 this.toolbars = [];
14080 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14083 * @event initialize
14084 * Fires when the editor is fully initialized (including the iframe)
14085 * @param {HtmlEditor} this
14090 * Fires when the editor is first receives the focus. Any insertion must wait
14091 * until after this event.
14092 * @param {HtmlEditor} this
14096 * @event beforesync
14097 * Fires before the textarea is updated with content from the editor iframe. Return false
14098 * to cancel the sync.
14099 * @param {HtmlEditor} this
14100 * @param {String} html
14104 * @event beforepush
14105 * Fires before the iframe editor is updated with content from the textarea. Return false
14106 * to cancel the push.
14107 * @param {HtmlEditor} this
14108 * @param {String} html
14113 * Fires when the textarea is updated with content from the editor iframe.
14114 * @param {HtmlEditor} this
14115 * @param {String} html
14120 * Fires when the iframe editor is updated with content from the textarea.
14121 * @param {HtmlEditor} this
14122 * @param {String} html
14126 * @event editmodechange
14127 * Fires when the editor switches edit modes
14128 * @param {HtmlEditor} this
14129 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14131 editmodechange: true,
14133 * @event editorevent
14134 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14135 * @param {HtmlEditor} this
14139 * @event firstfocus
14140 * Fires when on first focus - needed by toolbars..
14141 * @param {HtmlEditor} this
14146 * Auto save the htmlEditor value as a file into Events
14147 * @param {HtmlEditor} this
14151 * @event savedpreview
14152 * preview the saved version of htmlEditor
14153 * @param {HtmlEditor} this
14160 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14164 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14169 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14174 * @cfg {Number} height (in pixels)
14178 * @cfg {Number} width (in pixels)
14183 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14186 stylesheets: false,
14191 // private properties
14192 validationEvent : false,
14194 initialized : false,
14197 onFocus : Roo.emptyFn,
14199 hideMode:'offsets',
14202 tbContainer : false,
14204 toolbarContainer :function() {
14205 return this.wrap.select('.x-html-editor-tb',true).first();
14209 * Protected method that will not generally be called directly. It
14210 * is called when the editor creates its toolbar. Override this method if you need to
14211 * add custom toolbar buttons.
14212 * @param {HtmlEditor} editor
14214 createToolbar : function(){
14216 Roo.log("create toolbars");
14218 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14219 this.toolbars[0].render(this.toolbarContainer());
14223 // if (!editor.toolbars || !editor.toolbars.length) {
14224 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14227 // for (var i =0 ; i < editor.toolbars.length;i++) {
14228 // editor.toolbars[i] = Roo.factory(
14229 // typeof(editor.toolbars[i]) == 'string' ?
14230 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14231 // Roo.bootstrap.HtmlEditor);
14232 // editor.toolbars[i].init(editor);
14238 onRender : function(ct, position)
14240 // Roo.log("Call onRender: " + this.xtype);
14242 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14244 this.wrap = this.inputEl().wrap({
14245 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14248 this.editorcore.onRender(ct, position);
14250 if (this.resizable) {
14251 this.resizeEl = new Roo.Resizable(this.wrap, {
14255 minHeight : this.height,
14256 height: this.height,
14257 handles : this.resizable,
14260 resize : function(r, w, h) {
14261 _t.onResize(w,h); // -something
14267 this.createToolbar(this);
14270 if(!this.width && this.resizable){
14271 this.setSize(this.wrap.getSize());
14273 if (this.resizeEl) {
14274 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14275 // should trigger onReize..
14281 onResize : function(w, h)
14283 Roo.log('resize: ' +w + ',' + h );
14284 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14288 if(this.inputEl() ){
14289 if(typeof w == 'number'){
14290 var aw = w - this.wrap.getFrameWidth('lr');
14291 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14294 if(typeof h == 'number'){
14295 var tbh = -11; // fixme it needs to tool bar size!
14296 for (var i =0; i < this.toolbars.length;i++) {
14297 // fixme - ask toolbars for heights?
14298 tbh += this.toolbars[i].el.getHeight();
14299 //if (this.toolbars[i].footer) {
14300 // tbh += this.toolbars[i].footer.el.getHeight();
14308 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14309 ah -= 5; // knock a few pixes off for look..
14310 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14314 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14315 this.editorcore.onResize(ew,eh);
14320 * Toggles the editor between standard and source edit mode.
14321 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14323 toggleSourceEdit : function(sourceEditMode)
14325 this.editorcore.toggleSourceEdit(sourceEditMode);
14327 if(this.editorcore.sourceEditMode){
14328 Roo.log('editor - showing textarea');
14331 // Roo.log(this.syncValue());
14333 this.inputEl().removeClass('hide');
14334 this.inputEl().dom.removeAttribute('tabIndex');
14335 this.inputEl().focus();
14337 Roo.log('editor - hiding textarea');
14339 // Roo.log(this.pushValue());
14342 this.inputEl().addClass('hide');
14343 this.inputEl().dom.setAttribute('tabIndex', -1);
14344 //this.deferFocus();
14347 if(this.resizable){
14348 this.setSize(this.wrap.getSize());
14351 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14354 // private (for BoxComponent)
14355 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14357 // private (for BoxComponent)
14358 getResizeEl : function(){
14362 // private (for BoxComponent)
14363 getPositionEl : function(){
14368 initEvents : function(){
14369 this.originalValue = this.getValue();
14373 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14376 // markInvalid : Roo.emptyFn,
14378 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14381 // clearInvalid : Roo.emptyFn,
14383 setValue : function(v){
14384 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14385 this.editorcore.pushValue();
14390 deferFocus : function(){
14391 this.focus.defer(10, this);
14395 focus : function(){
14396 this.editorcore.focus();
14402 onDestroy : function(){
14408 for (var i =0; i < this.toolbars.length;i++) {
14409 // fixme - ask toolbars for heights?
14410 this.toolbars[i].onDestroy();
14413 this.wrap.dom.innerHTML = '';
14414 this.wrap.remove();
14419 onFirstFocus : function(){
14420 //Roo.log("onFirstFocus");
14421 this.editorcore.onFirstFocus();
14422 for (var i =0; i < this.toolbars.length;i++) {
14423 this.toolbars[i].onFirstFocus();
14429 syncValue : function()
14431 this.editorcore.syncValue();
14434 pushValue : function()
14436 this.editorcore.pushValue();
14440 // hide stuff that is not compatible
14454 * @event specialkey
14458 * @cfg {String} fieldClass @hide
14461 * @cfg {String} focusClass @hide
14464 * @cfg {String} autoCreate @hide
14467 * @cfg {String} inputType @hide
14470 * @cfg {String} invalidClass @hide
14473 * @cfg {String} invalidText @hide
14476 * @cfg {String} msgFx @hide
14479 * @cfg {String} validateOnBlur @hide
14490 * @class Roo.bootstrap.HtmlEditorToolbar1
14495 new Roo.bootstrap.HtmlEditor({
14498 new Roo.bootstrap.HtmlEditorToolbar1({
14499 disable : { fonts: 1 , format: 1, ..., ... , ...],
14505 * @cfg {Object} disable List of elements to disable..
14506 * @cfg {Array} btns List of additional buttons.
14510 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14513 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14516 Roo.apply(this, config);
14518 // default disabled, based on 'good practice'..
14519 this.disable = this.disable || {};
14520 Roo.applyIf(this.disable, {
14523 specialElements : true
14525 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14527 this.editor = config.editor;
14528 this.editorcore = config.editor.editorcore;
14530 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14532 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14533 // dont call parent... till later.
14535 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14541 editorcore : false,
14546 "h1","h2","h3","h4","h5","h6",
14548 "abbr", "acronym", "address", "cite", "samp", "var",
14552 onRender : function(ct, position)
14554 // Roo.log("Call onRender: " + this.xtype);
14556 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14558 this.el.dom.style.marginBottom = '0';
14560 var editorcore = this.editorcore;
14561 var editor= this.editor;
14564 var btn = function(id,cmd , toggle, handler){
14566 var event = toggle ? 'toggle' : 'click';
14571 xns: Roo.bootstrap,
14574 enableToggle:toggle !== false,
14576 pressed : toggle ? false : null,
14579 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14580 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14589 xns: Roo.bootstrap,
14590 glyphicon : 'font',
14594 xns: Roo.bootstrap,
14598 Roo.each(this.formats, function(f) {
14599 style.menu.items.push({
14601 xns: Roo.bootstrap,
14602 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14607 editorcore.insertTag(this.tagname);
14614 children.push(style);
14617 btn('bold',false,true);
14618 btn('italic',false,true);
14619 btn('align-left', 'justifyleft',true);
14620 btn('align-center', 'justifycenter',true);
14621 btn('align-right' , 'justifyright',true);
14622 btn('link', false, false, function(btn) {
14623 //Roo.log("create link?");
14624 var url = prompt(this.createLinkText, this.defaultLinkValue);
14625 if(url && url != 'http:/'+'/'){
14626 this.editorcore.relayCmd('createlink', url);
14629 btn('list','insertunorderedlist',true);
14630 btn('pencil', false,true, function(btn){
14633 this.toggleSourceEdit(btn.pressed);
14639 xns: Roo.bootstrap,
14644 xns: Roo.bootstrap,
14649 cog.menu.items.push({
14651 xns: Roo.bootstrap,
14652 html : Clean styles,
14657 editorcore.insertTag(this.tagname);
14666 this.xtype = 'Navbar';
14668 for(var i=0;i< children.length;i++) {
14670 this.buttons.add(this.addxtypeChild(children[i]));
14674 editor.on('editorevent', this.updateToolbar, this);
14676 onBtnClick : function(id)
14678 this.editorcore.relayCmd(id);
14679 this.editorcore.focus();
14683 * Protected method that will not generally be called directly. It triggers
14684 * a toolbar update by reading the markup state of the current selection in the editor.
14686 updateToolbar: function(){
14688 if(!this.editorcore.activated){
14689 this.editor.onFirstFocus(); // is this neeed?
14693 var btns = this.buttons;
14694 var doc = this.editorcore.doc;
14695 btns.get('bold').setActive(doc.queryCommandState('bold'));
14696 btns.get('italic').setActive(doc.queryCommandState('italic'));
14697 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14699 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14700 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14701 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14703 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14704 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14707 var ans = this.editorcore.getAllAncestors();
14708 if (this.formatCombo) {
14711 var store = this.formatCombo.store;
14712 this.formatCombo.setValue("");
14713 for (var i =0; i < ans.length;i++) {
14714 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14716 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14724 // hides menus... - so this cant be on a menu...
14725 Roo.bootstrap.MenuMgr.hideAll();
14727 Roo.bootstrap.MenuMgr.hideAll();
14728 //this.editorsyncValue();
14730 onFirstFocus: function() {
14731 this.buttons.each(function(item){
14735 toggleSourceEdit : function(sourceEditMode){
14738 if(sourceEditMode){
14739 Roo.log("disabling buttons");
14740 this.buttons.each( function(item){
14741 if(item.cmd != 'pencil'){
14747 Roo.log("enabling buttons");
14748 if(this.editorcore.initialized){
14749 this.buttons.each( function(item){
14755 Roo.log("calling toggole on editor");
14756 // tell the editor that it's been pressed..
14757 this.editor.toggleSourceEdit(sourceEditMode);
14767 * @class Roo.bootstrap.Table.AbstractSelectionModel
14768 * @extends Roo.util.Observable
14769 * Abstract base class for grid SelectionModels. It provides the interface that should be
14770 * implemented by descendant classes. This class should not be directly instantiated.
14773 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14774 this.locked = false;
14775 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14779 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14780 /** @ignore Called by the grid automatically. Do not call directly. */
14781 init : function(grid){
14787 * Locks the selections.
14790 this.locked = true;
14794 * Unlocks the selections.
14796 unlock : function(){
14797 this.locked = false;
14801 * Returns true if the selections are locked.
14802 * @return {Boolean}
14804 isLocked : function(){
14805 return this.locked;
14809 * @class Roo.bootstrap.Table.ColumnModel
14810 * @extends Roo.util.Observable
14811 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14812 * the columns in the table.
14815 * @param {Object} config An Array of column config objects. See this class's
14816 * config objects for details.
14818 Roo.bootstrap.Table.ColumnModel = function(config){
14820 * The config passed into the constructor
14822 this.config = config;
14825 // if no id, create one
14826 // if the column does not have a dataIndex mapping,
14827 // map it to the order it is in the config
14828 for(var i = 0, len = config.length; i < len; i++){
14830 if(typeof c.dataIndex == "undefined"){
14833 if(typeof c.renderer == "string"){
14834 c.renderer = Roo.util.Format[c.renderer];
14836 if(typeof c.id == "undefined"){
14839 // if(c.editor && c.editor.xtype){
14840 // c.editor = Roo.factory(c.editor, Roo.grid);
14842 // if(c.editor && c.editor.isFormField){
14843 // c.editor = new Roo.grid.GridEditor(c.editor);
14846 this.lookup[c.id] = c;
14850 * The width of columns which have no width specified (defaults to 100)
14853 this.defaultWidth = 100;
14856 * Default sortable of columns which have no sortable specified (defaults to false)
14859 this.defaultSortable = false;
14863 * @event widthchange
14864 * Fires when the width of a column changes.
14865 * @param {ColumnModel} this
14866 * @param {Number} columnIndex The column index
14867 * @param {Number} newWidth The new width
14869 "widthchange": true,
14871 * @event headerchange
14872 * Fires when the text of a header changes.
14873 * @param {ColumnModel} this
14874 * @param {Number} columnIndex The column index
14875 * @param {Number} newText The new header text
14877 "headerchange": true,
14879 * @event hiddenchange
14880 * Fires when a column is hidden or "unhidden".
14881 * @param {ColumnModel} this
14882 * @param {Number} columnIndex The column index
14883 * @param {Boolean} hidden true if hidden, false otherwise
14885 "hiddenchange": true,
14887 * @event columnmoved
14888 * Fires when a column is moved.
14889 * @param {ColumnModel} this
14890 * @param {Number} oldIndex
14891 * @param {Number} newIndex
14893 "columnmoved" : true,
14895 * @event columlockchange
14896 * Fires when a column's locked state is changed
14897 * @param {ColumnModel} this
14898 * @param {Number} colIndex
14899 * @param {Boolean} locked true if locked
14901 "columnlockchange" : true
14903 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14905 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14907 * @cfg {String} header The header text to display in the Grid view.
14910 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14911 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14912 * specified, the column's index is used as an index into the Record's data Array.
14915 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14916 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14919 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14920 * Defaults to the value of the {@link #defaultSortable} property.
14921 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14924 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14927 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14930 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14933 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14936 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14937 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14938 * default renderer uses the raw data value.
14941 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14945 * Returns the id of the column at the specified index.
14946 * @param {Number} index The column index
14947 * @return {String} the id
14949 getColumnId : function(index){
14950 return this.config[index].id;
14954 * Returns the column for a specified id.
14955 * @param {String} id The column id
14956 * @return {Object} the column
14958 getColumnById : function(id){
14959 return this.lookup[id];
14964 * Returns the column for a specified dataIndex.
14965 * @param {String} dataIndex The column dataIndex
14966 * @return {Object|Boolean} the column or false if not found
14968 getColumnByDataIndex: function(dataIndex){
14969 var index = this.findColumnIndex(dataIndex);
14970 return index > -1 ? this.config[index] : false;
14974 * Returns the index for a specified column id.
14975 * @param {String} id The column id
14976 * @return {Number} the index, or -1 if not found
14978 getIndexById : function(id){
14979 for(var i = 0, len = this.config.length; i < len; i++){
14980 if(this.config[i].id == id){
14988 * Returns the index for a specified column dataIndex.
14989 * @param {String} dataIndex The column dataIndex
14990 * @return {Number} the index, or -1 if not found
14993 findColumnIndex : function(dataIndex){
14994 for(var i = 0, len = this.config.length; i < len; i++){
14995 if(this.config[i].dataIndex == dataIndex){
15003 moveColumn : function(oldIndex, newIndex){
15004 var c = this.config[oldIndex];
15005 this.config.splice(oldIndex, 1);
15006 this.config.splice(newIndex, 0, c);
15007 this.dataMap = null;
15008 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15011 isLocked : function(colIndex){
15012 return this.config[colIndex].locked === true;
15015 setLocked : function(colIndex, value, suppressEvent){
15016 if(this.isLocked(colIndex) == value){
15019 this.config[colIndex].locked = value;
15020 if(!suppressEvent){
15021 this.fireEvent("columnlockchange", this, colIndex, value);
15025 getTotalLockedWidth : function(){
15026 var totalWidth = 0;
15027 for(var i = 0; i < this.config.length; i++){
15028 if(this.isLocked(i) && !this.isHidden(i)){
15029 this.totalWidth += this.getColumnWidth(i);
15035 getLockedCount : function(){
15036 for(var i = 0, len = this.config.length; i < len; i++){
15037 if(!this.isLocked(i)){
15044 * Returns the number of columns.
15047 getColumnCount : function(visibleOnly){
15048 if(visibleOnly === true){
15050 for(var i = 0, len = this.config.length; i < len; i++){
15051 if(!this.isHidden(i)){
15057 return this.config.length;
15061 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15062 * @param {Function} fn
15063 * @param {Object} scope (optional)
15064 * @return {Array} result
15066 getColumnsBy : function(fn, scope){
15068 for(var i = 0, len = this.config.length; i < len; i++){
15069 var c = this.config[i];
15070 if(fn.call(scope||this, c, i) === true){
15078 * Returns true if the specified column is sortable.
15079 * @param {Number} col The column index
15080 * @return {Boolean}
15082 isSortable : function(col){
15083 if(typeof this.config[col].sortable == "undefined"){
15084 return this.defaultSortable;
15086 return this.config[col].sortable;
15090 * Returns the rendering (formatting) function defined for the column.
15091 * @param {Number} col The column index.
15092 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15094 getRenderer : function(col){
15095 if(!this.config[col].renderer){
15096 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15098 return this.config[col].renderer;
15102 * Sets the rendering (formatting) function for a column.
15103 * @param {Number} col The column index
15104 * @param {Function} fn The function to use to process the cell's raw data
15105 * to return HTML markup for the grid view. The render function is called with
15106 * the following parameters:<ul>
15107 * <li>Data value.</li>
15108 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15109 * <li>css A CSS style string to apply to the table cell.</li>
15110 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15111 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15112 * <li>Row index</li>
15113 * <li>Column index</li>
15114 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15116 setRenderer : function(col, fn){
15117 this.config[col].renderer = fn;
15121 * Returns the width for the specified column.
15122 * @param {Number} col The column index
15125 getColumnWidth : function(col){
15126 return this.config[col].width * 1 || this.defaultWidth;
15130 * Sets the width for a column.
15131 * @param {Number} col The column index
15132 * @param {Number} width The new width
15134 setColumnWidth : function(col, width, suppressEvent){
15135 this.config[col].width = width;
15136 this.totalWidth = null;
15137 if(!suppressEvent){
15138 this.fireEvent("widthchange", this, col, width);
15143 * Returns the total width of all columns.
15144 * @param {Boolean} includeHidden True to include hidden column widths
15147 getTotalWidth : function(includeHidden){
15148 if(!this.totalWidth){
15149 this.totalWidth = 0;
15150 for(var i = 0, len = this.config.length; i < len; i++){
15151 if(includeHidden || !this.isHidden(i)){
15152 this.totalWidth += this.getColumnWidth(i);
15156 return this.totalWidth;
15160 * Returns the header for the specified column.
15161 * @param {Number} col The column index
15164 getColumnHeader : function(col){
15165 return this.config[col].header;
15169 * Sets the header for a column.
15170 * @param {Number} col The column index
15171 * @param {String} header The new header
15173 setColumnHeader : function(col, header){
15174 this.config[col].header = header;
15175 this.fireEvent("headerchange", this, col, header);
15179 * Returns the tooltip for the specified column.
15180 * @param {Number} col The column index
15183 getColumnTooltip : function(col){
15184 return this.config[col].tooltip;
15187 * Sets the tooltip for a column.
15188 * @param {Number} col The column index
15189 * @param {String} tooltip The new tooltip
15191 setColumnTooltip : function(col, tooltip){
15192 this.config[col].tooltip = tooltip;
15196 * Returns the dataIndex for the specified column.
15197 * @param {Number} col The column index
15200 getDataIndex : function(col){
15201 return this.config[col].dataIndex;
15205 * Sets the dataIndex for a column.
15206 * @param {Number} col The column index
15207 * @param {Number} dataIndex The new dataIndex
15209 setDataIndex : function(col, dataIndex){
15210 this.config[col].dataIndex = dataIndex;
15216 * Returns true if the cell is editable.
15217 * @param {Number} colIndex The column index
15218 * @param {Number} rowIndex The row index
15219 * @return {Boolean}
15221 isCellEditable : function(colIndex, rowIndex){
15222 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15226 * Returns the editor defined for the cell/column.
15227 * return false or null to disable editing.
15228 * @param {Number} colIndex The column index
15229 * @param {Number} rowIndex The row index
15232 getCellEditor : function(colIndex, rowIndex){
15233 return this.config[colIndex].editor;
15237 * Sets if a column is editable.
15238 * @param {Number} col The column index
15239 * @param {Boolean} editable True if the column is editable
15241 setEditable : function(col, editable){
15242 this.config[col].editable = editable;
15247 * Returns true if the column is hidden.
15248 * @param {Number} colIndex The column index
15249 * @return {Boolean}
15251 isHidden : function(colIndex){
15252 return this.config[colIndex].hidden;
15257 * Returns true if the column width cannot be changed
15259 isFixed : function(colIndex){
15260 return this.config[colIndex].fixed;
15264 * Returns true if the column can be resized
15265 * @return {Boolean}
15267 isResizable : function(colIndex){
15268 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15271 * Sets if a column is hidden.
15272 * @param {Number} colIndex The column index
15273 * @param {Boolean} hidden True if the column is hidden
15275 setHidden : function(colIndex, hidden){
15276 this.config[colIndex].hidden = hidden;
15277 this.totalWidth = null;
15278 this.fireEvent("hiddenchange", this, colIndex, hidden);
15282 * Sets the editor for a column.
15283 * @param {Number} col The column index
15284 * @param {Object} editor The editor object
15286 setEditor : function(col, editor){
15287 this.config[col].editor = editor;
15291 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15292 if(typeof value == "string" && value.length < 1){
15298 // Alias for backwards compatibility
15299 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15302 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15303 * @class Roo.bootstrap.Table.RowSelectionModel
15304 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15305 * It supports multiple selections and keyboard selection/navigation.
15307 * @param {Object} config
15310 Roo.bootstrap.Table.RowSelectionModel = function(config){
15311 Roo.apply(this, config);
15312 this.selections = new Roo.util.MixedCollection(false, function(o){
15317 this.lastActive = false;
15321 * @event selectionchange
15322 * Fires when the selection changes
15323 * @param {SelectionModel} this
15325 "selectionchange" : true,
15327 * @event afterselectionchange
15328 * Fires after the selection changes (eg. by key press or clicking)
15329 * @param {SelectionModel} this
15331 "afterselectionchange" : true,
15333 * @event beforerowselect
15334 * Fires when a row is selected being selected, return false to cancel.
15335 * @param {SelectionModel} this
15336 * @param {Number} rowIndex The selected index
15337 * @param {Boolean} keepExisting False if other selections will be cleared
15339 "beforerowselect" : true,
15342 * Fires when a row is selected.
15343 * @param {SelectionModel} this
15344 * @param {Number} rowIndex The selected index
15345 * @param {Roo.data.Record} r The record
15347 "rowselect" : true,
15349 * @event rowdeselect
15350 * Fires when a row is deselected.
15351 * @param {SelectionModel} this
15352 * @param {Number} rowIndex The selected index
15354 "rowdeselect" : true
15356 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15357 this.locked = false;
15360 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15362 * @cfg {Boolean} singleSelect
15363 * True to allow selection of only one row at a time (defaults to false)
15365 singleSelect : false,
15368 initEvents : function(){
15370 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15371 this.grid.on("mousedown", this.handleMouseDown, this);
15372 }else{ // allow click to work like normal
15373 this.grid.on("rowclick", this.handleDragableRowClick, this);
15376 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15377 "up" : function(e){
15379 this.selectPrevious(e.shiftKey);
15380 }else if(this.last !== false && this.lastActive !== false){
15381 var last = this.last;
15382 this.selectRange(this.last, this.lastActive-1);
15383 this.grid.getView().focusRow(this.lastActive);
15384 if(last !== false){
15388 this.selectFirstRow();
15390 this.fireEvent("afterselectionchange", this);
15392 "down" : function(e){
15394 this.selectNext(e.shiftKey);
15395 }else if(this.last !== false && this.lastActive !== false){
15396 var last = this.last;
15397 this.selectRange(this.last, this.lastActive+1);
15398 this.grid.getView().focusRow(this.lastActive);
15399 if(last !== false){
15403 this.selectFirstRow();
15405 this.fireEvent("afterselectionchange", this);
15410 var view = this.grid.view;
15411 view.on("refresh", this.onRefresh, this);
15412 view.on("rowupdated", this.onRowUpdated, this);
15413 view.on("rowremoved", this.onRemove, this);
15417 onRefresh : function(){
15418 var ds = this.grid.dataSource, i, v = this.grid.view;
15419 var s = this.selections;
15420 s.each(function(r){
15421 if((i = ds.indexOfId(r.id)) != -1){
15430 onRemove : function(v, index, r){
15431 this.selections.remove(r);
15435 onRowUpdated : function(v, index, r){
15436 if(this.isSelected(r)){
15437 v.onRowSelect(index);
15443 * @param {Array} records The records to select
15444 * @param {Boolean} keepExisting (optional) True to keep existing selections
15446 selectRecords : function(records, keepExisting){
15448 this.clearSelections();
15450 var ds = this.grid.dataSource;
15451 for(var i = 0, len = records.length; i < len; i++){
15452 this.selectRow(ds.indexOf(records[i]), true);
15457 * Gets the number of selected rows.
15460 getCount : function(){
15461 return this.selections.length;
15465 * Selects the first row in the grid.
15467 selectFirstRow : function(){
15472 * Select the last row.
15473 * @param {Boolean} keepExisting (optional) True to keep existing selections
15475 selectLastRow : function(keepExisting){
15476 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15480 * Selects the row immediately following the last selected row.
15481 * @param {Boolean} keepExisting (optional) True to keep existing selections
15483 selectNext : function(keepExisting){
15484 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15485 this.selectRow(this.last+1, keepExisting);
15486 this.grid.getView().focusRow(this.last);
15491 * Selects the row that precedes the last selected row.
15492 * @param {Boolean} keepExisting (optional) True to keep existing selections
15494 selectPrevious : function(keepExisting){
15496 this.selectRow(this.last-1, keepExisting);
15497 this.grid.getView().focusRow(this.last);
15502 * Returns the selected records
15503 * @return {Array} Array of selected records
15505 getSelections : function(){
15506 return [].concat(this.selections.items);
15510 * Returns the first selected record.
15513 getSelected : function(){
15514 return this.selections.itemAt(0);
15519 * Clears all selections.
15521 clearSelections : function(fast){
15522 if(this.locked) return;
15524 var ds = this.grid.dataSource;
15525 var s = this.selections;
15526 s.each(function(r){
15527 this.deselectRow(ds.indexOfId(r.id));
15531 this.selections.clear();
15538 * Selects all rows.
15540 selectAll : function(){
15541 if(this.locked) return;
15542 this.selections.clear();
15543 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15544 this.selectRow(i, true);
15549 * Returns True if there is a selection.
15550 * @return {Boolean}
15552 hasSelection : function(){
15553 return this.selections.length > 0;
15557 * Returns True if the specified row is selected.
15558 * @param {Number/Record} record The record or index of the record to check
15559 * @return {Boolean}
15561 isSelected : function(index){
15562 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15563 return (r && this.selections.key(r.id) ? true : false);
15567 * Returns True if the specified record id is selected.
15568 * @param {String} id The id of record to check
15569 * @return {Boolean}
15571 isIdSelected : function(id){
15572 return (this.selections.key(id) ? true : false);
15576 handleMouseDown : function(e, t){
15577 var view = this.grid.getView(), rowIndex;
15578 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15581 if(e.shiftKey && this.last !== false){
15582 var last = this.last;
15583 this.selectRange(last, rowIndex, e.ctrlKey);
15584 this.last = last; // reset the last
15585 view.focusRow(rowIndex);
15587 var isSelected = this.isSelected(rowIndex);
15588 if(e.button !== 0 && isSelected){
15589 view.focusRow(rowIndex);
15590 }else if(e.ctrlKey && isSelected){
15591 this.deselectRow(rowIndex);
15592 }else if(!isSelected){
15593 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15594 view.focusRow(rowIndex);
15597 this.fireEvent("afterselectionchange", this);
15600 handleDragableRowClick : function(grid, rowIndex, e)
15602 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15603 this.selectRow(rowIndex, false);
15604 grid.view.focusRow(rowIndex);
15605 this.fireEvent("afterselectionchange", this);
15610 * Selects multiple rows.
15611 * @param {Array} rows Array of the indexes of the row to select
15612 * @param {Boolean} keepExisting (optional) True to keep existing selections
15614 selectRows : function(rows, keepExisting){
15616 this.clearSelections();
15618 for(var i = 0, len = rows.length; i < len; i++){
15619 this.selectRow(rows[i], true);
15624 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15625 * @param {Number} startRow The index of the first row in the range
15626 * @param {Number} endRow The index of the last row in the range
15627 * @param {Boolean} keepExisting (optional) True to retain existing selections
15629 selectRange : function(startRow, endRow, keepExisting){
15630 if(this.locked) return;
15632 this.clearSelections();
15634 if(startRow <= endRow){
15635 for(var i = startRow; i <= endRow; i++){
15636 this.selectRow(i, true);
15639 for(var i = startRow; i >= endRow; i--){
15640 this.selectRow(i, true);
15646 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15647 * @param {Number} startRow The index of the first row in the range
15648 * @param {Number} endRow The index of the last row in the range
15650 deselectRange : function(startRow, endRow, preventViewNotify){
15651 if(this.locked) return;
15652 for(var i = startRow; i <= endRow; i++){
15653 this.deselectRow(i, preventViewNotify);
15659 * @param {Number} row The index of the row to select
15660 * @param {Boolean} keepExisting (optional) True to keep existing selections
15662 selectRow : function(index, keepExisting, preventViewNotify){
15663 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15664 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15665 if(!keepExisting || this.singleSelect){
15666 this.clearSelections();
15668 var r = this.grid.dataSource.getAt(index);
15669 this.selections.add(r);
15670 this.last = this.lastActive = index;
15671 if(!preventViewNotify){
15672 this.grid.getView().onRowSelect(index);
15674 this.fireEvent("rowselect", this, index, r);
15675 this.fireEvent("selectionchange", this);
15681 * @param {Number} row The index of the row to deselect
15683 deselectRow : function(index, preventViewNotify){
15684 if(this.locked) return;
15685 if(this.last == index){
15688 if(this.lastActive == index){
15689 this.lastActive = false;
15691 var r = this.grid.dataSource.getAt(index);
15692 this.selections.remove(r);
15693 if(!preventViewNotify){
15694 this.grid.getView().onRowDeselect(index);
15696 this.fireEvent("rowdeselect", this, index);
15697 this.fireEvent("selectionchange", this);
15701 restoreLast : function(){
15703 this.last = this._last;
15708 acceptsNav : function(row, col, cm){
15709 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15713 onEditorKey : function(field, e){
15714 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15719 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15721 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15723 }else if(k == e.ENTER && !e.ctrlKey){
15727 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15729 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15731 }else if(k == e.ESC){
15735 g.startEditing(newCell[0], newCell[1]);
15746 * @class Roo.bootstrap.MessageBar
15747 * @extends Roo.bootstrap.Component
15748 * Bootstrap MessageBar class
15749 * @cfg {String} html contents of the MessageBar
15750 * @cfg {String} weight (info | success | warning | danger) default info
15751 * @cfg {String} beforeClass insert the bar before the given class
15752 * @cfg {Boolean} closable (true | false) default false
15753 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15756 * Create a new Element
15757 * @param {Object} config The config object
15760 Roo.bootstrap.MessageBar = function(config){
15761 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15764 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15770 beforeClass: 'bootstrap-sticky-wrap',
15772 getAutoCreate : function(){
15776 cls: 'alert alert-dismissable alert-' + this.weight,
15781 html: this.html || ''
15787 cfg.cls += ' alert-messages-fixed';
15801 onRender : function(ct, position)
15803 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15806 var cfg = Roo.apply({}, this.getAutoCreate());
15810 cfg.cls += ' ' + this.cls;
15813 cfg.style = this.style;
15815 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15817 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15820 this.el.select('>button.close').on('click', this.hide, this);
15826 if (!this.rendered) {
15832 this.fireEvent('show', this);
15838 if (!this.rendered) {
15844 this.fireEvent('hide', this);
15847 update : function()
15849 // var e = this.el.dom.firstChild;
15851 // if(this.closable){
15852 // e = e.nextSibling;
15855 // e.data = this.html || '';
15857 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';