4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
21 * Do not use directly - it does not do anything..
22 * @param {Object} config The config object
27 Roo.bootstrap.Component = function(config){
28 Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
34 allowDomMove : false, // to stop relocations in parent onRender...
42 initEvents : function() { },
48 can_build_overlaid : true,
55 // returns the parent component..
56 return Roo.ComponentMgr.get(this.parentId)
62 onRender : function(ct, position)
64 // Roo.log("Call onRender: " + this.xtype);
66 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
69 if (this.el.attr('xtype')) {
70 this.el.attr('xtypex', this.el.attr('xtype'));
71 this.el.dom.removeAttribute('xtype');
81 var cfg = Roo.apply({}, this.getAutoCreate());
84 // fill in the extra attributes
85 if (this.xattr && typeof(this.xattr) =='object') {
86 for (var i in this.xattr) {
87 cfg[i] = this.xattr[i];
92 cfg.dataId = this.dataId;
96 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
99 if (this.style) { // fixme needs to support more complex style data.
100 cfg.style = this.style;
104 cfg.name = this.name;
107 this.el = ct.createChild(cfg, position);
109 if(this.tabIndex !== undefined){
110 this.el.dom.setAttribute('tabIndex', this.tabIndex);
117 getChildContainer : function()
123 addxtype : function(tree,cntr)
127 cn = Roo.factory(tree);
129 cn.parentType = this.xtype; //??
130 cn.parentId = this.id;
132 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
134 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
136 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
138 var build_from_html = Roo.XComponent.build_from_html;
140 var is_body = (tree.xtype == 'Body') ;
142 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
144 var self_cntr_el = Roo.get(this[cntr]());
146 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148 return this.addxtypeChild(tree,cntr);
151 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
154 return this.addxtypeChild(Roo.apply({}, tree),cntr);
157 Roo.log('skipping render');
165 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
171 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
175 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
180 addxtypeChild : function (tree, cntr)
182 Roo.log('addxtypeChild:' + cntr);
184 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
187 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188 (typeof(tree['flexy:foreach']) != 'undefined');
193 // render the element if it's not BODY.
194 if (tree.xtype != 'Body') {
196 cn = Roo.factory(tree);
198 cn.parentType = this.xtype; //??
199 cn.parentId = this.id;
201 var build_from_html = Roo.XComponent.build_from_html;
204 // does the container contain child eleemnts with 'xtype' attributes.
205 // that match this xtype..
206 // note - when we render we create these as well..
207 // so we should check to see if body has xtype set.
208 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
210 var self_cntr_el = Roo.get(this[cntr]());
211 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
213 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
220 //echild.dom.removeAttribute('xtype');
222 Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229 // if object has flexy:if - then it may or may not be rendered.
230 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
231 // skip a flexy if element.
232 Roo.log('skipping render');
235 // actually if flexy:foreach is found, we really want to create
236 // multiple copies here...
238 //Roo.log(this[cntr]());
239 cn.render(this[cntr]());
241 // then add the element..
248 if (typeof (tree.menu) != 'undefined') {
249 tree.menu.parentType = cn.xtype;
250 tree.menu.triggerEl = cn.el;
251 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
255 if (!tree.items || !tree.items.length) {
259 var items = tree.items;
262 //Roo.log(items.length);
264 for(var i =0;i < items.length;i++) {
265 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
286 * @class Roo.bootstrap.Body
287 * @extends Roo.bootstrap.Component
288 * Bootstrap Body class
292 * @param {Object} config The config object
295 Roo.bootstrap.Body = function(config){
296 Roo.bootstrap.Body.superclass.constructor.call(this, config);
297 this.el = Roo.get(document.body);
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
305 onRender : function(ct, position){
308 //this.el.addClass([this.fieldClass, this.cls]);
326 * @class Roo.bootstrap.ButtonGroup
327 * @extends Roo.bootstrap.Component
328 * Bootstrap ButtonGroup class
329 * @cfg {String} size lg | sm | xs (default empty normal)
330 * @cfg {String} align vertical | justified (default none)
331 * @cfg {String} direction up | down (default down)
332 * @cfg {Boolean} toolbar false | true
333 * @cfg {Boolean} btn true | false
338 * @param {Object} config The config object
341 Roo.bootstrap.ButtonGroup = function(config){
342 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
353 getAutoCreate : function(){
359 cfg.html = this.html || cfg.html;
370 if (['vertical','justified'].indexOf(this.align)!==-1) {
371 cfg.cls = 'btn-group-' + this.align;
373 if (this.align == 'justified') {
374 console.log(this.items);
378 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379 cfg.cls += ' btn-group-' + this.size;
382 if (this.direction == 'up') {
383 cfg.cls += ' dropup' ;
399 * @class Roo.bootstrap.Button
400 * @extends Roo.bootstrap.Component
401 * Bootstrap Button class
402 * @cfg {String} html The button content
403 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404 * @cfg {String} size empty | lg | sm | xs
405 * @cfg {String} tag empty | a | input | submit
406 * @cfg {String} href empty or href
407 * @cfg {Boolean} disabled false | true
408 * @cfg {Boolean} isClose false | true
409 * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410 * @cfg {String} badge text for badge
411 * @cfg {String} theme default (or empty) | glow
412 * @cfg {Boolean} inverse false | true
413 * @cfg {Boolean} toggle false | true
414 * @cfg {String} ontext text for on toggle state
415 * @cfg {String} offtext text for off toggle state
416 * @cfg {Boolean} defaulton true | false
417 * @cfg {Boolean} preventDefault (true | false) default true
418 * @cfg {Boolean} removeClass true | false remove the standard class..
419 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
422 * Create a new button
423 * @param {Object} config The config object
427 Roo.bootstrap.Button = function(config){
428 Roo.bootstrap.Button.superclass.constructor.call(this, config);
433 * When a butotn is pressed
434 * @param {Roo.EventObject} e
439 * After the button has been toggles
440 * @param {Roo.EventObject} e
441 * @param {boolean} pressed (also available as button.pressed)
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
465 preventDefault: true,
474 getAutoCreate : function(){
482 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
488 cfg.html = this.html || cfg.html;
490 if (this.toggle == true) {
493 cls: 'slider-frame roo-button',
498 'data-off-text':'OFF',
499 cls: 'slider-button',
505 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506 cfg.cls += ' '+this.weight;
515 cfg["aria-hidden"] = true;
517 cfg.html = "×";
523 if (this.theme==='default') {
524 cfg.cls = 'btn roo-button';
526 //if (this.parentType != 'Navbar') {
527 this.weight = this.weight.length ? this.weight : 'default';
529 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
531 cfg.cls += ' btn-' + this.weight;
533 } else if (this.theme==='glow') {
536 cfg.cls = 'btn-glow roo-button';
538 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
540 cfg.cls += ' ' + this.weight;
546 this.cls += ' inverse';
551 cfg.cls += ' active';
555 cfg.disabled = 'disabled';
559 Roo.log('changing to ul' );
561 this.glyphicon = 'caret';
564 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
566 //gsRoo.log(this.parentType);
567 if (this.parentType === 'Navbar' && !this.parent().bar) {
568 Roo.log('changing to li?');
577 href : this.href || '#'
580 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
581 cfg.cls += ' dropdown';
588 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
590 if (this.glyphicon) {
591 cfg.html = ' ' + cfg.html;
596 cls: 'glyphicon glyphicon-' + this.glyphicon
606 // cfg.cls='btn roo-button';
610 var value = cfg.html;
615 cls: 'glyphicon glyphicon-' + this.glyphicon,
634 cfg.cls += ' dropdown';
635 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
638 if (cfg.tag !== 'a' && this.href !== '') {
639 throw "Tag must be a to set href.";
640 } else if (this.href.length > 0) {
641 cfg.href = this.href;
644 if(this.removeClass){
649 cfg.target = this.target;
654 initEvents: function() {
655 // Roo.log('init events?');
656 // Roo.log(this.el.dom);
657 if (this.el.hasClass('roo-button')) {
658 this.el.on('click', this.onClick, this);
660 this.el.select('.roo-button').on('click', this.onClick, this);
666 onClick : function(e)
672 Roo.log('button on click ');
673 if(this.preventDefault){
676 if (this.pressed === true || this.pressed === false) {
677 this.pressed = !this.pressed;
678 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679 this.fireEvent('toggle', this, e, this.pressed);
683 this.fireEvent('click', this, e);
687 * Enables this button
691 this.disabled = false;
692 this.el.removeClass('disabled');
696 * Disable this button
700 this.disabled = true;
701 this.el.addClass('disabled');
704 * sets the active state on/off,
705 * @param {Boolean} state (optional) Force a particular state
707 setActive : function(v) {
709 this.el[v ? 'addClass' : 'removeClass']('active');
712 * toggles the current active state
714 toggleActive : function()
716 var active = this.el.hasClass('active');
717 this.setActive(!active);
734 * @class Roo.bootstrap.Column
735 * @extends Roo.bootstrap.Component
736 * Bootstrap Column class
737 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739 * @cfg {Number} md colspan out of 12 for computer-sized screens
740 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741 * @cfg {String} html content of column.
744 * Create a new Column
745 * @param {Object} config The config object
748 Roo.bootstrap.Column = function(config){
749 Roo.bootstrap.Column.superclass.constructor.call(this, config);
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
761 getAutoCreate : function(){
762 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
770 ['xs','sm','md','lg'].map(function(size){
771 if (settings[size]) {
772 cfg.cls += ' col-' + size + '-' + settings[size];
775 if (this.html.length) {
776 cfg.html = this.html;
795 * @class Roo.bootstrap.Container
796 * @extends Roo.bootstrap.Component
797 * Bootstrap Container class
798 * @cfg {Boolean} jumbotron is it a jumbotron element
799 * @cfg {String} html content of element
800 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802 * @cfg {String} header content of header (for panel)
803 * @cfg {String} footer content of footer (for panel)
804 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
807 * Create a new Container
808 * @param {Object} config The config object
811 Roo.bootstrap.Container = function(config){
812 Roo.bootstrap.Container.superclass.constructor.call(this, config);
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
825 getChildContainer : function() {
831 if (this.panel.length) {
832 return this.el.select('.panel-body',true).first();
839 getAutoCreate : function(){
845 if (this.jumbotron) {
846 cfg.cls = 'jumbotron';
849 cfg.cls = this.cls + '';
852 if (this.sticky.length) {
854 var bd = Roo.get(document.body);
855 if (!bd.hasClass('bootstrap-sticky')) {
856 bd.addClass('bootstrap-sticky');
857 Roo.select('html',true).setStyle('height', '100%');
860 cfg.cls += 'bootstrap-sticky-' + this.sticky;
864 if (this.well.length) {
868 cfg.cls +=' well well-' +this.well;
878 if (this.panel.length) {
879 cfg.cls += ' panel panel-' + this.panel;
881 if (this.header.length) {
884 cls : 'panel-heading',
900 if (this.footer.length) {
902 cls : 'panel-footer',
910 body.html = this.html || cfg.html;
912 if (!cfg.cls.length) {
913 cfg.cls = 'container';
930 * @class Roo.bootstrap.Img
931 * @extends Roo.bootstrap.Component
932 * Bootstrap Img class
933 * @cfg {Boolean} imgResponsive false | true
934 * @cfg {String} border rounded | circle | thumbnail
935 * @cfg {String} src image source
936 * @cfg {String} alt image alternative text
937 * @cfg {String} href a tag href
938 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
942 * @param {Object} config The config object
945 Roo.bootstrap.Img = function(config){
946 Roo.bootstrap.Img.superclass.constructor.call(this, config);
952 * The img click event for the img.
953 * @param {Roo.EventObject} e
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
967 getAutoCreate : function(){
971 cls: 'img-responsive',
975 cfg.html = this.html || cfg.html;
977 cfg.src = this.src || cfg.src;
979 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980 cfg.cls += ' img-' + this.border;
997 a.target = this.target;
1003 return (this.href) ? a : cfg;
1006 initEvents: function() {
1009 this.el.on('click', this.onClick, this);
1013 onClick : function(e)
1015 Roo.log('img onclick');
1016 this.fireEvent('click', this, e);
1029 * @class Roo.bootstrap.Header
1030 * @extends Roo.bootstrap.Component
1031 * Bootstrap Header class
1032 * @cfg {String} html content of header
1033 * @cfg {Number} level (1|2|3|4|5|6) default 1
1036 * Create a new Header
1037 * @param {Object} config The config object
1041 Roo.bootstrap.Header = function(config){
1042 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1053 getAutoCreate : function(){
1056 tag: 'h' + (1 *this.level),
1057 html: this.html || 'fill in html'
1069 * Ext JS Library 1.1.1
1070 * Copyright(c) 2006-2007, Ext JS, LLC.
1072 * Originally Released Under LGPL - original licence link has changed is not relivant.
1075 * <script type="text/javascript">
1079 * @class Roo.bootstrap.MenuMgr
1080 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1083 Roo.bootstrap.MenuMgr = function(){
1084 var menus, active, groups = {}, attached = false, lastShow = new Date();
1086 // private - called when first menu is created
1089 active = new Roo.util.MixedCollection();
1090 Roo.get(document).addKeyListener(27, function(){
1091 if(active.length > 0){
1099 if(active && active.length > 0){
1100 var c = active.clone();
1110 if(active.length < 1){
1111 Roo.get(document).un("mouseup", onMouseDown);
1119 var last = active.last();
1120 lastShow = new Date();
1123 Roo.get(document).on("mouseup", onMouseDown);
1128 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129 m.parentMenu.activeChild = m;
1130 }else if(last && last.isVisible()){
1131 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1136 function onBeforeHide(m){
1138 m.activeChild.hide();
1140 if(m.autoHideTimer){
1141 clearTimeout(m.autoHideTimer);
1142 delete m.autoHideTimer;
1147 function onBeforeShow(m){
1148 var pm = m.parentMenu;
1149 if(!pm && !m.allowOtherMenus){
1151 }else if(pm && pm.activeChild && active != m){
1152 pm.activeChild.hide();
1157 function onMouseDown(e){
1158 Roo.log("on MouseDown");
1159 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1167 function onBeforeCheck(mi, state){
1169 var g = groups[mi.group];
1170 for(var i = 0, l = g.length; i < l; i++){
1172 g[i].setChecked(false);
1181 * Hides all menus that are currently visible
1183 hideAll : function(){
1188 register : function(menu){
1192 menus[menu.id] = menu;
1193 menu.on("beforehide", onBeforeHide);
1194 menu.on("hide", onHide);
1195 menu.on("beforeshow", onBeforeShow);
1196 menu.on("show", onShow);
1198 if(g && menu.events["checkchange"]){
1202 groups[g].push(menu);
1203 menu.on("checkchange", onCheck);
1208 * Returns a {@link Roo.menu.Menu} object
1209 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210 * be used to generate and return a new Menu instance.
1212 get : function(menu){
1213 if(typeof menu == "string"){ // menu id
1215 }else if(menu.events){ // menu instance
1218 /*else if(typeof menu.length == 'number'){ // array of menu items?
1219 return new Roo.bootstrap.Menu({items:menu});
1220 }else{ // otherwise, must be a config
1221 return new Roo.bootstrap.Menu(menu);
1228 unregister : function(menu){
1229 delete menus[menu.id];
1230 menu.un("beforehide", onBeforeHide);
1231 menu.un("hide", onHide);
1232 menu.un("beforeshow", onBeforeShow);
1233 menu.un("show", onShow);
1235 if(g && menu.events["checkchange"]){
1236 groups[g].remove(menu);
1237 menu.un("checkchange", onCheck);
1242 registerCheckable : function(menuItem){
1243 var g = menuItem.group;
1248 groups[g].push(menuItem);
1249 menuItem.on("beforecheckchange", onBeforeCheck);
1254 unregisterCheckable : function(menuItem){
1255 var g = menuItem.group;
1257 groups[g].remove(menuItem);
1258 menuItem.un("beforecheckchange", onBeforeCheck);
1270 * @class Roo.bootstrap.Menu
1271 * @extends Roo.bootstrap.Component
1272 * Bootstrap Menu class - container for MenuItems
1273 * @cfg {String} type type of menu
1277 * @param {Object} config The config object
1281 Roo.bootstrap.Menu = function(config){
1282 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283 if (this.registerMenu) {
1284 Roo.bootstrap.MenuMgr.register(this);
1289 * Fires before this menu is displayed
1290 * @param {Roo.menu.Menu} this
1295 * Fires before this menu is hidden
1296 * @param {Roo.menu.Menu} this
1301 * Fires after this menu is displayed
1302 * @param {Roo.menu.Menu} this
1307 * Fires after this menu is hidden
1308 * @param {Roo.menu.Menu} this
1313 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314 * @param {Roo.menu.Menu} this
1315 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316 * @param {Roo.EventObject} e
1321 * Fires when the mouse is hovering over this menu
1322 * @param {Roo.menu.Menu} this
1323 * @param {Roo.EventObject} e
1324 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1329 * Fires when the mouse exits this menu
1330 * @param {Roo.menu.Menu} this
1331 * @param {Roo.EventObject} e
1332 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1337 * Fires when a menu item contained in this menu is clicked
1338 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339 * @param {Roo.EventObject} e
1343 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1350 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1353 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1355 registerMenu : true,
1357 menuItems :false, // stores the menu items..
1363 getChildContainer : function() {
1367 getAutoCreate : function(){
1369 //if (['right'].indexOf(this.align)!==-1) {
1370 // cfg.cn[1].cls += ' pull-right'
1374 cls : 'dropdown-menu' ,
1375 style : 'z-index:1000'
1379 if (this.type === 'submenu') {
1380 cfg.cls = 'submenu active'
1385 initEvents : function() {
1387 // Roo.log("ADD event");
1388 // Roo.log(this.triggerEl.dom);
1389 this.triggerEl.on('click', this.onTriggerPress, this);
1390 this.triggerEl.addClass('dropdown-toggle');
1391 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1393 this.el.on("mouseover", this.onMouseOver, this);
1394 this.el.on("mouseout", this.onMouseOut, this);
1398 findTargetItem : function(e){
1399 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1403 //Roo.log(t); Roo.log(t.id);
1405 //Roo.log(this.menuitems);
1406 return this.menuitems.get(t.id);
1408 //return this.items.get(t.menuItemId);
1413 onClick : function(e){
1414 Roo.log("menu.onClick");
1415 var t = this.findTargetItem(e);
1421 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1422 if(t == this.activeItem && t.shouldDeactivate(e)){
1423 this.activeItem.deactivate();
1424 delete this.activeItem;
1428 this.setActiveItem(t, true);
1435 Roo.log('pass click event');
1439 this.fireEvent("click", this, t, e);
1443 onMouseOver : function(e){
1444 var t = this.findTargetItem(e);
1447 // if(t.canActivate && !t.disabled){
1448 // this.setActiveItem(t, true);
1452 this.fireEvent("mouseover", this, e, t);
1454 isVisible : function(){
1455 return !this.hidden;
1457 onMouseOut : function(e){
1458 var t = this.findTargetItem(e);
1461 // if(t == this.activeItem && t.shouldDeactivate(e)){
1462 // this.activeItem.deactivate();
1463 // delete this.activeItem;
1466 this.fireEvent("mouseout", this, e, t);
1471 * Displays this menu relative to another element
1472 * @param {String/HTMLElement/Roo.Element} element The element to align to
1473 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474 * the element (defaults to this.defaultAlign)
1475 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1477 show : function(el, pos, parentMenu){
1478 this.parentMenu = parentMenu;
1482 this.fireEvent("beforeshow", this);
1483 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1486 * Displays this menu at a specific xy position
1487 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1490 showAt : function(xy, parentMenu, /* private: */_e){
1491 this.parentMenu = parentMenu;
1496 this.fireEvent("beforeshow", this);
1498 //xy = this.el.adjustForConstraints(xy);
1500 //this.el.setXY(xy);
1502 this.hideMenuItems();
1503 this.hidden = false;
1504 this.triggerEl.addClass('open');
1506 this.fireEvent("show", this);
1512 this.doFocus.defer(50, this);
1516 doFocus : function(){
1518 this.focusEl.focus();
1523 * Hides this menu and optionally all parent menus
1524 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1526 hide : function(deep){
1528 this.hideMenuItems();
1529 if(this.el && this.isVisible()){
1530 this.fireEvent("beforehide", this);
1531 if(this.activeItem){
1532 this.activeItem.deactivate();
1533 this.activeItem = null;
1535 this.triggerEl.removeClass('open');;
1537 this.fireEvent("hide", this);
1539 if(deep === true && this.parentMenu){
1540 this.parentMenu.hide(true);
1544 onTriggerPress : function(e)
1547 Roo.log('trigger press');
1548 //Roo.log(e.getTarget());
1549 // Roo.log(this.triggerEl.dom);
1550 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1553 if (this.isVisible()) {
1557 this.show(this.triggerEl, false, false);
1566 hideMenuItems : function()
1568 //$(backdrop).remove()
1569 Roo.select('.open',true).each(function(aa) {
1571 aa.removeClass('open');
1572 //var parent = getParent($(this))
1573 //var relatedTarget = { relatedTarget: this }
1575 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576 //if (e.isDefaultPrevented()) return
1577 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1580 addxtypeChild : function (tree, cntr) {
1581 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1583 this.menuitems.add(comp);
1604 * @class Roo.bootstrap.MenuItem
1605 * @extends Roo.bootstrap.Component
1606 * Bootstrap MenuItem class
1607 * @cfg {String} html the menu label
1608 * @cfg {String} href the link
1609 * @cfg {Boolean} preventDefault (true | false) default true
1613 * Create a new MenuItem
1614 * @param {Object} config The config object
1618 Roo.bootstrap.MenuItem = function(config){
1619 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1624 * The raw click event for the entire grid.
1625 * @param {Roo.EventObject} e
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1635 preventDefault: true,
1637 getAutoCreate : function(){
1640 cls: 'dropdown-menu-item',
1650 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1655 initEvents: function() {
1657 //this.el.select('a').on('click', this.onClick, this);
1660 onClick : function(e)
1662 Roo.log('item on click ');
1663 //if(this.preventDefault){
1664 // e.preventDefault();
1666 //this.parent().hideMenuItems();
1668 this.fireEvent('click', this, e);
1687 * @class Roo.bootstrap.MenuSeparator
1688 * @extends Roo.bootstrap.Component
1689 * Bootstrap MenuSeparator class
1692 * Create a new MenuItem
1693 * @param {Object} config The config object
1697 Roo.bootstrap.MenuSeparator = function(config){
1698 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1703 getAutoCreate : function(){
1718 <div class="modal fade">
1719 <div class="modal-dialog">
1720 <div class="modal-content">
1721 <div class="modal-header">
1722 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1723 <h4 class="modal-title">Modal title</h4>
1725 <div class="modal-body">
1726 <p>One fine body…</p>
1728 <div class="modal-footer">
1729 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730 <button type="button" class="btn btn-primary">Save changes</button>
1732 </div><!-- /.modal-content -->
1733 </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1744 * @class Roo.bootstrap.Modal
1745 * @extends Roo.bootstrap.Component
1746 * Bootstrap Modal class
1747 * @cfg {String} title Title of dialog
1748 * @cfg {Array} buttons Array of buttons or standard button set..
1751 * Create a new Modal Dialog
1752 * @param {Object} config The config object
1755 Roo.bootstrap.Modal = function(config){
1756 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1761 * The raw btnclick event for the button
1762 * @param {Roo.EventObject} e
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1770 title : 'test dialog',
1774 onRender : function(ct, position)
1776 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1779 var cfg = Roo.apply({}, this.getAutoCreate());
1782 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1784 //if (!cfg.name.length) {
1788 cfg.cls += ' ' + this.cls;
1791 cfg.style = this.style;
1793 this.el = Roo.get(document.body).createChild(cfg, position);
1795 //var type = this.el.dom.type;
1797 if(this.tabIndex !== undefined){
1798 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1803 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804 this.maskEl.enableDisplayMode("block");
1806 //this.el.addClass("x-dlg-modal");
1809 Roo.each(this.buttons, function(bb) {
1810 b = Roo.apply({}, bb);
1811 b.xns = b.xns || Roo.bootstrap;
1812 b.xtype = b.xtype || 'Button';
1813 if (typeof(b.listeners) == 'undefined') {
1814 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1817 var btn = Roo.factory(b);
1819 btn.onRender(this.el.select('.modal-footer').first());
1823 // render the children.
1826 if(typeof(this.items) != 'undefined'){
1827 var items = this.items;
1830 for(var i =0;i < items.length;i++) {
1831 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1835 this.items = nitems;
1837 //this.el.addClass([this.fieldClass, this.cls]);
1840 getAutoCreate : function(){
1845 html : this.html || ''
1853 cls: "modal-dialog",
1856 cls : "modal-content",
1859 cls : 'modal-header',
1868 cls : 'modal-title',
1876 cls : 'modal-footer'
1892 getChildContainer : function() {
1894 return this.el.select('.modal-body',true).first();
1897 getButtonContainer : function() {
1898 return this.el.select('.modal-footer',true).first();
1901 initEvents : function()
1903 this.el.select('.modal-header .close').on('click', this.hide, this);
1905 // this.addxtype(this);
1909 if (!this.rendered) {
1913 this.el.addClass('on');
1914 this.el.removeClass('fade');
1915 this.el.setStyle('display', 'block');
1916 Roo.get(document.body).addClass("x-body-masked");
1917 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1919 this.el.setStyle('zIndex', '10001');
1920 this.fireEvent('show', this);
1926 Roo.log('Modal hide?!');
1928 Roo.get(document.body).removeClass("x-body-masked");
1929 this.el.removeClass('on');
1930 this.el.addClass('fade');
1931 this.el.setStyle('display', 'none');
1932 this.fireEvent('hide', this);
1934 onButtonClick: function(btn,e)
1937 this.fireEvent('btnclick', btn.name, e);
1942 Roo.apply(Roo.bootstrap.Modal, {
1944 * Button config that displays a single OK button
1953 * Button config that displays Yes and No buttons
1969 * Button config that displays OK and Cancel buttons
1984 * Button config that displays Yes, No and Cancel buttons
2011 * @class Roo.bootstrap.Navbar
2012 * @extends Roo.bootstrap.Component
2013 * Bootstrap Navbar class
2014 * @cfg {Boolean} sidebar has side bar
2015 * @cfg {Boolean} bar is a bar?
2016 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017 * @cfg {String} brand what is brand
2018 * @cfg {Boolean} inverse is inverted color
2019 * @cfg {String} type (nav | pills | tabs)
2020 * @cfg {Boolean} arrangement stacked | justified
2021 * @cfg {String} align (left | right) alignment
2022 * @cfg {String} brand_href href of the brand
2023 * @cfg {Boolean} main (true|false) main nav bar? default false
2024 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2028 * Create a new Navbar
2029 * @param {Object} config The config object
2033 Roo.bootstrap.Navbar = function(config){
2034 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2052 getAutoCreate : function(){
2057 if (this.sidebar === true) {
2065 if (this.bar === true) {
2073 cls: 'navbar-header',
2078 cls: 'navbar-toggle',
2079 'data-toggle': 'collapse',
2084 html: 'Toggle navigation'
2104 cls: 'collapse navbar-collapse'
2109 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2111 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112 cfg.cls += ' navbar-' + this.position;
2113 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2116 if (this.brand !== '') {
2119 href: this.brand_href ? this.brand_href : '#',
2120 cls: 'navbar-brand',
2128 cfg.cls += ' main-nav';
2134 } else if (this.bar === false) {
2137 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2147 if (['tabs','pills'].indexOf(this.type)!==-1) {
2148 cfg.cn[0].cls += ' nav-' + this.type
2150 if (this.type!=='nav') {
2151 Roo.log('nav type must be nav/tabs/pills')
2153 cfg.cn[0].cls += ' navbar-nav'
2156 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157 cfg.cn[0].cls += ' nav-' + this.arrangement;
2160 if (this.align === 'right') {
2161 cfg.cn[0].cls += ' navbar-right';
2164 cfg.cls += ' navbar-inverse';
2172 initEvents :function ()
2174 //Roo.log(this.el.select('.navbar-toggle',true));
2175 this.el.select('.navbar-toggle',true).on('click', function() {
2176 // Roo.log('click');
2177 this.el.select('.navbar-collapse',true).toggleClass('in');
2185 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2187 var size = this.el.getSize();
2188 this.maskEl.setSize(size.width, size.height);
2189 this.maskEl.enableDisplayMode("block");
2198 getChildContainer : function()
2200 if (this.bar === true) {
2201 return this.el.select('.collapse',true).first();
2229 * @class Roo.bootstrap.NavGroup
2230 * @extends Roo.bootstrap.Component
2231 * Bootstrap NavGroup class
2232 * @cfg {String} align left | right
2233 * @cfg {Boolean} inverse false | true
2234 * @cfg {String} type (nav|pills|tab) default nav
2237 * Create a new nav group
2238 * @param {Object} config The config object
2241 Roo.bootstrap.NavGroup = function(config){
2242 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2252 getAutoCreate : function(){
2253 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2260 if (['tabs','pills'].indexOf(this.type)!==-1) {
2261 cfg.cls += ' nav-' + this.type
2263 if (this.type!=='nav') {
2264 Roo.log('nav type must be nav/tabs/pills')
2266 cfg.cls += ' navbar-nav'
2269 if (this.parent().sidebar === true) {
2272 cls: 'dashboard-menu'
2278 if (this.form === true) {
2284 if (this.align === 'right') {
2285 cfg.cls += ' navbar-right';
2287 cfg.cls += ' navbar-left';
2291 if (this.align === 'right') {
2292 cfg.cls += ' navbar-right';
2296 cfg.cls += ' navbar-inverse';
2316 * @class Roo.bootstrap.Navbar.Item
2317 * @extends Roo.bootstrap.Component
2318 * Bootstrap Navbar.Button class
2319 * @cfg {String} href link to
2320 * @cfg {String} html content of button
2321 * @cfg {String} badge text inside badge
2322 * @cfg {String} glyphicon name of glyphicon
2323 * @cfg {String} icon name of font awesome icon
2324 * @cfg {Boolena} active Is item active
2325 * @cfg {Boolean} preventDefault (true | false) default false
2328 * Create a new Navbar Button
2329 * @param {Object} config The config object
2331 Roo.bootstrap.Navbar.Item = function(config){
2332 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2337 * The raw click event for the entire grid.
2338 * @param {Roo.EventObject} e
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
2353 preventDefault : false,
2355 getAutoCreate : function(){
2357 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2359 if (this.parent().parent().sidebar === true) {
2372 cfg.cn[0].html = this.html;
2376 this.cls += ' active';
2380 cfg.cn[0].cls += ' dropdown-toggle';
2381 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2385 cfg.cn[0].tag = 'a',
2386 cfg.cn[0].href = this.href;
2389 if (this.glyphicon) {
2390 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2394 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2406 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2416 if (this.glyphicon) {
2417 if(cfg.html){cfg.html = ' ' + this.html};
2421 cls: 'glyphicon glyphicon-' + this.glyphicon
2426 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2431 cfg.cn[0].html += " <span class='caret'></span>";
2432 //}else if (!this.href) {
2433 // cfg.cn[0].tag='p';
2434 // cfg.cn[0].cls='navbar-text';
2437 cfg.cn[0].href=this.href||'#';
2438 cfg.cn[0].html=this.html;
2441 if (this.badge !== '') {
2444 cfg.cn[0].html + ' ',
2455 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2460 initEvents: function() {
2461 // Roo.log('init events?');
2462 // Roo.log(this.el.dom);
2463 this.el.select('a',true).on('click', this.onClick, this);
2466 onClick : function(e)
2468 if(this.preventDefault){
2472 if(this.fireEvent('click', this, e) === false){
2476 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477 this.onTabsClick(e);
2481 onTabsClick : function(e)
2483 Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484 v.removeClass('active');
2487 this.el.addClass('active');
2489 if(this.href && this.href.substring(0,1) == '#'){
2490 var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2492 Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493 v.removeClass('active');
2496 tab.addClass('active');
2511 * @class Roo.bootstrap.Row
2512 * @extends Roo.bootstrap.Component
2513 * Bootstrap Row class (contains columns...)
2517 * @param {Object} config The config object
2520 Roo.bootstrap.Row = function(config){
2521 Roo.bootstrap.Row.superclass.constructor.call(this, config);
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
2526 getAutoCreate : function(){
2545 * @class Roo.bootstrap.Element
2546 * @extends Roo.bootstrap.Component
2547 * Bootstrap Element class
2548 * @cfg {String} html contents of the element
2549 * @cfg {String} tag tag of the element
2550 * @cfg {String} cls class of the element
2553 * Create a new Element
2554 * @param {Object} config The config object
2557 Roo.bootstrap.Element = function(config){
2558 Roo.bootstrap.Element.superclass.constructor.call(this, config);
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
2568 getAutoCreate : function(){
2593 * @class Roo.bootstrap.Pagination
2594 * @extends Roo.bootstrap.Component
2595 * Bootstrap Pagination class
2596 * @cfg {String} size xs | sm | md | lg
2597 * @cfg {Boolean} inverse false | true
2600 * Create a new Pagination
2601 * @param {Object} config The config object
2604 Roo.bootstrap.Pagination = function(config){
2605 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
2614 getAutoCreate : function(){
2620 cfg.cls += ' inverse';
2626 cfg.cls += " " + this.cls;
2644 * @class Roo.bootstrap.PaginationItem
2645 * @extends Roo.bootstrap.Component
2646 * Bootstrap PaginationItem class
2647 * @cfg {String} html text
2648 * @cfg {String} href the link
2649 * @cfg {Boolean} preventDefault (true | false) default true
2650 * @cfg {Boolean} active (true | false) default false
2654 * Create a new PaginationItem
2655 * @param {Object} config The config object
2659 Roo.bootstrap.PaginationItem = function(config){
2660 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2665 * The raw click event for the entire grid.
2666 * @param {Roo.EventObject} e
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
2676 preventDefault: true,
2680 getAutoCreate : function(){
2686 href : this.href ? this.href : '#',
2687 html : this.html ? this.html : ''
2697 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2703 initEvents: function() {
2705 this.el.on('click', this.onClick, this);
2708 onClick : function(e)
2710 Roo.log('PaginationItem on click ');
2711 if(this.preventDefault){
2715 this.fireEvent('click', this, e);
2731 * @class Roo.bootstrap.Slider
2732 * @extends Roo.bootstrap.Component
2733 * Bootstrap Slider class
2736 * Create a new Slider
2737 * @param {Object} config The config object
2740 Roo.bootstrap.Slider = function(config){
2741 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
2746 getAutoCreate : function(){
2750 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2754 cls: 'ui-slider-handle ui-state-default ui-corner-all'
2772 * @class Roo.bootstrap.Table
2773 * @extends Roo.bootstrap.Component
2774 * Bootstrap Table class
2775 * @cfg {String} cls table class
2776 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777 * @cfg {String} bgcolor Specifies the background color for a table
2778 * @cfg {Number} border Specifies whether the table cells should have borders or not
2779 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780 * @cfg {Number} cellspacing Specifies the space between cells
2781 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783 * @cfg {String} sortable Specifies that the table should be sortable
2784 * @cfg {String} summary Specifies a summary of the content of a table
2785 * @cfg {Number} width Specifies the width of a table
2787 * @cfg {boolean} striped Should the rows be alternative striped
2788 * @cfg {boolean} bordered Add borders to the table
2789 * @cfg {boolean} hover Add hover highlighting
2790 * @cfg {boolean} condensed Format condensed
2791 * @cfg {boolean} responsive Format condensed
2797 * Create a new Table
2798 * @param {Object} config The config object
2801 Roo.bootstrap.Table = function(config){
2802 Roo.bootstrap.Table.superclass.constructor.call(this, config);
2805 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806 this.sm = this.selModel;
2807 this.sm.xmodule = this.xmodule || false;
2809 if (this.cm && typeof(this.cm.config) == 'undefined') {
2810 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811 this.cm = this.colModel;
2812 this.cm.xmodule = this.xmodule || false;
2815 this.store= Roo.factory(this.store, Roo.data);
2816 this.ds = this.store;
2817 this.ds.xmodule = this.xmodule || false;
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
2844 getAutoCreate : function(){
2845 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2854 cfg.cls += ' table-striped';
2857 cfg.cls += ' table-hover';
2859 if (this.bordered) {
2860 cfg.cls += ' table-bordered';
2862 if (this.condensed) {
2863 cfg.cls += ' table-condensed';
2865 if (this.responsive) {
2866 cfg.cls += ' table-responsive';
2873 cfg.cls+= ' ' +this.cls;
2876 // this lot should be simplifed...
2879 cfg.align=this.align;
2882 cfg.bgcolor=this.bgcolor;
2885 cfg.border=this.border;
2887 if (this.cellpadding) {
2888 cfg.cellpadding=this.cellpadding;
2890 if (this.cellspacing) {
2891 cfg.cellspacing=this.cellspacing;
2894 cfg.frame=this.frame;
2897 cfg.rules=this.rules;
2899 if (this.sortable) {
2900 cfg.sortable=this.sortable;
2903 cfg.summary=this.summary;
2906 cfg.width=this.width;
2909 if(this.store || this.cm){
2910 cfg.cn.push(this.renderHeader());
2911 cfg.cn.push(this.renderBody());
2912 cfg.cn.push(this.renderFooter());
2914 cfg.cls+= ' TableGrid';
2920 // initTableGrid : function()
2929 // var cm = this.cm;
2931 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2934 // html: cm.getColumnHeader(i)
2938 // cfg.push(header);
2945 initEvents : function()
2947 if(!this.store || !this.cm){
2951 Roo.log('initEvents with ds!!!!');
2954 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2955 // this.maskEl.enableDisplayMode("block");
2956 // this.maskEl.show();
2958 this.store.on('load', this.onLoad, this);
2959 this.store.on('beforeload', this.onBeforeLoad, this);
2967 renderHeader : function()
2976 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2978 var config = cm.config[i];
2982 html: cm.getColumnHeader(i)
2985 if(typeof(config.dataIndex) != 'undefined'){
2986 c.sort = config.dataIndex;
2989 if(typeof(config.sortable) != 'undefined' && config.sortable){
2999 renderBody : function()
3009 renderFooter : function()
3021 Roo.log('ds onload');
3025 var tbody = this.el.select('tbody', true).first();
3029 if(this.store.getCount() > 0){
3030 this.store.data.each(function(d){
3036 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3037 var renderer = cm.getRenderer(i);
3038 var config = cm.config[i];
3042 if(typeof(renderer) !== 'undefined'){
3043 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3046 if(typeof(value) === 'object'){
3056 html: (typeof(value) === 'object') ? '' : value
3059 if(typeof(config.width) != 'undefined'){
3060 td.width = config.width;
3067 tbody.createChild(row);
3075 Roo.each(renders, function(r){
3076 _this.renderColumn(r);
3080 // if(this.loadMask){
3081 // this.maskEl.hide();
3085 onBeforeLoad : function()
3087 Roo.log('ds onBeforeLoad');
3091 // if(this.loadMask){
3092 // this.maskEl.show();
3098 this.el.select('tbody', true).first().dom.innerHTML = '';
3101 getSelectionModel : function(){
3103 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3105 return this.selModel;
3108 renderColumn : function(r)
3111 r.cfg.render(Roo.get(r.id));
3114 Roo.each(r.cfg.cn, function(c){
3119 _this.renderColumn(child);
3136 * @class Roo.bootstrap.TableCell
3137 * @extends Roo.bootstrap.Component
3138 * Bootstrap TableCell class
3139 * @cfg {String} html cell contain text
3140 * @cfg {String} cls cell class
3141 * @cfg {String} tag cell tag (td|th) default td
3142 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3143 * @cfg {String} align Aligns the content in a cell
3144 * @cfg {String} axis Categorizes cells
3145 * @cfg {String} bgcolor Specifies the background color of a cell
3146 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3147 * @cfg {Number} colspan Specifies the number of columns a cell should span
3148 * @cfg {String} headers Specifies one or more header cells a cell is related to
3149 * @cfg {Number} height Sets the height of a cell
3150 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3151 * @cfg {Number} rowspan Sets the number of rows a cell should span
3152 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3153 * @cfg {String} valign Vertical aligns the content in a cell
3154 * @cfg {Number} width Specifies the width of a cell
3157 * Create a new TableCell
3158 * @param {Object} config The config object
3161 Roo.bootstrap.TableCell = function(config){
3162 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3165 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3185 getAutoCreate : function(){
3186 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3206 cfg.align=this.align
3212 cfg.bgcolor=this.bgcolor
3215 cfg.charoff=this.charoff
3218 cfg.colspan=this.colspan
3221 cfg.headers=this.headers
3224 cfg.height=this.height
3227 cfg.nowrap=this.nowrap
3230 cfg.rowspan=this.rowspan
3233 cfg.scope=this.scope
3236 cfg.valign=this.valign
3239 cfg.width=this.width
3258 * @class Roo.bootstrap.TableRow
3259 * @extends Roo.bootstrap.Component
3260 * Bootstrap TableRow class
3261 * @cfg {String} cls row class
3262 * @cfg {String} align Aligns the content in a table row
3263 * @cfg {String} bgcolor Specifies a background color for a table row
3264 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3265 * @cfg {String} valign Vertical aligns the content in a table row
3268 * Create a new TableRow
3269 * @param {Object} config The config object
3272 Roo.bootstrap.TableRow = function(config){
3273 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3276 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3284 getAutoCreate : function(){
3285 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3295 cfg.align = this.align;
3298 cfg.bgcolor = this.bgcolor;
3301 cfg.charoff = this.charoff;
3304 cfg.valign = this.valign;
3322 * @class Roo.bootstrap.TableBody
3323 * @extends Roo.bootstrap.Component
3324 * Bootstrap TableBody class
3325 * @cfg {String} cls element class
3326 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3327 * @cfg {String} align Aligns the content inside the element
3328 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3329 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3332 * Create a new TableBody
3333 * @param {Object} config The config object
3336 Roo.bootstrap.TableBody = function(config){
3337 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3340 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3348 getAutoCreate : function(){
3349 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3363 cfg.align = this.align;
3366 cfg.charoff = this.charoff;
3369 cfg.valign = this.valign;
3376 // initEvents : function()
3383 // this.store = Roo.factory(this.store, Roo.data);
3384 // this.store.on('load', this.onLoad, this);
3386 // this.store.load();
3390 // onLoad: function ()
3392 // this.fireEvent('load', this);
3402 * Ext JS Library 1.1.1
3403 * Copyright(c) 2006-2007, Ext JS, LLC.
3405 * Originally Released Under LGPL - original licence link has changed is not relivant.
3408 * <script type="text/javascript">
3411 // as we use this in bootstrap.
3412 Roo.namespace('Roo.form');
3414 * @class Roo.form.Action
3415 * Internal Class used to handle form actions
3417 * @param {Roo.form.BasicForm} el The form element or its id
3418 * @param {Object} config Configuration options
3423 // define the action interface
3424 Roo.form.Action = function(form, options){
3426 this.options = options || {};
3429 * Client Validation Failed
3432 Roo.form.Action.CLIENT_INVALID = 'client';
3434 * Server Validation Failed
3437 Roo.form.Action.SERVER_INVALID = 'server';
3439 * Connect to Server Failed
3442 Roo.form.Action.CONNECT_FAILURE = 'connect';
3444 * Reading Data from Server Failed
3447 Roo.form.Action.LOAD_FAILURE = 'load';
3449 Roo.form.Action.prototype = {
3451 failureType : undefined,
3452 response : undefined,
3456 run : function(options){
3461 success : function(response){
3466 handleResponse : function(response){
3470 // default connection failure
3471 failure : function(response){
3473 this.response = response;
3474 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3475 this.form.afterAction(this, false);
3478 processResponse : function(response){
3479 this.response = response;
3480 if(!response.responseText){
3483 this.result = this.handleResponse(response);
3487 // utility functions used internally
3488 getUrl : function(appendParams){
3489 var url = this.options.url || this.form.url || this.form.el.dom.action;
3491 var p = this.getParams();
3493 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3499 getMethod : function(){
3500 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3503 getParams : function(){
3504 var bp = this.form.baseParams;
3505 var p = this.options.params;
3507 if(typeof p == "object"){
3508 p = Roo.urlEncode(Roo.applyIf(p, bp));
3509 }else if(typeof p == 'string' && bp){
3510 p += '&' + Roo.urlEncode(bp);
3513 p = Roo.urlEncode(bp);
3518 createCallback : function(){
3520 success: this.success,
3521 failure: this.failure,
3523 timeout: (this.form.timeout*1000),
3524 upload: this.form.fileUpload ? this.success : undefined
3529 Roo.form.Action.Submit = function(form, options){
3530 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3533 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3536 haveProgress : false,
3537 uploadComplete : false,
3539 // uploadProgress indicator.
3540 uploadProgress : function()
3542 if (!this.form.progressUrl) {
3546 if (!this.haveProgress) {
3547 Roo.MessageBox.progress("Uploading", "Uploading");
3549 if (this.uploadComplete) {
3550 Roo.MessageBox.hide();
3554 this.haveProgress = true;
3556 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3558 var c = new Roo.data.Connection();
3560 url : this.form.progressUrl,
3565 success : function(req){
3566 //console.log(data);
3570 rdata = Roo.decode(req.responseText)
3572 Roo.log("Invalid data from server..");
3576 if (!rdata || !rdata.success) {
3578 Roo.MessageBox.alert(Roo.encode(rdata));
3581 var data = rdata.data;
3583 if (this.uploadComplete) {
3584 Roo.MessageBox.hide();
3589 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3590 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3593 this.uploadProgress.defer(2000,this);
3596 failure: function(data) {
3597 Roo.log('progress url failed ');
3608 // run get Values on the form, so it syncs any secondary forms.
3609 this.form.getValues();
3611 var o = this.options;
3612 var method = this.getMethod();
3613 var isPost = method == 'POST';
3614 if(o.clientValidation === false || this.form.isValid()){
3616 if (this.form.progressUrl) {
3617 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3618 (new Date() * 1) + '' + Math.random());
3623 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3624 form:this.form.el.dom,
3625 url:this.getUrl(!isPost),
3627 params:isPost ? this.getParams() : null,
3628 isUpload: this.form.fileUpload
3631 this.uploadProgress();
3633 }else if (o.clientValidation !== false){ // client validation failed
3634 this.failureType = Roo.form.Action.CLIENT_INVALID;
3635 this.form.afterAction(this, false);
3639 success : function(response)
3641 this.uploadComplete= true;
3642 if (this.haveProgress) {
3643 Roo.MessageBox.hide();
3647 var result = this.processResponse(response);
3648 if(result === true || result.success){
3649 this.form.afterAction(this, true);
3653 this.form.markInvalid(result.errors);
3654 this.failureType = Roo.form.Action.SERVER_INVALID;
3656 this.form.afterAction(this, false);
3658 failure : function(response)
3660 this.uploadComplete= true;
3661 if (this.haveProgress) {
3662 Roo.MessageBox.hide();
3665 this.response = response;
3666 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3667 this.form.afterAction(this, false);
3670 handleResponse : function(response){
3671 if(this.form.errorReader){
3672 var rs = this.form.errorReader.read(response);
3675 for(var i = 0, len = rs.records.length; i < len; i++) {
3676 var r = rs.records[i];
3680 if(errors.length < 1){
3684 success : rs.success,
3690 ret = Roo.decode(response.responseText);
3694 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3704 Roo.form.Action.Load = function(form, options){
3705 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3706 this.reader = this.form.reader;
3709 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3714 Roo.Ajax.request(Roo.apply(
3715 this.createCallback(), {
3716 method:this.getMethod(),
3717 url:this.getUrl(false),
3718 params:this.getParams()
3722 success : function(response){
3724 var result = this.processResponse(response);
3725 if(result === true || !result.success || !result.data){
3726 this.failureType = Roo.form.Action.LOAD_FAILURE;
3727 this.form.afterAction(this, false);
3730 this.form.clearInvalid();
3731 this.form.setValues(result.data);
3732 this.form.afterAction(this, true);
3735 handleResponse : function(response){
3736 if(this.form.reader){
3737 var rs = this.form.reader.read(response);
3738 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3740 success : rs.success,
3744 return Roo.decode(response.responseText);
3748 Roo.form.Action.ACTION_TYPES = {
3749 'load' : Roo.form.Action.Load,
3750 'submit' : Roo.form.Action.Submit
3759 * @class Roo.bootstrap.Form
3760 * @extends Roo.bootstrap.Component
3761 * Bootstrap Form class
3762 * @cfg {String} method GET | POST (default POST)
3763 * @cfg {String} labelAlign top | left (default top)
3764 * @cfg {String} align left | right - for navbars
3769 * @param {Object} config The config object
3773 Roo.bootstrap.Form = function(config){
3774 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3777 * @event clientvalidation
3778 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3779 * @param {Form} this
3780 * @param {Boolean} valid true if the form has passed client-side validation
3782 clientvalidation: true,
3784 * @event beforeaction
3785 * Fires before any action is performed. Return false to cancel the action.
3786 * @param {Form} this
3787 * @param {Action} action The action to be performed
3791 * @event actionfailed
3792 * Fires when an action fails.
3793 * @param {Form} this
3794 * @param {Action} action The action that failed
3796 actionfailed : true,
3798 * @event actioncomplete
3799 * Fires when an action is completed.
3800 * @param {Form} this
3801 * @param {Action} action The action that completed
3803 actioncomplete : true
3808 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3811 * @cfg {String} method
3812 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3817 * The URL to use for form actions if one isn't supplied in the action options.
3820 * @cfg {Boolean} fileUpload
3821 * Set to true if this form is a file upload.
3825 * @cfg {Object} baseParams
3826 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3830 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3834 * @cfg {Sting} align (left|right) for navbar forms
3839 activeAction : null,
3842 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3843 * element by passing it or its id or mask the form itself by passing in true.
3846 waitMsgTarget : false,
3851 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3852 * element by passing it or its id or mask the form itself by passing in true.
3856 getAutoCreate : function(){
3860 method : this.method || 'POST',
3861 id : this.id || Roo.id(),
3864 if (this.parent().xtype.match(/^Nav/)) {
3865 cfg.cls = 'navbar-form navbar-' + this.align;
3869 if (this.labelAlign == 'left' ) {
3870 cfg.cls += ' form-horizontal';
3876 initEvents : function()
3878 this.el.on('submit', this.onSubmit, this);
3883 onSubmit : function(e){
3888 * Returns true if client-side validation on the form is successful.
3891 isValid : function(){
3892 var items = this.getItems();
3894 items.each(function(f){
3903 * Returns true if any fields in this form have changed since their original load.
3906 isDirty : function(){
3908 var items = this.getItems();
3909 items.each(function(f){
3919 * Performs a predefined action (submit or load) or custom actions you define on this form.
3920 * @param {String} actionName The name of the action type
3921 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3922 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3923 * accept other config options):
3925 Property Type Description
3926 ---------------- --------------- ----------------------------------------------------------------------------------
3927 url String The url for the action (defaults to the form's url)
3928 method String The form method to use (defaults to the form's method, or POST if not defined)
3929 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3930 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3931 validate the form on the client (defaults to false)
3933 * @return {BasicForm} this
3935 doAction : function(action, options){
3936 if(typeof action == 'string'){
3937 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3939 if(this.fireEvent('beforeaction', this, action) !== false){
3940 this.beforeAction(action);
3941 action.run.defer(100, action);
3947 beforeAction : function(action){
3948 var o = action.options;
3950 // not really supported yet.. ??
3952 //if(this.waitMsgTarget === true){
3953 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3954 //}else if(this.waitMsgTarget){
3955 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3956 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3958 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3964 afterAction : function(action, success){
3965 this.activeAction = null;
3966 var o = action.options;
3968 //if(this.waitMsgTarget === true){
3970 //}else if(this.waitMsgTarget){
3971 // this.waitMsgTarget.unmask();
3973 // Roo.MessageBox.updateProgress(1);
3974 // Roo.MessageBox.hide();
3981 Roo.callback(o.success, o.scope, [this, action]);
3982 this.fireEvent('actioncomplete', this, action);
3986 // failure condition..
3987 // we have a scenario where updates need confirming.
3988 // eg. if a locking scenario exists..
3989 // we look for { errors : { needs_confirm : true }} in the response.
3991 (typeof(action.result) != 'undefined') &&
3992 (typeof(action.result.errors) != 'undefined') &&
3993 (typeof(action.result.errors.needs_confirm) != 'undefined')
3996 Roo.log("not supported yet");
3999 Roo.MessageBox.confirm(
4000 "Change requires confirmation",
4001 action.result.errorMsg,
4006 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4016 Roo.callback(o.failure, o.scope, [this, action]);
4017 // show an error message if no failed handler is set..
4018 if (!this.hasListener('actionfailed')) {
4019 Roo.log("need to add dialog support");
4021 Roo.MessageBox.alert("Error",
4022 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4023 action.result.errorMsg :
4024 "Saving Failed, please check your entries or try again"
4029 this.fireEvent('actionfailed', this, action);
4034 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4035 * @param {String} id The value to search for
4038 findField : function(id){
4039 var items = this.getItems();
4040 var field = items.get(id);
4042 items.each(function(f){
4043 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4050 return field || null;
4053 * Mark fields in this form invalid in bulk.
4054 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4055 * @return {BasicForm} this
4057 markInvalid : function(errors){
4058 if(errors instanceof Array){
4059 for(var i = 0, len = errors.length; i < len; i++){
4060 var fieldError = errors[i];
4061 var f = this.findField(fieldError.id);
4063 f.markInvalid(fieldError.msg);
4069 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4070 field.markInvalid(errors[id]);
4074 //Roo.each(this.childForms || [], function (f) {
4075 // f.markInvalid(errors);
4082 * Set values for fields in this form in bulk.
4083 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4084 * @return {BasicForm} this
4086 setValues : function(values){
4087 if(values instanceof Array){ // array of objects
4088 for(var i = 0, len = values.length; i < len; i++){
4090 var f = this.findField(v.id);
4092 f.setValue(v.value);
4093 if(this.trackResetOnLoad){
4094 f.originalValue = f.getValue();
4098 }else{ // object hash
4101 if(typeof values[id] != 'function' && (field = this.findField(id))){
4103 if (field.setFromData &&
4105 field.displayField &&
4106 // combos' with local stores can
4107 // be queried via setValue()
4108 // to set their value..
4109 (field.store && !field.store.isLocal)
4113 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4114 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4115 field.setFromData(sd);
4118 field.setValue(values[id]);
4122 if(this.trackResetOnLoad){
4123 field.originalValue = field.getValue();
4129 //Roo.each(this.childForms || [], function (f) {
4130 // f.setValues(values);
4137 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4138 * they are returned as an array.
4139 * @param {Boolean} asString
4142 getValues : function(asString){
4143 //if (this.childForms) {
4144 // copy values from the child forms
4145 // Roo.each(this.childForms, function (f) {
4146 // this.setValues(f.getValues());
4152 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4153 if(asString === true){
4156 return Roo.urlDecode(fs);
4160 * Returns the fields in this form as an object with key/value pairs.
4161 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4164 getFieldValues : function(with_hidden)
4166 var items = this.getItems();
4168 items.each(function(f){
4172 var v = f.getValue();
4173 if (f.inputType =='radio') {
4174 if (typeof(ret[f.getName()]) == 'undefined') {
4175 ret[f.getName()] = ''; // empty..
4178 if (!f.el.dom.checked) {
4186 // not sure if this supported any more..
4187 if ((typeof(v) == 'object') && f.getRawValue) {
4188 v = f.getRawValue() ; // dates..
4190 // combo boxes where name != hiddenName...
4191 if (f.name != f.getName()) {
4192 ret[f.name] = f.getRawValue();
4194 ret[f.getName()] = v;
4201 * Clears all invalid messages in this form.
4202 * @return {BasicForm} this
4204 clearInvalid : function(){
4205 var items = this.getItems();
4207 items.each(function(f){
4218 * @return {BasicForm} this
4221 var items = this.getItems();
4222 items.each(function(f){
4226 Roo.each(this.childForms || [], function (f) {
4233 getItems : function()
4235 var r=new Roo.util.MixedCollection(false, function(o){
4236 return o.id || (o.id = Roo.id());
4238 var iter = function(el) {
4245 Roo.each(el.items,function(e) {
4264 * Ext JS Library 1.1.1
4265 * Copyright(c) 2006-2007, Ext JS, LLC.
4267 * Originally Released Under LGPL - original licence link has changed is not relivant.
4270 * <script type="text/javascript">
4273 * @class Roo.form.VTypes
4274 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4277 Roo.form.VTypes = function(){
4278 // closure these in so they are only created once.
4279 var alpha = /^[a-zA-Z_]+$/;
4280 var alphanum = /^[a-zA-Z0-9_]+$/;
4281 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4282 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4284 // All these messages and functions are configurable
4287 * The function used to validate email addresses
4288 * @param {String} value The email address
4290 'email' : function(v){
4291 return email.test(v);
4294 * The error text to display when the email validation function returns false
4297 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4299 * The keystroke filter mask to be applied on email input
4302 'emailMask' : /[a-z0-9_\.\-@]/i,
4305 * The function used to validate URLs
4306 * @param {String} value The URL
4308 'url' : function(v){
4312 * The error text to display when the url validation function returns false
4315 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4318 * The function used to validate alpha values
4319 * @param {String} value The value
4321 'alpha' : function(v){
4322 return alpha.test(v);
4325 * The error text to display when the alpha validation function returns false
4328 'alphaText' : 'This field should only contain letters and _',
4330 * The keystroke filter mask to be applied on alpha input
4333 'alphaMask' : /[a-z_]/i,
4336 * The function used to validate alphanumeric values
4337 * @param {String} value The value
4339 'alphanum' : function(v){
4340 return alphanum.test(v);
4343 * The error text to display when the alphanumeric validation function returns false
4346 'alphanumText' : 'This field should only contain letters, numbers and _',
4348 * The keystroke filter mask to be applied on alphanumeric input
4351 'alphanumMask' : /[a-z0-9_]/i
4361 * @class Roo.bootstrap.Input
4362 * @extends Roo.bootstrap.Component
4363 * Bootstrap Input class
4364 * @cfg {Boolean} disabled is it disabled
4365 * @cfg {String} fieldLabel - the label associated
4366 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4367 * @cfg {String} name name of the input
4368 * @cfg {string} fieldLabel - the label associated
4369 * @cfg {string} inputType - input / file submit ...
4370 * @cfg {string} placeholder - placeholder to put in text.
4371 * @cfg {string} before - input group add on before
4372 * @cfg {string} after - input group add on after
4373 * @cfg {string} size - (lg|sm) or leave empty..
4374 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4375 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4376 * @cfg {Number} md colspan out of 12 for computer-sized screens
4377 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4378 * @cfg {string} value default value of the input
4379 * @cfg {Number} labelWidth set the width of label (0-12)
4380 * @cfg {String} labelAlign (top|left)
4381 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4385 * Create a new Input
4386 * @param {Object} config The config object
4389 Roo.bootstrap.Input = function(config){
4390 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4395 * Fires when this field receives input focus.
4396 * @param {Roo.form.Field} this
4401 * Fires when this field loses input focus.
4402 * @param {Roo.form.Field} this
4407 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4408 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4409 * @param {Roo.form.Field} this
4410 * @param {Roo.EventObject} e The event object
4415 * Fires just before the field blurs if the field value has changed.
4416 * @param {Roo.form.Field} this
4417 * @param {Mixed} newValue The new value
4418 * @param {Mixed} oldValue The original value
4423 * Fires after the field has been marked as invalid.
4424 * @param {Roo.form.Field} this
4425 * @param {String} msg The validation message
4430 * Fires after the field has been validated with no errors.
4431 * @param {Roo.form.Field} this
4436 * Fires after the key up
4437 * @param {Roo.form.Field} this
4438 * @param {Roo.EventObject} e The event Object
4444 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4446 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4447 automatic validation (defaults to "keyup").
4449 validationEvent : "keyup",
4451 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4453 validateOnBlur : true,
4455 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4457 validationDelay : 250,
4459 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4461 focusClass : "x-form-focus", // not needed???
4465 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4467 invalidClass : "has-error",
4470 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4472 selectOnFocus : false,
4475 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4479 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4484 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4486 disableKeyFilter : false,
4489 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4493 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4497 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4499 blankText : "This field is required",
4502 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4506 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4508 maxLength : Number.MAX_VALUE,
4510 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4512 minLengthText : "The minimum length for this field is {0}",
4514 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4516 maxLengthText : "The maximum length for this field is {0}",
4520 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4521 * If available, this function will be called only after the basic validators all return true, and will be passed the
4522 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4526 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4527 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4528 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4532 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4555 parentLabelAlign : function()
4558 while (parent.parent()) {
4559 parent = parent.parent();
4560 if (typeof(parent.labelAlign) !='undefined') {
4561 return parent.labelAlign;
4568 getAutoCreate : function(){
4570 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4576 if(this.inputType != 'hidden'){
4577 cfg.cls = 'form-group' //input-group
4583 type : this.inputType,
4585 cls : 'form-control',
4586 placeholder : this.placeholder || ''
4590 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4591 input.maxLength = this.maxLength;
4594 if (this.disabled) {
4595 input.disabled=true;
4598 if (this.readOnly) {
4599 input.readonly=true;
4603 input.name = this.name;
4606 input.cls += ' input-' + this.size;
4609 ['xs','sm','md','lg'].map(function(size){
4610 if (settings[size]) {
4611 cfg.cls += ' col-' + size + '-' + settings[size];
4615 var inputblock = input;
4617 if (this.before || this.after) {
4620 cls : 'input-group',
4624 inputblock.cn.push({
4626 cls : 'input-group-addon',
4630 inputblock.cn.push(input);
4632 inputblock.cn.push({
4634 cls : 'input-group-addon',
4641 if (align ==='left' && this.fieldLabel.length) {
4642 Roo.log("left and has label");
4648 cls : 'control-label col-sm-' + this.labelWidth,
4649 html : this.fieldLabel
4653 cls : "col-sm-" + (12 - this.labelWidth),
4660 } else if ( this.fieldLabel.length) {
4666 //cls : 'input-group-addon',
4667 html : this.fieldLabel
4677 Roo.log(" no label && no align");
4686 Roo.log('input-parentType: ' + this.parentType);
4688 if (this.parentType === 'Navbar' && this.parent().bar) {
4689 cfg.cls += ' navbar-form';
4697 * return the real input element.
4699 inputEl: function ()
4701 return this.el.select('input.form-control',true).first();
4703 setDisabled : function(v)
4705 var i = this.inputEl().dom;
4707 i.removeAttribute('disabled');
4711 i.setAttribute('disabled','true');
4713 initEvents : function()
4716 this.inputEl().on("keydown" , this.fireKey, this);
4717 this.inputEl().on("focus", this.onFocus, this);
4718 this.inputEl().on("blur", this.onBlur, this);
4720 this.inputEl().relayEvent('keyup', this);
4722 // reference to original value for reset
4723 this.originalValue = this.getValue();
4724 //Roo.form.TextField.superclass.initEvents.call(this);
4725 if(this.validationEvent == 'keyup'){
4726 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4727 this.inputEl().on('keyup', this.filterValidation, this);
4729 else if(this.validationEvent !== false){
4730 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4733 if(this.selectOnFocus){
4734 this.on("focus", this.preFocus, this);
4737 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4738 this.inputEl().on("keypress", this.filterKeys, this);
4741 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4742 this.el.on("click", this.autoSize, this);
4745 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4746 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4750 filterValidation : function(e){
4751 if(!e.isNavKeyPress()){
4752 this.validationTask.delay(this.validationDelay);
4756 * Validates the field value
4757 * @return {Boolean} True if the value is valid, else false
4759 validate : function(){
4760 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4761 if(this.disabled || this.validateValue(this.getRawValue())){
4762 this.clearInvalid();
4770 * Validates a value according to the field's validation rules and marks the field as invalid
4771 * if the validation fails
4772 * @param {Mixed} value The value to validate
4773 * @return {Boolean} True if the value is valid, else false
4775 validateValue : function(value){
4776 if(value.length < 1) { // if it's blank
4777 if(this.allowBlank){
4778 this.clearInvalid();
4781 this.markInvalid(this.blankText);
4785 if(value.length < this.minLength){
4786 this.markInvalid(String.format(this.minLengthText, this.minLength));
4789 if(value.length > this.maxLength){
4790 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4794 var vt = Roo.form.VTypes;
4795 if(!vt[this.vtype](value, this)){
4796 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4800 if(typeof this.validator == "function"){
4801 var msg = this.validator(value);
4803 this.markInvalid(msg);
4807 if(this.regex && !this.regex.test(value)){
4808 this.markInvalid(this.regexText);
4817 fireKey : function(e){
4818 //Roo.log('field ' + e.getKey());
4819 if(e.isNavKeyPress()){
4820 this.fireEvent("specialkey", this, e);
4823 focus : function (selectText){
4825 this.inputEl().focus();
4826 if(selectText === true){
4827 this.inputEl().dom.select();
4833 onFocus : function(){
4834 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4835 // this.el.addClass(this.focusClass);
4838 this.hasFocus = true;
4839 this.startValue = this.getValue();
4840 this.fireEvent("focus", this);
4844 beforeBlur : Roo.emptyFn,
4848 onBlur : function(){
4850 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4851 //this.el.removeClass(this.focusClass);
4853 this.hasFocus = false;
4854 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4857 var v = this.getValue();
4858 if(String(v) !== String(this.startValue)){
4859 this.fireEvent('change', this, v, this.startValue);
4861 this.fireEvent("blur", this);
4865 * Resets the current field value to the originally loaded value and clears any validation messages
4868 this.setValue(this.originalValue);
4869 this.clearInvalid();
4872 * Returns the name of the field
4873 * @return {Mixed} name The name field
4875 getName: function(){
4879 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4880 * @return {Mixed} value The field value
4882 getValue : function(){
4883 return this.inputEl().getValue();
4886 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4887 * @return {Mixed} value The field value
4889 getRawValue : function(){
4890 var v = this.inputEl().getValue();
4896 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4897 * @param {Mixed} value The value to set
4899 setRawValue : function(v){
4900 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4903 selectText : function(start, end){
4904 var v = this.getRawValue();
4906 start = start === undefined ? 0 : start;
4907 end = end === undefined ? v.length : end;
4908 var d = this.inputEl().dom;
4909 if(d.setSelectionRange){
4910 d.setSelectionRange(start, end);
4911 }else if(d.createTextRange){
4912 var range = d.createTextRange();
4913 range.moveStart("character", start);
4914 range.moveEnd("character", v.length-end);
4921 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4922 * @param {Mixed} value The value to set
4924 setValue : function(v){
4927 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4933 processValue : function(value){
4934 if(this.stripCharsRe){
4935 var newValue = value.replace(this.stripCharsRe, '');
4936 if(newValue !== value){
4937 this.setRawValue(newValue);
4944 preFocus : function(){
4946 if(this.selectOnFocus){
4947 this.inputEl().dom.select();
4950 filterKeys : function(e){
4952 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4955 var c = e.getCharCode(), cc = String.fromCharCode(c);
4956 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4959 if(!this.maskRe.test(cc)){
4964 * Clear any invalid styles/messages for this field
4966 clearInvalid : function(){
4968 if(!this.el || this.preventMark){ // not rendered
4971 this.el.removeClass(this.invalidClass);
4973 switch(this.msgTarget){
4975 this.el.dom.qtip = '';
4978 this.el.dom.title = '';
4982 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4987 this.errorIcon.dom.qtip = '';
4988 this.errorIcon.hide();
4989 this.un('resize', this.alignErrorIcon, this);
4993 var t = Roo.getDom(this.msgTarget);
4995 t.style.display = 'none';
4999 this.fireEvent('valid', this);
5002 * Mark this field as invalid
5003 * @param {String} msg The validation message
5005 markInvalid : function(msg){
5006 if(!this.el || this.preventMark){ // not rendered
5009 this.el.addClass(this.invalidClass);
5011 msg = msg || this.invalidText;
5012 switch(this.msgTarget){
5014 this.el.dom.qtip = msg;
5015 this.el.dom.qclass = 'x-form-invalid-tip';
5016 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5017 Roo.QuickTips.enable();
5021 this.el.dom.title = msg;
5025 var elp = this.el.findParent('.x-form-element', 5, true);
5026 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5027 this.errorEl.setWidth(elp.getWidth(true)-20);
5029 this.errorEl.update(msg);
5030 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5033 if(!this.errorIcon){
5034 var elp = this.el.findParent('.x-form-element', 5, true);
5035 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5037 this.alignErrorIcon();
5038 this.errorIcon.dom.qtip = msg;
5039 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5040 this.errorIcon.show();
5041 this.on('resize', this.alignErrorIcon, this);
5044 var t = Roo.getDom(this.msgTarget);
5046 t.style.display = this.msgDisplay;
5050 this.fireEvent('invalid', this, msg);
5053 SafariOnKeyDown : function(event)
5055 // this is a workaround for a password hang bug on chrome/ webkit.
5057 var isSelectAll = false;
5059 if(this.inputEl().dom.selectionEnd > 0){
5060 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5062 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5063 event.preventDefault();
5068 if(isSelectAll){ // backspace and delete key
5070 event.preventDefault();
5071 // this is very hacky as keydown always get's upper case.
5073 var cc = String.fromCharCode(event.getCharCode());
5074 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5078 adjustWidth : function(tag, w){
5079 tag = tag.toLowerCase();
5080 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5081 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5085 if(tag == 'textarea'){
5088 }else if(Roo.isOpera){
5092 if(tag == 'textarea'){
5111 * @class Roo.bootstrap.TextArea
5112 * @extends Roo.bootstrap.Input
5113 * Bootstrap TextArea class
5114 * @cfg {Number} cols Specifies the visible width of a text area
5115 * @cfg {Number} rows Specifies the visible number of lines in a text area
5116 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5117 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5118 * @cfg {string} html text
5121 * Create a new TextArea
5122 * @param {Object} config The config object
5125 Roo.bootstrap.TextArea = function(config){
5126 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5130 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5140 getAutoCreate : function(){
5142 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5153 value : this.value || '',
5154 html: this.html || '',
5155 cls : 'form-control',
5156 placeholder : this.placeholder || ''
5160 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5161 input.maxLength = this.maxLength;
5165 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5169 input.cols = this.cols;
5172 if (this.readOnly) {
5173 input.readonly = true;
5177 input.name = this.name;
5181 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5185 ['xs','sm','md','lg'].map(function(size){
5186 if (settings[size]) {
5187 cfg.cls += ' col-' + size + '-' + settings[size];
5191 var inputblock = input;
5193 if (this.before || this.after) {
5196 cls : 'input-group',
5200 inputblock.cn.push({
5202 cls : 'input-group-addon',
5206 inputblock.cn.push(input);
5208 inputblock.cn.push({
5210 cls : 'input-group-addon',
5217 if (align ==='left' && this.fieldLabel.length) {
5218 Roo.log("left and has label");
5224 cls : 'control-label col-sm-' + this.labelWidth,
5225 html : this.fieldLabel
5229 cls : "col-sm-" + (12 - this.labelWidth),
5236 } else if ( this.fieldLabel.length) {
5242 //cls : 'input-group-addon',
5243 html : this.fieldLabel
5253 Roo.log(" no label && no align");
5263 if (this.disabled) {
5264 input.disabled=true;
5271 * return the real textarea element.
5273 inputEl: function ()
5275 return this.el.select('textarea.form-control',true).first();
5283 * trigger field - base class for combo..
5288 * @class Roo.bootstrap.TriggerField
5289 * @extends Roo.bootstrap.Input
5290 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5291 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5292 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5293 * for which you can provide a custom implementation. For example:
5295 var trigger = new Roo.bootstrap.TriggerField();
5296 trigger.onTriggerClick = myTriggerFn;
5297 trigger.applyTo('my-field');
5300 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5301 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5302 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5303 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5305 * Create a new TriggerField.
5306 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5307 * to the base TextField)
5309 Roo.bootstrap.TriggerField = function(config){
5310 this.mimicing = false;
5311 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5314 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5316 * @cfg {String} triggerClass A CSS class to apply to the trigger
5319 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5323 /** @cfg {Boolean} grow @hide */
5324 /** @cfg {Number} growMin @hide */
5325 /** @cfg {Number} growMax @hide */
5331 autoSize: Roo.emptyFn,
5338 actionMode : 'wrap',
5342 getAutoCreate : function(){
5344 var parent = this.parent();
5346 var align = this.parentLabelAlign();
5351 cls: 'form-group' //input-group
5358 type : this.inputType,
5359 cls : 'form-control',
5360 autocomplete: 'off',
5361 placeholder : this.placeholder || ''
5365 input.name = this.name;
5368 input.cls += ' input-' + this.size;
5371 if (this.disabled) {
5372 input.disabled=true;
5375 var inputblock = input;
5377 if (this.before || this.after) {
5380 cls : 'input-group',
5384 inputblock.cn.push({
5386 cls : 'input-group-addon',
5390 inputblock.cn.push(input);
5392 inputblock.cn.push({
5394 cls : 'input-group-addon',
5407 cls: 'form-hidden-field'
5415 Roo.log('multiple');
5423 cls: 'form-hidden-field'
5427 cls: 'select2-choices',
5431 cls: 'select2-search-field',
5444 cls: 'select2-container input-group',
5449 cls: 'typeahead typeahead-long dropdown-menu',
5450 style: 'display:none'
5458 cls : 'input-group-addon btn dropdown-toggle',
5466 cls: 'combobox-clear',
5480 combobox.cls += ' select2-container-multi';
5483 if (align ==='left' && this.fieldLabel.length) {
5485 Roo.log("left and has label");
5491 cls : 'control-label col-sm-' + this.labelWidth,
5492 html : this.fieldLabel
5496 cls : "col-sm-" + (12 - this.labelWidth),
5503 } else if ( this.fieldLabel.length) {
5509 //cls : 'input-group-addon',
5510 html : this.fieldLabel
5520 Roo.log(" no label && no align");
5527 ['xs','sm','md','lg'].map(function(size){
5528 if (settings[size]) {
5529 cfg.cls += ' col-' + size + '-' + settings[size];
5540 onResize : function(w, h){
5541 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5542 // if(typeof w == 'number'){
5543 // var x = w - this.trigger.getWidth();
5544 // this.inputEl().setWidth(this.adjustWidth('input', x));
5545 // this.trigger.setStyle('left', x+'px');
5550 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5553 getResizeEl : function(){
5554 return this.inputEl();
5558 getPositionEl : function(){
5559 return this.inputEl();
5563 alignErrorIcon : function(){
5564 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5568 initEvents : function(){
5570 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5571 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5573 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5574 if(this.hideTrigger){
5575 this.trigger.setDisplayed(false);
5577 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5581 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5584 //this.trigger.addClassOnOver('x-form-trigger-over');
5585 //this.trigger.addClassOnClick('x-form-trigger-click');
5588 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5593 initTrigger : function(){
5598 onDestroy : function(){
5600 this.trigger.removeAllListeners();
5601 // this.trigger.remove();
5604 // this.wrap.remove();
5606 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5610 onFocus : function(){
5611 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5614 this.wrap.addClass('x-trigger-wrap-focus');
5615 this.mimicing = true;
5616 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5617 if(this.monitorTab){
5618 this.el.on("keydown", this.checkTab, this);
5625 checkTab : function(e){
5626 if(e.getKey() == e.TAB){
5632 onBlur : function(){
5637 mimicBlur : function(e, t){
5639 if(!this.wrap.contains(t) && this.validateBlur()){
5646 triggerBlur : function(){
5647 this.mimicing = false;
5648 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5649 if(this.monitorTab){
5650 this.el.un("keydown", this.checkTab, this);
5652 //this.wrap.removeClass('x-trigger-wrap-focus');
5653 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5657 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5658 validateBlur : function(e, t){
5663 onDisable : function(){
5664 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5666 // this.wrap.addClass('x-item-disabled');
5671 onEnable : function(){
5672 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5674 // this.el.removeClass('x-item-disabled');
5679 onShow : function(){
5680 var ae = this.getActionEl();
5683 ae.dom.style.display = '';
5684 ae.dom.style.visibility = 'visible';
5690 onHide : function(){
5691 var ae = this.getActionEl();
5692 ae.dom.style.display = 'none';
5696 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5697 * by an implementing function.
5699 * @param {EventObject} e
5701 onTriggerClick : Roo.emptyFn
5705 * Ext JS Library 1.1.1
5706 * Copyright(c) 2006-2007, Ext JS, LLC.
5708 * Originally Released Under LGPL - original licence link has changed is not relivant.
5711 * <script type="text/javascript">
5716 * @class Roo.data.SortTypes
5718 * Defines the default sorting (casting?) comparison functions used when sorting data.
5720 Roo.data.SortTypes = {
5722 * Default sort that does nothing
5723 * @param {Mixed} s The value being converted
5724 * @return {Mixed} The comparison value
5731 * The regular expression used to strip tags
5735 stripTagsRE : /<\/?[^>]+>/gi,
5738 * Strips all HTML tags to sort on text only
5739 * @param {Mixed} s The value being converted
5740 * @return {String} The comparison value
5742 asText : function(s){
5743 return String(s).replace(this.stripTagsRE, "");
5747 * Strips all HTML tags to sort on text only - Case insensitive
5748 * @param {Mixed} s The value being converted
5749 * @return {String} The comparison value
5751 asUCText : function(s){
5752 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5756 * Case insensitive string
5757 * @param {Mixed} s The value being converted
5758 * @return {String} The comparison value
5760 asUCString : function(s) {
5761 return String(s).toUpperCase();
5766 * @param {Mixed} s The value being converted
5767 * @return {Number} The comparison value
5769 asDate : function(s) {
5773 if(s instanceof Date){
5776 return Date.parse(String(s));
5781 * @param {Mixed} s The value being converted
5782 * @return {Float} The comparison value
5784 asFloat : function(s) {
5785 var val = parseFloat(String(s).replace(/,/g, ""));
5786 if(isNaN(val)) val = 0;
5792 * @param {Mixed} s The value being converted
5793 * @return {Number} The comparison value
5795 asInt : function(s) {
5796 var val = parseInt(String(s).replace(/,/g, ""));
5797 if(isNaN(val)) val = 0;
5802 * Ext JS Library 1.1.1
5803 * Copyright(c) 2006-2007, Ext JS, LLC.
5805 * Originally Released Under LGPL - original licence link has changed is not relivant.
5808 * <script type="text/javascript">
5812 * @class Roo.data.Record
5813 * Instances of this class encapsulate both record <em>definition</em> information, and record
5814 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5815 * to access Records cached in an {@link Roo.data.Store} object.<br>
5817 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5818 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5821 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5823 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5824 * {@link #create}. The parameters are the same.
5825 * @param {Array} data An associative Array of data values keyed by the field name.
5826 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5827 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5828 * not specified an integer id is generated.
5830 Roo.data.Record = function(data, id){
5831 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5836 * Generate a constructor for a specific record layout.
5837 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5838 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5839 * Each field definition object may contain the following properties: <ul>
5840 * <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,
5841 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5842 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5843 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5844 * is being used, then this is a string containing the javascript expression to reference the data relative to
5845 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5846 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5847 * this may be omitted.</p></li>
5848 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5849 * <ul><li>auto (Default, implies no conversion)</li>
5854 * <li>date</li></ul></p></li>
5855 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5856 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5857 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5858 * by the Reader into an object that will be stored in the Record. It is passed the
5859 * following parameters:<ul>
5860 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5862 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5864 * <br>usage:<br><pre><code>
5865 var TopicRecord = Roo.data.Record.create(
5866 {name: 'title', mapping: 'topic_title'},
5867 {name: 'author', mapping: 'username'},
5868 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5869 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5870 {name: 'lastPoster', mapping: 'user2'},
5871 {name: 'excerpt', mapping: 'post_text'}
5874 var myNewRecord = new TopicRecord({
5875 title: 'Do my job please',
5878 lastPost: new Date(),
5879 lastPoster: 'Animal',
5880 excerpt: 'No way dude!'
5882 myStore.add(myNewRecord);
5887 Roo.data.Record.create = function(o){
5889 f.superclass.constructor.apply(this, arguments);
5891 Roo.extend(f, Roo.data.Record);
5892 var p = f.prototype;
5893 p.fields = new Roo.util.MixedCollection(false, function(field){
5896 for(var i = 0, len = o.length; i < len; i++){
5897 p.fields.add(new Roo.data.Field(o[i]));
5899 f.getField = function(name){
5900 return p.fields.get(name);
5905 Roo.data.Record.AUTO_ID = 1000;
5906 Roo.data.Record.EDIT = 'edit';
5907 Roo.data.Record.REJECT = 'reject';
5908 Roo.data.Record.COMMIT = 'commit';
5910 Roo.data.Record.prototype = {
5912 * Readonly flag - true if this record has been modified.
5921 join : function(store){
5926 * Set the named field to the specified value.
5927 * @param {String} name The name of the field to set.
5928 * @param {Object} value The value to set the field to.
5930 set : function(name, value){
5931 if(this.data[name] == value){
5938 if(typeof this.modified[name] == 'undefined'){
5939 this.modified[name] = this.data[name];
5941 this.data[name] = value;
5942 if(!this.editing && this.store){
5943 this.store.afterEdit(this);
5948 * Get the value of the named field.
5949 * @param {String} name The name of the field to get the value of.
5950 * @return {Object} The value of the field.
5952 get : function(name){
5953 return this.data[name];
5957 beginEdit : function(){
5958 this.editing = true;
5963 cancelEdit : function(){
5964 this.editing = false;
5965 delete this.modified;
5969 endEdit : function(){
5970 this.editing = false;
5971 if(this.dirty && this.store){
5972 this.store.afterEdit(this);
5977 * Usually called by the {@link Roo.data.Store} which owns the Record.
5978 * Rejects all changes made to the Record since either creation, or the last commit operation.
5979 * Modified fields are reverted to their original values.
5981 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5982 * of reject operations.
5984 reject : function(){
5985 var m = this.modified;
5987 if(typeof m[n] != "function"){
5988 this.data[n] = m[n];
5992 delete this.modified;
5993 this.editing = false;
5995 this.store.afterReject(this);
6000 * Usually called by the {@link Roo.data.Store} which owns the Record.
6001 * Commits all changes made to the Record since either creation, or the last commit operation.
6003 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6004 * of commit operations.
6006 commit : function(){
6008 delete this.modified;
6009 this.editing = false;
6011 this.store.afterCommit(this);
6016 hasError : function(){
6017 return this.error != null;
6021 clearError : function(){
6026 * Creates a copy of this record.
6027 * @param {String} id (optional) A new record id if you don't want to use this record's id
6030 copy : function(newId) {
6031 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6035 * Ext JS Library 1.1.1
6036 * Copyright(c) 2006-2007, Ext JS, LLC.
6038 * Originally Released Under LGPL - original licence link has changed is not relivant.
6041 * <script type="text/javascript">
6047 * @class Roo.data.Store
6048 * @extends Roo.util.Observable
6049 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6050 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6052 * 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
6053 * has no knowledge of the format of the data returned by the Proxy.<br>
6055 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6056 * instances from the data object. These records are cached and made available through accessor functions.
6058 * Creates a new Store.
6059 * @param {Object} config A config object containing the objects needed for the Store to access data,
6060 * and read the data into Records.
6062 Roo.data.Store = function(config){
6063 this.data = new Roo.util.MixedCollection(false);
6064 this.data.getKey = function(o){
6067 this.baseParams = {};
6074 "multisort" : "_multisort"
6077 if(config && config.data){
6078 this.inlineData = config.data;
6082 Roo.apply(this, config);
6084 if(this.reader){ // reader passed
6085 this.reader = Roo.factory(this.reader, Roo.data);
6086 this.reader.xmodule = this.xmodule || false;
6087 if(!this.recordType){
6088 this.recordType = this.reader.recordType;
6090 if(this.reader.onMetaChange){
6091 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6095 if(this.recordType){
6096 this.fields = this.recordType.prototype.fields;
6102 * @event datachanged
6103 * Fires when the data cache has changed, and a widget which is using this Store
6104 * as a Record cache should refresh its view.
6105 * @param {Store} this
6110 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6111 * @param {Store} this
6112 * @param {Object} meta The JSON metadata
6117 * Fires when Records have been added to the Store
6118 * @param {Store} this
6119 * @param {Roo.data.Record[]} records The array of Records added
6120 * @param {Number} index The index at which the record(s) were added
6125 * Fires when a Record has been removed from the Store
6126 * @param {Store} this
6127 * @param {Roo.data.Record} record The Record that was removed
6128 * @param {Number} index The index at which the record was removed
6133 * Fires when a Record has been updated
6134 * @param {Store} this
6135 * @param {Roo.data.Record} record The Record that was updated
6136 * @param {String} operation The update operation being performed. Value may be one of:
6138 Roo.data.Record.EDIT
6139 Roo.data.Record.REJECT
6140 Roo.data.Record.COMMIT
6146 * Fires when the data cache has been cleared.
6147 * @param {Store} this
6152 * Fires before a request is made for a new data object. If the beforeload handler returns false
6153 * the load action will be canceled.
6154 * @param {Store} this
6155 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6159 * @event beforeloadadd
6160 * Fires after a new set of Records has been loaded.
6161 * @param {Store} this
6162 * @param {Roo.data.Record[]} records The Records that were loaded
6163 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6165 beforeloadadd : true,
6168 * Fires after a new set of Records has been loaded, before they are added to the store.
6169 * @param {Store} this
6170 * @param {Roo.data.Record[]} records The Records that were loaded
6171 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6172 * @params {Object} return from reader
6176 * @event loadexception
6177 * Fires if an exception occurs in the Proxy during loading.
6178 * Called with the signature of the Proxy's "loadexception" event.
6179 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6182 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6183 * @param {Object} load options
6184 * @param {Object} jsonData from your request (normally this contains the Exception)
6186 loadexception : true
6190 this.proxy = Roo.factory(this.proxy, Roo.data);
6191 this.proxy.xmodule = this.xmodule || false;
6192 this.relayEvents(this.proxy, ["loadexception"]);
6194 this.sortToggle = {};
6195 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6197 Roo.data.Store.superclass.constructor.call(this);
6199 if(this.inlineData){
6200 this.loadData(this.inlineData);
6201 delete this.inlineData;
6205 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6207 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6208 * without a remote query - used by combo/forms at present.
6212 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6215 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6218 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6219 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6222 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6223 * on any HTTP request
6226 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6229 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6233 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6234 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6239 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6240 * loaded or when a record is removed. (defaults to false).
6242 pruneModifiedRecords : false,
6248 * Add Records to the Store and fires the add event.
6249 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6251 add : function(records){
6252 records = [].concat(records);
6253 for(var i = 0, len = records.length; i < len; i++){
6254 records[i].join(this);
6256 var index = this.data.length;
6257 this.data.addAll(records);
6258 this.fireEvent("add", this, records, index);
6262 * Remove a Record from the Store and fires the remove event.
6263 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6265 remove : function(record){
6266 var index = this.data.indexOf(record);
6267 this.data.removeAt(index);
6268 if(this.pruneModifiedRecords){
6269 this.modified.remove(record);
6271 this.fireEvent("remove", this, record, index);
6275 * Remove all Records from the Store and fires the clear event.
6277 removeAll : function(){
6279 if(this.pruneModifiedRecords){
6282 this.fireEvent("clear", this);
6286 * Inserts Records to the Store at the given index and fires the add event.
6287 * @param {Number} index The start index at which to insert the passed Records.
6288 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6290 insert : function(index, records){
6291 records = [].concat(records);
6292 for(var i = 0, len = records.length; i < len; i++){
6293 this.data.insert(index, records[i]);
6294 records[i].join(this);
6296 this.fireEvent("add", this, records, index);
6300 * Get the index within the cache of the passed Record.
6301 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6302 * @return {Number} The index of the passed Record. Returns -1 if not found.
6304 indexOf : function(record){
6305 return this.data.indexOf(record);
6309 * Get the index within the cache of the Record with the passed id.
6310 * @param {String} id The id of the Record to find.
6311 * @return {Number} The index of the Record. Returns -1 if not found.
6313 indexOfId : function(id){
6314 return this.data.indexOfKey(id);
6318 * Get the Record with the specified id.
6319 * @param {String} id The id of the Record to find.
6320 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6322 getById : function(id){
6323 return this.data.key(id);
6327 * Get the Record at the specified index.
6328 * @param {Number} index The index of the Record to find.
6329 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6331 getAt : function(index){
6332 return this.data.itemAt(index);
6336 * Returns a range of Records between specified indices.
6337 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6338 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6339 * @return {Roo.data.Record[]} An array of Records
6341 getRange : function(start, end){
6342 return this.data.getRange(start, end);
6346 storeOptions : function(o){
6347 o = Roo.apply({}, o);
6350 this.lastOptions = o;
6354 * Loads the Record cache from the configured Proxy using the configured Reader.
6356 * If using remote paging, then the first load call must specify the <em>start</em>
6357 * and <em>limit</em> properties in the options.params property to establish the initial
6358 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6360 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6361 * and this call will return before the new data has been loaded. Perform any post-processing
6362 * in a callback function, or in a "load" event handler.</strong>
6364 * @param {Object} options An object containing properties which control loading options:<ul>
6365 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6366 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6367 * passed the following arguments:<ul>
6368 * <li>r : Roo.data.Record[]</li>
6369 * <li>options: Options object from the load call</li>
6370 * <li>success: Boolean success indicator</li></ul></li>
6371 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6372 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6375 load : function(options){
6376 options = options || {};
6377 if(this.fireEvent("beforeload", this, options) !== false){
6378 this.storeOptions(options);
6379 var p = Roo.apply(options.params || {}, this.baseParams);
6380 // if meta was not loaded from remote source.. try requesting it.
6381 if (!this.reader.metaFromRemote) {
6384 if(this.sortInfo && this.remoteSort){
6385 var pn = this.paramNames;
6386 p[pn["sort"]] = this.sortInfo.field;
6387 p[pn["dir"]] = this.sortInfo.direction;
6389 if (this.multiSort) {
6390 var pn = this.paramNames;
6391 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6394 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6399 * Reloads the Record cache from the configured Proxy using the configured Reader and
6400 * the options from the last load operation performed.
6401 * @param {Object} options (optional) An object containing properties which may override the options
6402 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6403 * the most recently used options are reused).
6405 reload : function(options){
6406 this.load(Roo.applyIf(options||{}, this.lastOptions));
6410 // Called as a callback by the Reader during a load operation.
6411 loadRecords : function(o, options, success){
6412 if(!o || success === false){
6413 if(success !== false){
6414 this.fireEvent("load", this, [], options, o);
6416 if(options.callback){
6417 options.callback.call(options.scope || this, [], options, false);
6421 // if data returned failure - throw an exception.
6422 if (o.success === false) {
6423 // show a message if no listener is registered.
6424 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6425 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6427 // loadmask wil be hooked into this..
6428 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6431 var r = o.records, t = o.totalRecords || r.length;
6433 this.fireEvent("beforeloadadd", this, r, options, o);
6435 if(!options || options.add !== true){
6436 if(this.pruneModifiedRecords){
6439 for(var i = 0, len = r.length; i < len; i++){
6443 this.data = this.snapshot;
6444 delete this.snapshot;
6447 this.data.addAll(r);
6448 this.totalLength = t;
6450 this.fireEvent("datachanged", this);
6452 this.totalLength = Math.max(t, this.data.length+r.length);
6455 this.fireEvent("load", this, r, options, o);
6456 if(options.callback){
6457 options.callback.call(options.scope || this, r, options, true);
6463 * Loads data from a passed data block. A Reader which understands the format of the data
6464 * must have been configured in the constructor.
6465 * @param {Object} data The data block from which to read the Records. The format of the data expected
6466 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6467 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6469 loadData : function(o, append){
6470 var r = this.reader.readRecords(o);
6471 this.loadRecords(r, {add: append}, true);
6475 * Gets the number of cached records.
6477 * <em>If using paging, this may not be the total size of the dataset. If the data object
6478 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6479 * the data set size</em>
6481 getCount : function(){
6482 return this.data.length || 0;
6486 * Gets the total number of records in the dataset as returned by the server.
6488 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6489 * the dataset size</em>
6491 getTotalCount : function(){
6492 return this.totalLength || 0;
6496 * Returns the sort state of the Store as an object with two properties:
6498 field {String} The name of the field by which the Records are sorted
6499 direction {String} The sort order, "ASC" or "DESC"
6502 getSortState : function(){
6503 return this.sortInfo;
6507 applySort : function(){
6508 if(this.sortInfo && !this.remoteSort){
6509 var s = this.sortInfo, f = s.field;
6510 var st = this.fields.get(f).sortType;
6511 var fn = function(r1, r2){
6512 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6513 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6515 this.data.sort(s.direction, fn);
6516 if(this.snapshot && this.snapshot != this.data){
6517 this.snapshot.sort(s.direction, fn);
6523 * Sets the default sort column and order to be used by the next load operation.
6524 * @param {String} fieldName The name of the field to sort by.
6525 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6527 setDefaultSort : function(field, dir){
6528 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6533 * If remote sorting is used, the sort is performed on the server, and the cache is
6534 * reloaded. If local sorting is used, the cache is sorted internally.
6535 * @param {String} fieldName The name of the field to sort by.
6536 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6538 sort : function(fieldName, dir){
6539 var f = this.fields.get(fieldName);
6541 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6543 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6544 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6549 this.sortToggle[f.name] = dir;
6550 this.sortInfo = {field: f.name, direction: dir};
6551 if(!this.remoteSort){
6553 this.fireEvent("datachanged", this);
6555 this.load(this.lastOptions);
6560 * Calls the specified function for each of the Records in the cache.
6561 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6562 * Returning <em>false</em> aborts and exits the iteration.
6563 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6565 each : function(fn, scope){
6566 this.data.each(fn, scope);
6570 * Gets all records modified since the last commit. Modified records are persisted across load operations
6571 * (e.g., during paging).
6572 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6574 getModifiedRecords : function(){
6575 return this.modified;
6579 createFilterFn : function(property, value, anyMatch){
6580 if(!value.exec){ // not a regex
6581 value = String(value);
6582 if(value.length == 0){
6585 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6588 return value.test(r.data[property]);
6593 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6594 * @param {String} property A field on your records
6595 * @param {Number} start The record index to start at (defaults to 0)
6596 * @param {Number} end The last record index to include (defaults to length - 1)
6597 * @return {Number} The sum
6599 sum : function(property, start, end){
6600 var rs = this.data.items, v = 0;
6602 end = (end || end === 0) ? end : rs.length-1;
6604 for(var i = start; i <= end; i++){
6605 v += (rs[i].data[property] || 0);
6611 * Filter the records by a specified property.
6612 * @param {String} field A field on your records
6613 * @param {String/RegExp} value Either a string that the field
6614 * should start with or a RegExp to test against the field
6615 * @param {Boolean} anyMatch True to match any part not just the beginning
6617 filter : function(property, value, anyMatch){
6618 var fn = this.createFilterFn(property, value, anyMatch);
6619 return fn ? this.filterBy(fn) : this.clearFilter();
6623 * Filter by a function. The specified function will be called with each
6624 * record in this data source. If the function returns true the record is included,
6625 * otherwise it is filtered.
6626 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6627 * @param {Object} scope (optional) The scope of the function (defaults to this)
6629 filterBy : function(fn, scope){
6630 this.snapshot = this.snapshot || this.data;
6631 this.data = this.queryBy(fn, scope||this);
6632 this.fireEvent("datachanged", this);
6636 * Query the records by a specified property.
6637 * @param {String} field A field on your records
6638 * @param {String/RegExp} value Either a string that the field
6639 * should start with or a RegExp to test against the field
6640 * @param {Boolean} anyMatch True to match any part not just the beginning
6641 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6643 query : function(property, value, anyMatch){
6644 var fn = this.createFilterFn(property, value, anyMatch);
6645 return fn ? this.queryBy(fn) : this.data.clone();
6649 * Query by a function. The specified function will be called with each
6650 * record in this data source. If the function returns true the record is included
6652 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6653 * @param {Object} scope (optional) The scope of the function (defaults to this)
6654 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6656 queryBy : function(fn, scope){
6657 var data = this.snapshot || this.data;
6658 return data.filterBy(fn, scope||this);
6662 * Collects unique values for a particular dataIndex from this store.
6663 * @param {String} dataIndex The property to collect
6664 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6665 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6666 * @return {Array} An array of the unique values
6668 collect : function(dataIndex, allowNull, bypassFilter){
6669 var d = (bypassFilter === true && this.snapshot) ?
6670 this.snapshot.items : this.data.items;
6671 var v, sv, r = [], l = {};
6672 for(var i = 0, len = d.length; i < len; i++){
6673 v = d[i].data[dataIndex];
6675 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6684 * Revert to a view of the Record cache with no filtering applied.
6685 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6687 clearFilter : function(suppressEvent){
6688 if(this.snapshot && this.snapshot != this.data){
6689 this.data = this.snapshot;
6690 delete this.snapshot;
6691 if(suppressEvent !== true){
6692 this.fireEvent("datachanged", this);
6698 afterEdit : function(record){
6699 if(this.modified.indexOf(record) == -1){
6700 this.modified.push(record);
6702 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6706 afterReject : function(record){
6707 this.modified.remove(record);
6708 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6712 afterCommit : function(record){
6713 this.modified.remove(record);
6714 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6718 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6719 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6721 commitChanges : function(){
6722 var m = this.modified.slice(0);
6724 for(var i = 0, len = m.length; i < len; i++){
6730 * Cancel outstanding changes on all changed records.
6732 rejectChanges : function(){
6733 var m = this.modified.slice(0);
6735 for(var i = 0, len = m.length; i < len; i++){
6740 onMetaChange : function(meta, rtype, o){
6741 this.recordType = rtype;
6742 this.fields = rtype.prototype.fields;
6743 delete this.snapshot;
6744 this.sortInfo = meta.sortInfo || this.sortInfo;
6746 this.fireEvent('metachange', this, this.reader.meta);
6749 moveIndex : function(data, type)
6751 var index = this.indexOf(data);
6753 var newIndex = index + type;
6757 this.insert(newIndex, data);
6762 * Ext JS Library 1.1.1
6763 * Copyright(c) 2006-2007, Ext JS, LLC.
6765 * Originally Released Under LGPL - original licence link has changed is not relivant.
6768 * <script type="text/javascript">
6772 * @class Roo.data.SimpleStore
6773 * @extends Roo.data.Store
6774 * Small helper class to make creating Stores from Array data easier.
6775 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6776 * @cfg {Array} fields An array of field definition objects, or field name strings.
6777 * @cfg {Array} data The multi-dimensional array of data
6779 * @param {Object} config
6781 Roo.data.SimpleStore = function(config){
6782 Roo.data.SimpleStore.superclass.constructor.call(this, {
6784 reader: new Roo.data.ArrayReader({
6787 Roo.data.Record.create(config.fields)
6789 proxy : new Roo.data.MemoryProxy(config.data)
6793 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6795 * Ext JS Library 1.1.1
6796 * Copyright(c) 2006-2007, Ext JS, LLC.
6798 * Originally Released Under LGPL - original licence link has changed is not relivant.
6801 * <script type="text/javascript">
6806 * @extends Roo.data.Store
6807 * @class Roo.data.JsonStore
6808 * Small helper class to make creating Stores for JSON data easier. <br/>
6810 var store = new Roo.data.JsonStore({
6811 url: 'get-images.php',
6813 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6816 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6817 * JsonReader and HttpProxy (unless inline data is provided).</b>
6818 * @cfg {Array} fields An array of field definition objects, or field name strings.
6820 * @param {Object} config
6822 Roo.data.JsonStore = function(c){
6823 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6824 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6825 reader: new Roo.data.JsonReader(c, c.fields)
6828 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6830 * Ext JS Library 1.1.1
6831 * Copyright(c) 2006-2007, Ext JS, LLC.
6833 * Originally Released Under LGPL - original licence link has changed is not relivant.
6836 * <script type="text/javascript">
6840 Roo.data.Field = function(config){
6841 if(typeof config == "string"){
6842 config = {name: config};
6844 Roo.apply(this, config);
6850 var st = Roo.data.SortTypes;
6851 // named sortTypes are supported, here we look them up
6852 if(typeof this.sortType == "string"){
6853 this.sortType = st[this.sortType];
6856 // set default sortType for strings and dates
6860 this.sortType = st.asUCString;
6863 this.sortType = st.asDate;
6866 this.sortType = st.none;
6871 var stripRe = /[\$,%]/g;
6873 // prebuilt conversion function for this field, instead of
6874 // switching every time we're reading a value
6876 var cv, dateFormat = this.dateFormat;
6881 cv = function(v){ return v; };
6884 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6888 return v !== undefined && v !== null && v !== '' ?
6889 parseInt(String(v).replace(stripRe, ""), 10) : '';
6894 return v !== undefined && v !== null && v !== '' ?
6895 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6900 cv = function(v){ return v === true || v === "true" || v == 1; };
6907 if(v instanceof Date){
6911 if(dateFormat == "timestamp"){
6912 return new Date(v*1000);
6914 return Date.parseDate(v, dateFormat);
6916 var parsed = Date.parse(v);
6917 return parsed ? new Date(parsed) : null;
6926 Roo.data.Field.prototype = {
6934 * Ext JS Library 1.1.1
6935 * Copyright(c) 2006-2007, Ext JS, LLC.
6937 * Originally Released Under LGPL - original licence link has changed is not relivant.
6940 * <script type="text/javascript">
6943 // Base class for reading structured data from a data source. This class is intended to be
6944 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6947 * @class Roo.data.DataReader
6948 * Base class for reading structured data from a data source. This class is intended to be
6949 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6952 Roo.data.DataReader = function(meta, recordType){
6956 this.recordType = recordType instanceof Array ?
6957 Roo.data.Record.create(recordType) : recordType;
6960 Roo.data.DataReader.prototype = {
6962 * Create an empty record
6963 * @param {Object} data (optional) - overlay some values
6964 * @return {Roo.data.Record} record created.
6966 newRow : function(d) {
6968 this.recordType.prototype.fields.each(function(c) {
6970 case 'int' : da[c.name] = 0; break;
6971 case 'date' : da[c.name] = new Date(); break;
6972 case 'float' : da[c.name] = 0.0; break;
6973 case 'boolean' : da[c.name] = false; break;
6974 default : da[c.name] = ""; break;
6978 return new this.recordType(Roo.apply(da, d));
6983 * Ext JS Library 1.1.1
6984 * Copyright(c) 2006-2007, Ext JS, LLC.
6986 * Originally Released Under LGPL - original licence link has changed is not relivant.
6989 * <script type="text/javascript">
6993 * @class Roo.data.DataProxy
6994 * @extends Roo.data.Observable
6995 * This class is an abstract base class for implementations which provide retrieval of
6996 * unformatted data objects.<br>
6998 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6999 * (of the appropriate type which knows how to parse the data object) to provide a block of
7000 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7002 * Custom implementations must implement the load method as described in
7003 * {@link Roo.data.HttpProxy#load}.
7005 Roo.data.DataProxy = function(){
7009 * Fires before a network request is made to retrieve a data object.
7010 * @param {Object} This DataProxy object.
7011 * @param {Object} params The params parameter to the load function.
7016 * Fires before the load method's callback is called.
7017 * @param {Object} This DataProxy object.
7018 * @param {Object} o The data object.
7019 * @param {Object} arg The callback argument object passed to the load function.
7023 * @event loadexception
7024 * Fires if an Exception occurs during data retrieval.
7025 * @param {Object} This DataProxy object.
7026 * @param {Object} o The data object.
7027 * @param {Object} arg The callback argument object passed to the load function.
7028 * @param {Object} e The Exception.
7030 loadexception : true
7032 Roo.data.DataProxy.superclass.constructor.call(this);
7035 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7038 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7042 * Ext JS Library 1.1.1
7043 * Copyright(c) 2006-2007, Ext JS, LLC.
7045 * Originally Released Under LGPL - original licence link has changed is not relivant.
7048 * <script type="text/javascript">
7051 * @class Roo.data.MemoryProxy
7052 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7053 * to the Reader when its load method is called.
7055 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7057 Roo.data.MemoryProxy = function(data){
7061 Roo.data.MemoryProxy.superclass.constructor.call(this);
7065 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7067 * Load data from the requested source (in this case an in-memory
7068 * data object passed to the constructor), read the data object into
7069 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7070 * process that block using the passed callback.
7071 * @param {Object} params This parameter is not used by the MemoryProxy class.
7072 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7073 * object into a block of Roo.data.Records.
7074 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7075 * The function must be passed <ul>
7076 * <li>The Record block object</li>
7077 * <li>The "arg" argument from the load function</li>
7078 * <li>A boolean success indicator</li>
7080 * @param {Object} scope The scope in which to call the callback
7081 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7083 load : function(params, reader, callback, scope, arg){
7084 params = params || {};
7087 result = reader.readRecords(this.data);
7089 this.fireEvent("loadexception", this, arg, null, e);
7090 callback.call(scope, null, arg, false);
7093 callback.call(scope, result, arg, true);
7097 update : function(params, records){
7102 * Ext JS Library 1.1.1
7103 * Copyright(c) 2006-2007, Ext JS, LLC.
7105 * Originally Released Under LGPL - original licence link has changed is not relivant.
7108 * <script type="text/javascript">
7111 * @class Roo.data.HttpProxy
7112 * @extends Roo.data.DataProxy
7113 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7114 * configured to reference a certain URL.<br><br>
7116 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7117 * from which the running page was served.<br><br>
7119 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7121 * Be aware that to enable the browser to parse an XML document, the server must set
7122 * the Content-Type header in the HTTP response to "text/xml".
7124 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7125 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7126 * will be used to make the request.
7128 Roo.data.HttpProxy = function(conn){
7129 Roo.data.HttpProxy.superclass.constructor.call(this);
7130 // is conn a conn config or a real conn?
7132 this.useAjax = !conn || !conn.events;
7136 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7137 // thse are take from connection...
7140 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7143 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7144 * extra parameters to each request made by this object. (defaults to undefined)
7147 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7148 * to each request made by this object. (defaults to undefined)
7151 * @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)
7154 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7157 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7163 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7167 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7168 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7169 * a finer-grained basis than the DataProxy events.
7171 getConnection : function(){
7172 return this.useAjax ? Roo.Ajax : this.conn;
7176 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7177 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7178 * process that block using the passed callback.
7179 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7180 * for the request to the remote server.
7181 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7182 * object into a block of Roo.data.Records.
7183 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7184 * The function must be passed <ul>
7185 * <li>The Record block object</li>
7186 * <li>The "arg" argument from the load function</li>
7187 * <li>A boolean success indicator</li>
7189 * @param {Object} scope The scope in which to call the callback
7190 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7192 load : function(params, reader, callback, scope, arg){
7193 if(this.fireEvent("beforeload", this, params) !== false){
7195 params : params || {},
7197 callback : callback,
7202 callback : this.loadResponse,
7206 Roo.applyIf(o, this.conn);
7207 if(this.activeRequest){
7208 Roo.Ajax.abort(this.activeRequest);
7210 this.activeRequest = Roo.Ajax.request(o);
7212 this.conn.request(o);
7215 callback.call(scope||this, null, arg, false);
7220 loadResponse : function(o, success, response){
7221 delete this.activeRequest;
7223 this.fireEvent("loadexception", this, o, response);
7224 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7229 result = o.reader.read(response);
7231 this.fireEvent("loadexception", this, o, response, e);
7232 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7236 this.fireEvent("load", this, o, o.request.arg);
7237 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7241 update : function(dataSet){
7246 updateResponse : function(dataSet){
7251 * Ext JS Library 1.1.1
7252 * Copyright(c) 2006-2007, Ext JS, LLC.
7254 * Originally Released Under LGPL - original licence link has changed is not relivant.
7257 * <script type="text/javascript">
7261 * @class Roo.data.ScriptTagProxy
7262 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7263 * other than the originating domain of the running page.<br><br>
7265 * <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
7266 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7268 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7269 * source code that is used as the source inside a <script> tag.<br><br>
7271 * In order for the browser to process the returned data, the server must wrap the data object
7272 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7273 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7274 * depending on whether the callback name was passed:
7277 boolean scriptTag = false;
7278 String cb = request.getParameter("callback");
7281 response.setContentType("text/javascript");
7283 response.setContentType("application/x-json");
7285 Writer out = response.getWriter();
7287 out.write(cb + "(");
7289 out.print(dataBlock.toJsonString());
7296 * @param {Object} config A configuration object.
7298 Roo.data.ScriptTagProxy = function(config){
7299 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7300 Roo.apply(this, config);
7301 this.head = document.getElementsByTagName("head")[0];
7304 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7306 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7308 * @cfg {String} url The URL from which to request the data object.
7311 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7315 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7316 * the server the name of the callback function set up by the load call to process the returned data object.
7317 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7318 * javascript output which calls this named function passing the data object as its only parameter.
7320 callbackParam : "callback",
7322 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7323 * name to the request.
7328 * Load data from the configured URL, read the data object into
7329 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7330 * process that block using the passed callback.
7331 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7332 * for the request to the remote server.
7333 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7334 * object into a block of Roo.data.Records.
7335 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7336 * The function must be passed <ul>
7337 * <li>The Record block object</li>
7338 * <li>The "arg" argument from the load function</li>
7339 * <li>A boolean success indicator</li>
7341 * @param {Object} scope The scope in which to call the callback
7342 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7344 load : function(params, reader, callback, scope, arg){
7345 if(this.fireEvent("beforeload", this, params) !== false){
7347 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7350 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7352 url += "&_dc=" + (new Date().getTime());
7354 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7357 cb : "stcCallback"+transId,
7358 scriptId : "stcScript"+transId,
7362 callback : callback,
7368 window[trans.cb] = function(o){
7369 conn.handleResponse(o, trans);
7372 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7374 if(this.autoAbort !== false){
7378 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7380 var script = document.createElement("script");
7381 script.setAttribute("src", url);
7382 script.setAttribute("type", "text/javascript");
7383 script.setAttribute("id", trans.scriptId);
7384 this.head.appendChild(script);
7388 callback.call(scope||this, null, arg, false);
7393 isLoading : function(){
7394 return this.trans ? true : false;
7398 * Abort the current server request.
7401 if(this.isLoading()){
7402 this.destroyTrans(this.trans);
7407 destroyTrans : function(trans, isLoaded){
7408 this.head.removeChild(document.getElementById(trans.scriptId));
7409 clearTimeout(trans.timeoutId);
7411 window[trans.cb] = undefined;
7413 delete window[trans.cb];
7416 // if hasn't been loaded, wait for load to remove it to prevent script error
7417 window[trans.cb] = function(){
7418 window[trans.cb] = undefined;
7420 delete window[trans.cb];
7427 handleResponse : function(o, trans){
7429 this.destroyTrans(trans, true);
7432 result = trans.reader.readRecords(o);
7434 this.fireEvent("loadexception", this, o, trans.arg, e);
7435 trans.callback.call(trans.scope||window, null, trans.arg, false);
7438 this.fireEvent("load", this, o, trans.arg);
7439 trans.callback.call(trans.scope||window, result, trans.arg, true);
7443 handleFailure : function(trans){
7445 this.destroyTrans(trans, false);
7446 this.fireEvent("loadexception", this, null, trans.arg);
7447 trans.callback.call(trans.scope||window, null, trans.arg, false);
7451 * Ext JS Library 1.1.1
7452 * Copyright(c) 2006-2007, Ext JS, LLC.
7454 * Originally Released Under LGPL - original licence link has changed is not relivant.
7457 * <script type="text/javascript">
7461 * @class Roo.data.JsonReader
7462 * @extends Roo.data.DataReader
7463 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7464 * based on mappings in a provided Roo.data.Record constructor.
7466 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7467 * in the reply previously.
7472 var RecordDef = Roo.data.Record.create([
7473 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7474 {name: 'occupation'} // This field will use "occupation" as the mapping.
7476 var myReader = new Roo.data.JsonReader({
7477 totalProperty: "results", // The property which contains the total dataset size (optional)
7478 root: "rows", // The property which contains an Array of row objects
7479 id: "id" // The property within each row object that provides an ID for the record (optional)
7483 * This would consume a JSON file like this:
7485 { 'results': 2, 'rows': [
7486 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7487 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7490 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7491 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7492 * paged from the remote server.
7493 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7494 * @cfg {String} root name of the property which contains the Array of row objects.
7495 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7497 * Create a new JsonReader
7498 * @param {Object} meta Metadata configuration options
7499 * @param {Object} recordType Either an Array of field definition objects,
7500 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7502 Roo.data.JsonReader = function(meta, recordType){
7505 // set some defaults:
7507 totalProperty: 'total',
7508 successProperty : 'success',
7513 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7515 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7518 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7519 * Used by Store query builder to append _requestMeta to params.
7522 metaFromRemote : false,
7524 * This method is only used by a DataProxy which has retrieved data from a remote server.
7525 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7526 * @return {Object} data A data block which is used by an Roo.data.Store object as
7527 * a cache of Roo.data.Records.
7529 read : function(response){
7530 var json = response.responseText;
7532 var o = /* eval:var:o */ eval("("+json+")");
7534 throw {message: "JsonReader.read: Json object not found"};
7540 this.metaFromRemote = true;
7541 this.meta = o.metaData;
7542 this.recordType = Roo.data.Record.create(o.metaData.fields);
7543 this.onMetaChange(this.meta, this.recordType, o);
7545 return this.readRecords(o);
7548 // private function a store will implement
7549 onMetaChange : function(meta, recordType, o){
7556 simpleAccess: function(obj, subsc) {
7563 getJsonAccessor: function(){
7565 return function(expr) {
7567 return(re.test(expr))
7568 ? new Function("obj", "return obj." + expr)
7578 * Create a data block containing Roo.data.Records from an XML document.
7579 * @param {Object} o An object which contains an Array of row objects in the property specified
7580 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7581 * which contains the total size of the dataset.
7582 * @return {Object} data A data block which is used by an Roo.data.Store object as
7583 * a cache of Roo.data.Records.
7585 readRecords : function(o){
7587 * After any data loads, the raw JSON data is available for further custom processing.
7591 var s = this.meta, Record = this.recordType,
7592 f = Record.prototype.fields, fi = f.items, fl = f.length;
7594 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7596 if(s.totalProperty) {
7597 this.getTotal = this.getJsonAccessor(s.totalProperty);
7599 if(s.successProperty) {
7600 this.getSuccess = this.getJsonAccessor(s.successProperty);
7602 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7604 var g = this.getJsonAccessor(s.id);
7605 this.getId = function(rec) {
7607 return (r === undefined || r === "") ? null : r;
7610 this.getId = function(){return null;};
7613 for(var jj = 0; jj < fl; jj++){
7615 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7616 this.ef[jj] = this.getJsonAccessor(map);
7620 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7621 if(s.totalProperty){
7622 var vt = parseInt(this.getTotal(o), 10);
7627 if(s.successProperty){
7628 var vs = this.getSuccess(o);
7629 if(vs === false || vs === 'false'){
7634 for(var i = 0; i < c; i++){
7637 var id = this.getId(n);
7638 for(var j = 0; j < fl; j++){
7640 var v = this.ef[j](n);
7642 Roo.log('missing convert for ' + f.name);
7646 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7648 var record = new Record(values, id);
7650 records[i] = record;
7656 totalRecords : totalRecords
7661 * Ext JS Library 1.1.1
7662 * Copyright(c) 2006-2007, Ext JS, LLC.
7664 * Originally Released Under LGPL - original licence link has changed is not relivant.
7667 * <script type="text/javascript">
7671 * @class Roo.data.ArrayReader
7672 * @extends Roo.data.DataReader
7673 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7674 * Each element of that Array represents a row of data fields. The
7675 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7676 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7680 var RecordDef = Roo.data.Record.create([
7681 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7682 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7684 var myReader = new Roo.data.ArrayReader({
7685 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7689 * This would consume an Array like this:
7691 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7693 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7695 * Create a new JsonReader
7696 * @param {Object} meta Metadata configuration options.
7697 * @param {Object} recordType Either an Array of field definition objects
7698 * as specified to {@link Roo.data.Record#create},
7699 * or an {@link Roo.data.Record} object
7700 * created using {@link Roo.data.Record#create}.
7702 Roo.data.ArrayReader = function(meta, recordType){
7703 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7706 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7708 * Create a data block containing Roo.data.Records from an XML document.
7709 * @param {Object} o An Array of row objects which represents the dataset.
7710 * @return {Object} data A data block which is used by an Roo.data.Store object as
7711 * a cache of Roo.data.Records.
7713 readRecords : function(o){
7714 var sid = this.meta ? this.meta.id : null;
7715 var recordType = this.recordType, fields = recordType.prototype.fields;
7718 for(var i = 0; i < root.length; i++){
7721 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7722 for(var j = 0, jlen = fields.length; j < jlen; j++){
7723 var f = fields.items[j];
7724 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7725 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7729 var record = new recordType(values, id);
7731 records[records.length] = record;
7735 totalRecords : records.length
7744 * @class Roo.bootstrap.ComboBox
7745 * @extends Roo.bootstrap.TriggerField
7746 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7747 * @cfg {Boolean} append (true|false) default false
7749 * Create a new ComboBox.
7750 * @param {Object} config Configuration options
7752 Roo.bootstrap.ComboBox = function(config){
7753 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7757 * Fires when the dropdown list is expanded
7758 * @param {Roo.bootstrap.ComboBox} combo This combo box
7763 * Fires when the dropdown list is collapsed
7764 * @param {Roo.bootstrap.ComboBox} combo This combo box
7768 * @event beforeselect
7769 * Fires before a list item is selected. Return false to cancel the selection.
7770 * @param {Roo.bootstrap.ComboBox} combo This combo box
7771 * @param {Roo.data.Record} record The data record returned from the underlying store
7772 * @param {Number} index The index of the selected item in the dropdown list
7774 'beforeselect' : true,
7777 * Fires when a list item is selected
7778 * @param {Roo.bootstrap.ComboBox} combo This combo box
7779 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7780 * @param {Number} index The index of the selected item in the dropdown list
7784 * @event beforequery
7785 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7786 * The event object passed has these properties:
7787 * @param {Roo.bootstrap.ComboBox} combo This combo box
7788 * @param {String} query The query
7789 * @param {Boolean} forceAll true to force "all" query
7790 * @param {Boolean} cancel true to cancel the query
7791 * @param {Object} e The query event object
7793 'beforequery': true,
7796 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7797 * @param {Roo.bootstrap.ComboBox} combo This combo box
7802 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7803 * @param {Roo.bootstrap.ComboBox} combo This combo box
7804 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7809 * Fires when the remove value from the combobox array
7810 * @param {Roo.bootstrap.ComboBox} combo This combo box
7817 this.selectedIndex = -1;
7818 if(this.mode == 'local'){
7819 if(config.queryDelay === undefined){
7820 this.queryDelay = 10;
7822 if(config.minChars === undefined){
7828 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7831 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7832 * rendering into an Roo.Editor, defaults to false)
7835 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7836 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7839 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7842 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7843 * the dropdown list (defaults to undefined, with no header element)
7847 * @cfg {String/Roo.Template} tpl The template to use to render the output
7851 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7853 listWidth: undefined,
7855 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7856 * mode = 'remote' or 'text' if mode = 'local')
7858 displayField: undefined,
7860 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7861 * mode = 'remote' or 'value' if mode = 'local').
7862 * Note: use of a valueField requires the user make a selection
7863 * in order for a value to be mapped.
7865 valueField: undefined,
7869 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7870 * field's data value (defaults to the underlying DOM element's name)
7872 hiddenName: undefined,
7874 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7878 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7880 selectedClass: 'active',
7883 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7887 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7888 * anchor positions (defaults to 'tl-bl')
7890 listAlign: 'tl-bl?',
7892 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7896 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7897 * query specified by the allQuery config option (defaults to 'query')
7899 triggerAction: 'query',
7901 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7902 * (defaults to 4, does not apply if editable = false)
7906 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7907 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7911 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7912 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7916 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7917 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7921 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7922 * when editable = true (defaults to false)
7924 selectOnFocus:false,
7926 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7928 queryParam: 'query',
7930 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7931 * when mode = 'remote' (defaults to 'Loading...')
7933 loadingText: 'Loading...',
7935 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7939 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7943 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7944 * traditional select (defaults to true)
7948 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7952 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7956 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7957 * listWidth has a higher value)
7961 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7962 * allow the user to set arbitrary text into the field (defaults to false)
7964 forceSelection:false,
7966 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7967 * if typeAhead = true (defaults to 250)
7969 typeAheadDelay : 250,
7971 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7972 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7974 valueNotFoundText : undefined,
7976 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7981 * @cfg {Boolean} disableClear Disable showing of clear button.
7983 disableClear : false,
7985 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
7987 alwaysQuery : false,
7990 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8004 // element that contains real text value.. (when hidden is used..)
8007 initEvents: function(){
8010 throw "can not find store for combo";
8012 this.store = Roo.factory(this.store, Roo.data);
8016 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8019 if(this.hiddenName){
8021 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8023 this.hiddenField.dom.value =
8024 this.hiddenValue !== undefined ? this.hiddenValue :
8025 this.value !== undefined ? this.value : '';
8027 // prevent input submission
8028 this.el.dom.removeAttribute('name');
8029 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8034 // this.el.dom.setAttribute('autocomplete', 'off');
8037 var cls = 'x-combo-list';
8038 this.list = this.el.select('ul.dropdown-menu',true).first();
8040 //this.list = new Roo.Layer({
8041 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8044 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8045 this.list.setWidth(lw);
8047 this.list.on('mouseover', this.onViewOver, this);
8048 this.list.on('mousemove', this.onViewMove, this);
8050 this.list.on('scroll', this.onViewScroll, this);
8053 this.list.swallowEvent('mousewheel');
8054 this.assetHeight = 0;
8057 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8058 this.assetHeight += this.header.getHeight();
8061 this.innerList = this.list.createChild({cls:cls+'-inner'});
8062 this.innerList.on('mouseover', this.onViewOver, this);
8063 this.innerList.on('mousemove', this.onViewMove, this);
8064 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8066 if(this.allowBlank && !this.pageSize && !this.disableClear){
8067 this.footer = this.list.createChild({cls:cls+'-ft'});
8068 this.pageTb = new Roo.Toolbar(this.footer);
8072 this.footer = this.list.createChild({cls:cls+'-ft'});
8073 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8074 {pageSize: this.pageSize});
8078 if (this.pageTb && this.allowBlank && !this.disableClear) {
8080 this.pageTb.add(new Roo.Toolbar.Fill(), {
8081 cls: 'x-btn-icon x-btn-clear',
8087 _this.onSelect(false, -1);
8092 this.assetHeight += this.footer.getHeight();
8097 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8100 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8101 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8103 //this.view.wrapEl.setDisplayed(false);
8104 this.view.on('click', this.onViewClick, this);
8108 this.store.on('beforeload', this.onBeforeLoad, this);
8109 this.store.on('load', this.onLoad, this);
8110 this.store.on('loadexception', this.onLoadException, this);
8113 this.resizer = new Roo.Resizable(this.list, {
8114 pinned:true, handles:'se'
8116 this.resizer.on('resize', function(r, w, h){
8117 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8119 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8120 this.restrictHeight();
8122 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8126 this.editable = true;
8127 this.setEditable(false);
8132 if (typeof(this.events.add.listeners) != 'undefined') {
8134 this.addicon = this.wrap.createChild(
8135 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8137 this.addicon.on('click', function(e) {
8138 this.fireEvent('add', this);
8141 if (typeof(this.events.edit.listeners) != 'undefined') {
8143 this.editicon = this.wrap.createChild(
8144 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8146 this.editicon.setStyle('margin-left', '40px');
8148 this.editicon.on('click', function(e) {
8150 // we fire even if inothing is selected..
8151 this.fireEvent('edit', this, this.lastData );
8157 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8159 this.inKeyMode = true;
8163 "down" : function(e){
8164 if(!this.isExpanded()){
8165 this.onTriggerClick();
8167 this.inKeyMode = true;
8172 "enter" : function(e){
8177 "esc" : function(e){
8181 "tab" : function(e){
8184 if(this.fireEvent("specialkey", this, e)){
8185 this.onViewClick(false);
8193 doRelay : function(foo, bar, hname){
8194 if(hname == 'down' || this.scope.isExpanded()){
8195 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8204 this.queryDelay = Math.max(this.queryDelay || 10,
8205 this.mode == 'local' ? 10 : 250);
8208 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8211 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8213 if(this.editable !== false){
8214 this.inputEl().on("keyup", this.onKeyUp, this);
8216 if(this.forceSelection){
8217 this.on('blur', this.doForce, this);
8221 this.choices = this.el.select('ul.select2-choices', true).first();
8222 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8226 onDestroy : function(){
8228 this.view.setStore(null);
8229 this.view.el.removeAllListeners();
8230 this.view.el.remove();
8231 this.view.purgeListeners();
8234 this.list.dom.innerHTML = '';
8237 this.store.un('beforeload', this.onBeforeLoad, this);
8238 this.store.un('load', this.onLoad, this);
8239 this.store.un('loadexception', this.onLoadException, this);
8241 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8245 fireKey : function(e){
8246 if(e.isNavKeyPress() && !this.list.isVisible()){
8247 this.fireEvent("specialkey", this, e);
8252 onResize: function(w, h){
8253 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8255 // if(typeof w != 'number'){
8256 // // we do not handle it!?!?
8259 // var tw = this.trigger.getWidth();
8260 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8261 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8263 // this.inputEl().setWidth( this.adjustWidth('input', x));
8265 // //this.trigger.setStyle('left', x+'px');
8267 // if(this.list && this.listWidth === undefined){
8268 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8269 // this.list.setWidth(lw);
8270 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8278 * Allow or prevent the user from directly editing the field text. If false is passed,
8279 * the user will only be able to select from the items defined in the dropdown list. This method
8280 * is the runtime equivalent of setting the 'editable' config option at config time.
8281 * @param {Boolean} value True to allow the user to directly edit the field text
8283 setEditable : function(value){
8284 if(value == this.editable){
8287 this.editable = value;
8289 this.inputEl().dom.setAttribute('readOnly', true);
8290 this.inputEl().on('mousedown', this.onTriggerClick, this);
8291 this.inputEl().addClass('x-combo-noedit');
8293 this.inputEl().dom.setAttribute('readOnly', false);
8294 this.inputEl().un('mousedown', this.onTriggerClick, this);
8295 this.inputEl().removeClass('x-combo-noedit');
8301 onBeforeLoad : function(combo,opts){
8306 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8308 this.restrictHeight();
8309 this.selectedIndex = -1;
8313 onLoad : function(){
8315 this.hasQuery = false;
8321 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8322 this.loading.hide();
8325 if(this.store.getCount() > 0){
8327 this.restrictHeight();
8328 if(this.lastQuery == this.allQuery){
8330 this.inputEl().dom.select();
8332 if(!this.selectByValue(this.value, true)){
8333 this.select(0, true);
8337 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8338 this.taTask.delay(this.typeAheadDelay);
8342 this.onEmptyResults();
8348 onLoadException : function()
8350 this.hasQuery = false;
8352 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8353 this.loading.hide();
8357 Roo.log(this.store.reader.jsonData);
8358 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8360 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8366 onTypeAhead : function(){
8367 if(this.store.getCount() > 0){
8368 var r = this.store.getAt(0);
8369 var newValue = r.data[this.displayField];
8370 var len = newValue.length;
8371 var selStart = this.getRawValue().length;
8373 if(selStart != len){
8374 this.setRawValue(newValue);
8375 this.selectText(selStart, newValue.length);
8381 onSelect : function(record, index){
8383 if(this.fireEvent('beforeselect', this, record, index) !== false){
8385 this.setFromData(index > -1 ? record.data : false);
8388 this.fireEvent('select', this, record, index);
8393 * Returns the currently selected field value or empty string if no value is set.
8394 * @return {String} value The selected value
8396 getValue : function(){
8399 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8402 if(this.valueField){
8403 return typeof this.value != 'undefined' ? this.value : '';
8405 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8410 * Clears any text/value currently set in the field
8412 clearValue : function(){
8413 if(this.hiddenField){
8414 this.hiddenField.dom.value = '';
8417 this.setRawValue('');
8418 this.lastSelectionText = '';
8423 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8424 * will be displayed in the field. If the value does not match the data value of an existing item,
8425 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8426 * Otherwise the field will be blank (although the value will still be set).
8427 * @param {String} value The value to match
8429 setValue : function(v){
8436 if(this.valueField){
8437 var r = this.findRecord(this.valueField, v);
8439 text = r.data[this.displayField];
8440 }else if(this.valueNotFoundText !== undefined){
8441 text = this.valueNotFoundText;
8444 this.lastSelectionText = text;
8445 if(this.hiddenField){
8446 this.hiddenField.dom.value = v;
8448 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8452 * @property {Object} the last set data for the element
8457 * Sets the value of the field based on a object which is related to the record format for the store.
8458 * @param {Object} value the value to set as. or false on reset?
8460 setFromData : function(o){
8467 var dv = ''; // display value
8468 var vv = ''; // value value..
8470 if (this.displayField) {
8471 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8473 // this is an error condition!!!
8474 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8477 if(this.valueField){
8478 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8481 if(this.hiddenField){
8482 this.hiddenField.dom.value = vv;
8484 this.lastSelectionText = dv;
8485 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8489 // no hidden field.. - we store the value in 'value', but still display
8490 // display field!!!!
8491 this.lastSelectionText = dv;
8492 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8499 // overridden so that last data is reset..
8500 this.setValue(this.originalValue);
8501 this.clearInvalid();
8502 this.lastData = false;
8504 this.view.clearSelections();
8508 findRecord : function(prop, value){
8510 if(this.store.getCount() > 0){
8511 this.store.each(function(r){
8512 if(r.data[prop] == value){
8524 // returns hidden if it's set..
8525 if (!this.rendered) {return ''};
8526 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8530 onViewMove : function(e, t){
8531 this.inKeyMode = false;
8535 onViewOver : function(e, t){
8536 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8539 var item = this.view.findItemFromChild(t);
8541 var index = this.view.indexOf(item);
8542 this.select(index, false);
8547 onViewClick : function(doFocus)
8549 var index = this.view.getSelectedIndexes()[0];
8550 var r = this.store.getAt(index);
8552 this.onSelect(r, index);
8554 if(doFocus !== false && !this.blockFocus){
8555 this.inputEl().focus();
8560 restrictHeight : function(){
8561 //this.innerList.dom.style.height = '';
8562 //var inner = this.innerList.dom;
8563 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8564 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8565 //this.list.beginUpdate();
8566 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8567 this.list.alignTo(this.inputEl(), this.listAlign);
8568 //this.list.endUpdate();
8572 onEmptyResults : function(){
8577 * Returns true if the dropdown list is expanded, else false.
8579 isExpanded : function(){
8580 return this.list.isVisible();
8584 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8585 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8586 * @param {String} value The data value of the item to select
8587 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8588 * selected item if it is not currently in view (defaults to true)
8589 * @return {Boolean} True if the value matched an item in the list, else false
8591 selectByValue : function(v, scrollIntoView){
8592 if(v !== undefined && v !== null){
8593 var r = this.findRecord(this.valueField || this.displayField, v);
8595 this.select(this.store.indexOf(r), scrollIntoView);
8603 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8604 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8605 * @param {Number} index The zero-based index of the list item to select
8606 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8607 * selected item if it is not currently in view (defaults to true)
8609 select : function(index, scrollIntoView){
8610 this.selectedIndex = index;
8611 this.view.select(index);
8612 if(scrollIntoView !== false){
8613 var el = this.view.getNode(index);
8615 //this.innerList.scrollChildIntoView(el, false);
8622 selectNext : function(){
8623 var ct = this.store.getCount();
8625 if(this.selectedIndex == -1){
8627 }else if(this.selectedIndex < ct-1){
8628 this.select(this.selectedIndex+1);
8634 selectPrev : function(){
8635 var ct = this.store.getCount();
8637 if(this.selectedIndex == -1){
8639 }else if(this.selectedIndex != 0){
8640 this.select(this.selectedIndex-1);
8646 onKeyUp : function(e){
8647 if(this.editable !== false && !e.isSpecialKey()){
8648 this.lastKey = e.getKey();
8649 this.dqTask.delay(this.queryDelay);
8654 validateBlur : function(){
8655 return !this.list || !this.list.isVisible();
8659 initQuery : function(){
8660 this.doQuery(this.getRawValue());
8664 doForce : function(){
8665 if(this.el.dom.value.length > 0){
8667 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8673 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8674 * query allowing the query action to be canceled if needed.
8675 * @param {String} query The SQL query to execute
8676 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8677 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8678 * saved in the current store (defaults to false)
8680 doQuery : function(q, forceAll){
8682 if(q === undefined || q === null){
8691 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8696 forceAll = qe.forceAll;
8697 if(forceAll === true || (q.length >= this.minChars)){
8699 this.hasQuery = true;
8701 if(this.lastQuery != q || this.alwaysQuery){
8703 if(this.mode == 'local'){
8704 this.selectedIndex = -1;
8706 this.store.clearFilter();
8708 this.store.filter(this.displayField, q);
8712 this.store.baseParams[this.queryParam] = q;
8714 var options = {params : this.getParams(q)};
8718 options.params.start = this.page * this.pageSize;
8721 this.store.load(options);
8725 this.selectedIndex = -1;
8730 this.loadNext = false;
8734 getParams : function(q){
8736 //p[this.queryParam] = q;
8740 p.limit = this.pageSize;
8746 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8748 collapse : function(){
8749 if(!this.isExpanded()){
8754 Roo.get(document).un('mousedown', this.collapseIf, this);
8755 Roo.get(document).un('mousewheel', this.collapseIf, this);
8756 if (!this.editable) {
8757 Roo.get(document).un('keydown', this.listKeyPress, this);
8759 this.fireEvent('collapse', this);
8763 collapseIf : function(e){
8764 var in_combo = e.within(this.el);
8765 var in_list = e.within(this.list);
8767 if (in_combo || in_list) {
8768 //e.stopPropagation();
8777 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8779 expand : function(){
8781 if(this.isExpanded() || !this.hasFocus){
8785 this.list.alignTo(this.inputEl(), this.listAlign);
8787 Roo.get(document).on('mousedown', this.collapseIf, this);
8788 Roo.get(document).on('mousewheel', this.collapseIf, this);
8789 if (!this.editable) {
8790 Roo.get(document).on('keydown', this.listKeyPress, this);
8793 this.fireEvent('expand', this);
8797 // Implements the default empty TriggerField.onTriggerClick function
8798 onTriggerClick : function()
8800 Roo.log('trigger click');
8807 this.loadNext = false;
8809 if(this.isExpanded()){
8811 if (!this.blockFocus) {
8812 this.inputEl().focus();
8816 this.hasFocus = true;
8817 if(this.triggerAction == 'all') {
8818 this.doQuery(this.allQuery, true);
8820 this.doQuery(this.getRawValue());
8822 if (!this.blockFocus) {
8823 this.inputEl().focus();
8827 listKeyPress : function(e)
8829 //Roo.log('listkeypress');
8830 // scroll to first matching element based on key pres..
8831 if (e.isSpecialKey()) {
8834 var k = String.fromCharCode(e.getKey()).toUpperCase();
8837 var csel = this.view.getSelectedNodes();
8838 var cselitem = false;
8840 var ix = this.view.indexOf(csel[0]);
8841 cselitem = this.store.getAt(ix);
8842 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8848 this.store.each(function(v) {
8850 // start at existing selection.
8851 if (cselitem.id == v.id) {
8857 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8858 match = this.store.indexOf(v);
8864 if (match === false) {
8865 return true; // no more action?
8868 this.view.select(match);
8869 var sn = Roo.get(this.view.getSelectedNodes()[0])
8870 //sn.scrollIntoView(sn.dom.parentNode, false);
8873 onViewScroll : function(e, t){
8875 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8879 this.hasQuery = true;
8881 this.loading = this.list.select('.loading', true).first();
8883 if(this.loading === null){
8884 this.list.createChild({
8886 cls: 'loading select2-more-results select2-active',
8887 html: 'Loading more results...'
8890 this.loading = this.list.select('.loading', true).first();
8892 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8894 this.loading.hide();
8897 this.loading.show();
8902 this.loadNext = true;
8904 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8909 addItem : function(o)
8911 var dv = ''; // display value
8913 if (this.displayField) {
8914 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8916 // this is an error condition!!!
8917 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8924 var choice = this.choices.createChild({
8926 cls: 'select2-search-choice',
8935 cls: 'select2-search-choice-close',
8940 }, this.searchField);
8942 var close = choice.select('a.select2-search-choice-close', true).first()
8944 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8951 this.inputEl().dom.value = '';
8955 onRemoveItem : function(e, _self, o)
8957 Roo.log('remove item');
8958 var index = this.item.indexOf(o.data) * 1;
8961 Roo.log('not this item?!');
8965 this.item.splice(index, 1);
8970 this.fireEvent('remove', this);
8974 syncValue : function()
8976 if(!this.item.length){
8983 Roo.each(this.item, function(i){
8984 if(_this.valueField){
8985 value.push(i[_this.valueField]);
8992 this.value = value.join(',');
8994 if(this.hiddenField){
8995 this.hiddenField.dom.value = this.value;
8999 clearItem : function()
9007 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9017 * @cfg {Boolean} grow
9021 * @cfg {Number} growMin
9025 * @cfg {Number} growMax
9035 * Ext JS Library 1.1.1
9036 * Copyright(c) 2006-2007, Ext JS, LLC.
9038 * Originally Released Under LGPL - original licence link has changed is not relivant.
9041 * <script type="text/javascript">
9046 * @extends Roo.util.Observable
9047 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9048 * This class also supports single and multi selection modes. <br>
9049 * Create a data model bound view:
9051 var store = new Roo.data.Store(...);
9053 var view = new Roo.View({
9055 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9058 selectedClass: "ydataview-selected",
9062 // listen for node click?
9063 view.on("click", function(vw, index, node, e){
9064 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9068 dataModel.load("foobar.xml");
9070 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9072 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9073 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9075 * Note: old style constructor is still suported (container, template, config)
9079 * @param {Object} config The config object
9082 Roo.View = function(config, depreciated_tpl, depreciated_config){
9084 if (typeof(depreciated_tpl) == 'undefined') {
9085 // new way.. - universal constructor.
9086 Roo.apply(this, config);
9087 this.el = Roo.get(this.el);
9090 this.el = Roo.get(config);
9091 this.tpl = depreciated_tpl;
9092 Roo.apply(this, depreciated_config);
9094 this.wrapEl = this.el.wrap().wrap();
9095 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9098 if(typeof(this.tpl) == "string"){
9099 this.tpl = new Roo.Template(this.tpl);
9101 // support xtype ctors..
9102 this.tpl = new Roo.factory(this.tpl, Roo);
9114 * @event beforeclick
9115 * Fires before a click is processed. Returns false to cancel the default action.
9116 * @param {Roo.View} this
9117 * @param {Number} index The index of the target node
9118 * @param {HTMLElement} node The target node
9119 * @param {Roo.EventObject} e The raw event object
9121 "beforeclick" : true,
9124 * Fires when a template node is clicked.
9125 * @param {Roo.View} this
9126 * @param {Number} index The index of the target node
9127 * @param {HTMLElement} node The target node
9128 * @param {Roo.EventObject} e The raw event object
9133 * Fires when a template node is double clicked.
9134 * @param {Roo.View} this
9135 * @param {Number} index The index of the target node
9136 * @param {HTMLElement} node The target node
9137 * @param {Roo.EventObject} e The raw event object
9141 * @event contextmenu
9142 * Fires when a template node is right clicked.
9143 * @param {Roo.View} this
9144 * @param {Number} index The index of the target node
9145 * @param {HTMLElement} node The target node
9146 * @param {Roo.EventObject} e The raw event object
9148 "contextmenu" : true,
9150 * @event selectionchange
9151 * Fires when the selected nodes change.
9152 * @param {Roo.View} this
9153 * @param {Array} selections Array of the selected nodes
9155 "selectionchange" : true,
9158 * @event beforeselect
9159 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9160 * @param {Roo.View} this
9161 * @param {HTMLElement} node The node to be selected
9162 * @param {Array} selections Array of currently selected nodes
9164 "beforeselect" : true,
9166 * @event preparedata
9167 * Fires on every row to render, to allow you to change the data.
9168 * @param {Roo.View} this
9169 * @param {Object} data to be rendered (change this)
9171 "preparedata" : true
9179 "click": this.onClick,
9180 "dblclick": this.onDblClick,
9181 "contextmenu": this.onContextMenu,
9185 this.selections = [];
9187 this.cmp = new Roo.CompositeElementLite([]);
9189 this.store = Roo.factory(this.store, Roo.data);
9190 this.setStore(this.store, true);
9193 if ( this.footer && this.footer.xtype) {
9195 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9197 this.footer.dataSource = this.store
9198 this.footer.container = fctr;
9199 this.footer = Roo.factory(this.footer, Roo);
9200 fctr.insertFirst(this.el);
9202 // this is a bit insane - as the paging toolbar seems to detach the el..
9203 // dom.parentNode.parentNode.parentNode
9204 // they get detached?
9208 Roo.View.superclass.constructor.call(this);
9213 Roo.extend(Roo.View, Roo.util.Observable, {
9216 * @cfg {Roo.data.Store} store Data store to load data from.
9221 * @cfg {String|Roo.Element} el The container element.
9226 * @cfg {String|Roo.Template} tpl The template used by this View
9230 * @cfg {String} dataName the named area of the template to use as the data area
9231 * Works with domtemplates roo-name="name"
9235 * @cfg {String} selectedClass The css class to add to selected nodes
9237 selectedClass : "x-view-selected",
9239 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9244 * @cfg {String} text to display on mask (default Loading)
9248 * @cfg {Boolean} multiSelect Allow multiple selection
9250 multiSelect : false,
9252 * @cfg {Boolean} singleSelect Allow single selection
9254 singleSelect: false,
9257 * @cfg {Boolean} toggleSelect - selecting
9259 toggleSelect : false,
9262 * Returns the element this view is bound to.
9263 * @return {Roo.Element}
9272 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9274 refresh : function(){
9278 // if we are using something like 'domtemplate', then
9279 // the what gets used is:
9280 // t.applySubtemplate(NAME, data, wrapping data..)
9281 // the outer template then get' applied with
9282 // the store 'extra data'
9283 // and the body get's added to the
9284 // roo-name="data" node?
9285 // <span class='roo-tpl-{name}'></span> ?????
9289 this.clearSelections();
9292 var records = this.store.getRange();
9293 if(records.length < 1) {
9295 // is this valid?? = should it render a template??
9297 this.el.update(this.emptyText);
9301 if (this.dataName) {
9302 this.el.update(t.apply(this.store.meta)); //????
9303 el = this.el.child('.roo-tpl-' + this.dataName);
9306 for(var i = 0, len = records.length; i < len; i++){
9307 var data = this.prepareData(records[i].data, i, records[i]);
9308 this.fireEvent("preparedata", this, data, i, records[i]);
9309 html[html.length] = Roo.util.Format.trim(
9311 t.applySubtemplate(this.dataName, data, this.store.meta) :
9318 el.update(html.join(""));
9319 this.nodes = el.dom.childNodes;
9320 this.updateIndexes(0);
9325 * Function to override to reformat the data that is sent to
9326 * the template for each node.
9327 * DEPRICATED - use the preparedata event handler.
9328 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9329 * a JSON object for an UpdateManager bound view).
9331 prepareData : function(data, index, record)
9333 this.fireEvent("preparedata", this, data, index, record);
9337 onUpdate : function(ds, record){
9338 Roo.log('on update');
9339 this.clearSelections();
9340 var index = this.store.indexOf(record);
9341 var n = this.nodes[index];
9342 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9343 n.parentNode.removeChild(n);
9344 this.updateIndexes(index, index);
9350 onAdd : function(ds, records, index)
9352 Roo.log(['on Add', ds, records, index] );
9353 this.clearSelections();
9354 if(this.nodes.length == 0){
9358 var n = this.nodes[index];
9359 for(var i = 0, len = records.length; i < len; i++){
9360 var d = this.prepareData(records[i].data, i, records[i]);
9362 this.tpl.insertBefore(n, d);
9365 this.tpl.append(this.el, d);
9368 this.updateIndexes(index);
9371 onRemove : function(ds, record, index){
9372 Roo.log('onRemove');
9373 this.clearSelections();
9374 var el = this.dataName ?
9375 this.el.child('.roo-tpl-' + this.dataName) :
9378 el.dom.removeChild(this.nodes[index]);
9379 this.updateIndexes(index);
9383 * Refresh an individual node.
9384 * @param {Number} index
9386 refreshNode : function(index){
9387 this.onUpdate(this.store, this.store.getAt(index));
9390 updateIndexes : function(startIndex, endIndex){
9391 var ns = this.nodes;
9392 startIndex = startIndex || 0;
9393 endIndex = endIndex || ns.length - 1;
9394 for(var i = startIndex; i <= endIndex; i++){
9395 ns[i].nodeIndex = i;
9400 * Changes the data store this view uses and refresh the view.
9401 * @param {Store} store
9403 setStore : function(store, initial){
9404 if(!initial && this.store){
9405 this.store.un("datachanged", this.refresh);
9406 this.store.un("add", this.onAdd);
9407 this.store.un("remove", this.onRemove);
9408 this.store.un("update", this.onUpdate);
9409 this.store.un("clear", this.refresh);
9410 this.store.un("beforeload", this.onBeforeLoad);
9411 this.store.un("load", this.onLoad);
9412 this.store.un("loadexception", this.onLoad);
9416 store.on("datachanged", this.refresh, this);
9417 store.on("add", this.onAdd, this);
9418 store.on("remove", this.onRemove, this);
9419 store.on("update", this.onUpdate, this);
9420 store.on("clear", this.refresh, this);
9421 store.on("beforeload", this.onBeforeLoad, this);
9422 store.on("load", this.onLoad, this);
9423 store.on("loadexception", this.onLoad, this);
9431 * onbeforeLoad - masks the loading area.
9434 onBeforeLoad : function(store,opts)
9436 Roo.log('onBeforeLoad');
9440 this.el.mask(this.mask ? this.mask : "Loading" );
9442 onLoad : function ()
9449 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9450 * @param {HTMLElement} node
9451 * @return {HTMLElement} The template node
9453 findItemFromChild : function(node){
9454 var el = this.dataName ?
9455 this.el.child('.roo-tpl-' + this.dataName,true) :
9458 if(!node || node.parentNode == el){
9461 var p = node.parentNode;
9462 while(p && p != el){
9463 if(p.parentNode == el){
9472 onClick : function(e){
9473 var item = this.findItemFromChild(e.getTarget());
9475 var index = this.indexOf(item);
9476 if(this.onItemClick(item, index, e) !== false){
9477 this.fireEvent("click", this, index, item, e);
9480 this.clearSelections();
9485 onContextMenu : function(e){
9486 var item = this.findItemFromChild(e.getTarget());
9488 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9493 onDblClick : function(e){
9494 var item = this.findItemFromChild(e.getTarget());
9496 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9500 onItemClick : function(item, index, e)
9502 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9505 if (this.toggleSelect) {
9506 var m = this.isSelected(item) ? 'unselect' : 'select';
9509 _t[m](item, true, false);
9512 if(this.multiSelect || this.singleSelect){
9513 if(this.multiSelect && e.shiftKey && this.lastSelection){
9514 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9516 this.select(item, this.multiSelect && e.ctrlKey);
9517 this.lastSelection = item;
9525 * Get the number of selected nodes.
9528 getSelectionCount : function(){
9529 return this.selections.length;
9533 * Get the currently selected nodes.
9534 * @return {Array} An array of HTMLElements
9536 getSelectedNodes : function(){
9537 return this.selections;
9541 * Get the indexes of the selected nodes.
9544 getSelectedIndexes : function(){
9545 var indexes = [], s = this.selections;
9546 for(var i = 0, len = s.length; i < len; i++){
9547 indexes.push(s[i].nodeIndex);
9553 * Clear all selections
9554 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9556 clearSelections : function(suppressEvent){
9557 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9558 this.cmp.elements = this.selections;
9559 this.cmp.removeClass(this.selectedClass);
9560 this.selections = [];
9562 this.fireEvent("selectionchange", this, this.selections);
9568 * Returns true if the passed node is selected
9569 * @param {HTMLElement/Number} node The node or node index
9572 isSelected : function(node){
9573 var s = this.selections;
9577 node = this.getNode(node);
9578 return s.indexOf(node) !== -1;
9583 * @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
9584 * @param {Boolean} keepExisting (optional) true to keep existing selections
9585 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9587 select : function(nodeInfo, keepExisting, suppressEvent){
9588 if(nodeInfo instanceof Array){
9590 this.clearSelections(true);
9592 for(var i = 0, len = nodeInfo.length; i < len; i++){
9593 this.select(nodeInfo[i], true, true);
9597 var node = this.getNode(nodeInfo);
9598 if(!node || this.isSelected(node)){
9599 return; // already selected.
9602 this.clearSelections(true);
9604 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9605 Roo.fly(node).addClass(this.selectedClass);
9606 this.selections.push(node);
9608 this.fireEvent("selectionchange", this, this.selections);
9616 * @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
9617 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9618 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9620 unselect : function(nodeInfo, keepExisting, suppressEvent)
9622 if(nodeInfo instanceof Array){
9623 Roo.each(this.selections, function(s) {
9624 this.unselect(s, nodeInfo);
9628 var node = this.getNode(nodeInfo);
9629 if(!node || !this.isSelected(node)){
9630 Roo.log("not selected");
9631 return; // not selected.
9635 Roo.each(this.selections, function(s) {
9637 Roo.fly(node).removeClass(this.selectedClass);
9644 this.selections= ns;
9645 this.fireEvent("selectionchange", this, this.selections);
9649 * Gets a template node.
9650 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9651 * @return {HTMLElement} The node or null if it wasn't found
9653 getNode : function(nodeInfo){
9654 if(typeof nodeInfo == "string"){
9655 return document.getElementById(nodeInfo);
9656 }else if(typeof nodeInfo == "number"){
9657 return this.nodes[nodeInfo];
9663 * Gets a range template nodes.
9664 * @param {Number} startIndex
9665 * @param {Number} endIndex
9666 * @return {Array} An array of nodes
9668 getNodes : function(start, end){
9669 var ns = this.nodes;
9671 end = typeof end == "undefined" ? ns.length - 1 : end;
9674 for(var i = start; i <= end; i++){
9678 for(var i = start; i >= end; i--){
9686 * Finds the index of the passed node
9687 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9688 * @return {Number} The index of the node or -1
9690 indexOf : function(node){
9691 node = this.getNode(node);
9692 if(typeof node.nodeIndex == "number"){
9693 return node.nodeIndex;
9695 var ns = this.nodes;
9696 for(var i = 0, len = ns.length; i < len; i++){
9707 * based on jquery fullcalendar
9711 Roo.bootstrap = Roo.bootstrap || {};
9713 * @class Roo.bootstrap.Calendar
9714 * @extends Roo.bootstrap.Component
9715 * Bootstrap Calendar class
9716 * @cfg {Boolean} loadMask (true|false) default false
9719 * Create a new Container
9720 * @param {Object} config The config object
9725 Roo.bootstrap.Calendar = function(config){
9726 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9730 * Fires when a date is selected
9731 * @param {DatePicker} this
9732 * @param {Date} date The selected date
9736 * @event monthchange
9737 * Fires when the displayed month changes
9738 * @param {DatePicker} this
9739 * @param {Date} date The selected month
9741 'monthchange': true,
9744 * Fires when mouse over an event
9745 * @param {Calendar} this
9746 * @param {event} Event
9751 * Fires when the mouse leaves an
9752 * @param {Calendar} this
9758 * Fires when the mouse click an
9759 * @param {Calendar} this
9768 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9771 * @cfg {Number} startDay
9772 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9778 getAutoCreate : function(){
9781 var fc_button = function(name, corner, style, content ) {
9782 return Roo.apply({},{
9784 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9786 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9789 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9797 style : 'width:100%',
9804 cls : 'fc-header-left',
9806 fc_button('prev', 'left', 'arrow', '‹' ),
9807 fc_button('next', 'right', 'arrow', '›' ),
9808 { tag: 'span', cls: 'fc-header-space' },
9809 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9817 cls : 'fc-header-center',
9821 cls: 'fc-header-title',
9824 html : 'month / year'
9832 cls : 'fc-header-right',
9834 /* fc_button('month', 'left', '', 'month' ),
9835 fc_button('week', '', '', 'week' ),
9836 fc_button('day', 'right', '', 'day' )
9848 var cal_heads = function() {
9850 // fixme - handle this.
9852 for (var i =0; i < Date.dayNames.length; i++) {
9853 var d = Date.dayNames[i];
9856 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9857 html : d.substring(0,3)
9861 ret[0].cls += ' fc-first';
9862 ret[6].cls += ' fc-last';
9865 var cal_cell = function(n) {
9868 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9873 cls: 'fc-day-number',
9877 cls: 'fc-day-content',
9881 style: 'position: relative;' // height: 17px;
9893 var cal_rows = function() {
9896 for (var r = 0; r < 6; r++) {
9903 for (var i =0; i < Date.dayNames.length; i++) {
9904 var d = Date.dayNames[i];
9905 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9908 row.cn[0].cls+=' fc-first';
9909 row.cn[0].cn[0].style = 'min-height:90px';
9910 row.cn[6].cls+=' fc-last';
9914 ret[0].cls += ' fc-first';
9915 ret[4].cls += ' fc-prev-last';
9916 ret[5].cls += ' fc-last';
9923 cls: 'fc-border-separate',
9924 style : 'width:100%',
9932 cls : 'fc-first fc-last',
9951 style : "position: relative;",
9954 cls : 'fc-view fc-view-month fc-grid',
9955 style : 'position: relative',
9956 unselectable : 'on',
9959 cls : 'fc-event-container',
9960 style : 'position:absolute;z-index:8;top:0;left:0;'
9978 initEvents : function()
9981 throw "can not find store for calendar";
9987 style: "text-align:center",
9991 style: "background-color:white;width:50%;margin:250 auto",
9995 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10006 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10008 var size = this.el.select('.fc-content', true).first().getSize();
10009 this.maskEl.setSize(size.width, size.height);
10010 this.maskEl.enableDisplayMode("block");
10011 if(!this.loadMask){
10012 this.maskEl.hide();
10015 this.store = Roo.factory(this.store, Roo.data);
10016 this.store.on('load', this.onLoad, this);
10017 this.store.on('beforeload', this.onBeforeLoad, this);
10021 this.cells = this.el.select('.fc-day',true);
10022 //Roo.log(this.cells);
10023 this.textNodes = this.el.query('.fc-day-number');
10024 this.cells.addClassOnOver('fc-state-hover');
10026 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10027 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10028 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10029 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10031 this.on('monthchange', this.onMonthChange, this);
10033 this.update(new Date().clearTime());
10036 resize : function() {
10037 var sz = this.el.getSize();
10039 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10040 this.el.select('.fc-day-content div',true).setHeight(34);
10045 showPrevMonth : function(e){
10046 this.update(this.activeDate.add("mo", -1));
10048 showToday : function(e){
10049 this.update(new Date().clearTime());
10052 showNextMonth : function(e){
10053 this.update(this.activeDate.add("mo", 1));
10057 showPrevYear : function(){
10058 this.update(this.activeDate.add("y", -1));
10062 showNextYear : function(){
10063 this.update(this.activeDate.add("y", 1));
10068 update : function(date)
10070 var vd = this.activeDate;
10071 this.activeDate = date;
10072 // if(vd && this.el){
10073 // var t = date.getTime();
10074 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10075 // Roo.log('using add remove');
10077 // this.fireEvent('monthchange', this, date);
10079 // this.cells.removeClass("fc-state-highlight");
10080 // this.cells.each(function(c){
10081 // if(c.dateValue == t){
10082 // c.addClass("fc-state-highlight");
10083 // setTimeout(function(){
10084 // try{c.dom.firstChild.focus();}catch(e){}
10094 var days = date.getDaysInMonth();
10096 var firstOfMonth = date.getFirstDateOfMonth();
10097 var startingPos = firstOfMonth.getDay()-this.startDay;
10099 if(startingPos < this.startDay){
10103 var pm = date.add(Date.MONTH, -1);
10104 var prevStart = pm.getDaysInMonth()-startingPos;
10106 this.cells = this.el.select('.fc-day',true);
10107 this.textNodes = this.el.query('.fc-day-number');
10108 this.cells.addClassOnOver('fc-state-hover');
10110 var cells = this.cells.elements;
10111 var textEls = this.textNodes;
10113 Roo.each(cells, function(cell){
10114 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10117 days += startingPos;
10119 // convert everything to numbers so it's fast
10120 var day = 86400000;
10121 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10124 //Roo.log(prevStart);
10126 var today = new Date().clearTime().getTime();
10127 var sel = date.clearTime().getTime();
10128 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10129 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10130 var ddMatch = this.disabledDatesRE;
10131 var ddText = this.disabledDatesText;
10132 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10133 var ddaysText = this.disabledDaysText;
10134 var format = this.format;
10136 var setCellClass = function(cal, cell){
10138 //Roo.log('set Cell Class');
10140 var t = d.getTime();
10144 cell.dateValue = t;
10146 cell.className += " fc-today";
10147 cell.className += " fc-state-highlight";
10148 cell.title = cal.todayText;
10151 // disable highlight in other month..
10152 //cell.className += " fc-state-highlight";
10157 cell.className = " fc-state-disabled";
10158 cell.title = cal.minText;
10162 cell.className = " fc-state-disabled";
10163 cell.title = cal.maxText;
10167 if(ddays.indexOf(d.getDay()) != -1){
10168 cell.title = ddaysText;
10169 cell.className = " fc-state-disabled";
10172 if(ddMatch && format){
10173 var fvalue = d.dateFormat(format);
10174 if(ddMatch.test(fvalue)){
10175 cell.title = ddText.replace("%0", fvalue);
10176 cell.className = " fc-state-disabled";
10180 if (!cell.initialClassName) {
10181 cell.initialClassName = cell.dom.className;
10184 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10189 for(; i < startingPos; i++) {
10190 textEls[i].innerHTML = (++prevStart);
10191 d.setDate(d.getDate()+1);
10193 cells[i].className = "fc-past fc-other-month";
10194 setCellClass(this, cells[i]);
10199 for(; i < days; i++){
10200 intDay = i - startingPos + 1;
10201 textEls[i].innerHTML = (intDay);
10202 d.setDate(d.getDate()+1);
10204 cells[i].className = ''; // "x-date-active";
10205 setCellClass(this, cells[i]);
10209 for(; i < 42; i++) {
10210 textEls[i].innerHTML = (++extraDays);
10211 d.setDate(d.getDate()+1);
10213 cells[i].className = "fc-future fc-other-month";
10214 setCellClass(this, cells[i]);
10217 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10219 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10221 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10222 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10224 if(totalRows != 6){
10225 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10226 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10229 this.fireEvent('monthchange', this, date);
10233 if(!this.internalRender){
10234 var main = this.el.dom.firstChild;
10235 var w = main.offsetWidth;
10236 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10237 Roo.fly(main).setWidth(w);
10238 this.internalRender = true;
10239 // opera does not respect the auto grow header center column
10240 // then, after it gets a width opera refuses to recalculate
10241 // without a second pass
10242 if(Roo.isOpera && !this.secondPass){
10243 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10244 this.secondPass = true;
10245 this.update.defer(10, this, [date]);
10252 findCell : function(dt) {
10253 dt = dt.clearTime().getTime();
10255 this.cells.each(function(c){
10256 //Roo.log("check " +c.dateValue + '?=' + dt);
10257 if(c.dateValue == dt){
10267 findCells : function(ev) {
10268 var s = ev.start.clone().clearTime().getTime();
10270 var e= ev.end.clone().clearTime().getTime();
10273 this.cells.each(function(c){
10274 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10276 if(c.dateValue > e){
10279 if(c.dateValue < s){
10288 findBestRow: function(cells)
10292 for (var i =0 ; i < cells.length;i++) {
10293 ret = Math.max(cells[i].rows || 0,ret);
10300 addItem : function(ev)
10302 // look for vertical location slot in
10303 var cells = this.findCells(ev);
10305 ev.row = this.findBestRow(cells);
10307 // work out the location.
10311 for(var i =0; i < cells.length; i++) {
10319 if (crow.start.getY() == cells[i].getY()) {
10321 crow.end = cells[i];
10337 for (var i = 0; i < cells.length;i++) {
10338 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10342 this.calevents.push(ev);
10345 clearEvents: function() {
10347 if(!this.calevents){
10351 Roo.each(this.cells.elements, function(c){
10355 Roo.each(this.calevents, function(e) {
10356 Roo.each(e.els, function(el) {
10357 el.un('mouseenter' ,this.onEventEnter, this);
10358 el.un('mouseleave' ,this.onEventLeave, this);
10365 renderEvents: function()
10367 // first make sure there is enough space..
10369 this.cells.each(function(c) {
10371 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10374 for (var e = 0; e < this.calevents.length; e++) {
10375 var ev = this.calevents[e];
10376 var cells = ev.cells;
10377 var rows = ev.rows;
10379 for(var i =0; i < rows.length; i++) {
10382 // how many rows should it span..
10385 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10386 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10388 unselectable : "on",
10391 cls: 'fc-event-inner',
10395 // cls: 'fc-event-time',
10396 // html : cells.length > 1 ? '' : ev.time
10400 cls: 'fc-event-title',
10401 html : String.format('{0}', ev.title)
10408 cls: 'ui-resizable-handle ui-resizable-e',
10409 html : '  '
10415 cfg.cls += ' fc-event-start';
10417 if ((i+1) == rows.length) {
10418 cfg.cls += ' fc-event-end';
10421 var ctr = this.el.select('.fc-event-container',true).first();
10422 var cg = ctr.createChild(cfg);
10424 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10425 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10426 cg.on('click', this.onEventClick, this, ev);
10430 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10431 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10433 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10434 cg.setWidth(ebox.right - sbox.x -2);
10442 onEventEnter: function (e, el,event,d) {
10443 this.fireEvent('evententer', this, el, event);
10446 onEventLeave: function (e, el,event,d) {
10447 this.fireEvent('eventleave', this, el, event);
10450 onEventClick: function (e, el,event,d) {
10451 this.fireEvent('eventclick', this, el, event);
10454 onMonthChange: function () {
10458 onLoad: function ()
10460 this.calevents = [];
10463 if(this.store.getCount() > 0){
10464 this.store.data.each(function(d){
10467 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10468 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10469 time : d.data.start_time,
10470 title : d.data.title,
10471 description : d.data.description,
10472 venue : d.data.venue
10477 this.renderEvents();
10480 this.maskEl.hide();
10484 onBeforeLoad: function()
10486 this.clearEvents();
10489 this.maskEl.show();
10503 * @class Roo.bootstrap.Popover
10504 * @extends Roo.bootstrap.Component
10505 * Bootstrap Popover class
10506 * @cfg {String} html contents of the popover (or false to use children..)
10507 * @cfg {String} title of popover (or false to hide)
10508 * @cfg {String} placement how it is placed
10509 * @cfg {String} trigger click || hover (or false to trigger manually)
10510 * @cfg {String} over what (parent or false to trigger manually.)
10513 * Create a new Popover
10514 * @param {Object} config The config object
10517 Roo.bootstrap.Popover = function(config){
10518 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10521 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10523 title: 'Fill in a title',
10526 placement : 'right',
10527 trigger : 'hover', // hover
10531 can_build_overlaid : false,
10533 getChildContainer : function()
10535 return this.el.select('.popover-content',true).first();
10538 getAutoCreate : function(){
10539 Roo.log('make popover?');
10541 cls : 'popover roo-dynamic',
10542 style: 'display:block',
10548 cls : 'popover-inner',
10552 cls: 'popover-title',
10556 cls : 'popover-content',
10567 setTitle: function(str)
10569 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10571 setContent: function(str)
10573 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10575 // as it get's added to the bottom of the page.
10576 onRender : function(ct, position)
10578 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10580 var cfg = Roo.apply({}, this.getAutoCreate());
10584 cfg.cls += ' ' + this.cls;
10587 cfg.style = this.style;
10589 Roo.log("adding to ")
10590 this.el = Roo.get(document.body).createChild(cfg, position);
10596 initEvents : function()
10598 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10599 this.el.enableDisplayMode('block');
10601 if (this.over === false) {
10604 if (this.triggers === false) {
10607 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10608 var triggers = this.trigger ? this.trigger.split(' ') : [];
10609 Roo.each(triggers, function(trigger) {
10611 if (trigger == 'click') {
10612 on_el.on('click', this.toggle, this);
10613 } else if (trigger != 'manual') {
10614 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10615 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10617 on_el.on(eventIn ,this.enter, this);
10618 on_el.on(eventOut, this.leave, this);
10629 toggle : function () {
10630 this.hoverState == 'in' ? this.leave() : this.enter();
10633 enter : function () {
10636 clearTimeout(this.timeout);
10638 this.hoverState = 'in'
10640 if (!this.delay || !this.delay.show) {
10645 this.timeout = setTimeout(function () {
10646 if (_t.hoverState == 'in') {
10649 }, this.delay.show)
10651 leave : function() {
10652 clearTimeout(this.timeout);
10654 this.hoverState = 'out'
10656 if (!this.delay || !this.delay.hide) {
10661 this.timeout = setTimeout(function () {
10662 if (_t.hoverState == 'out') {
10665 }, this.delay.hide)
10668 show : function (on_el)
10671 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10674 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10675 if (this.html !== false) {
10676 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10678 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10679 if (!this.title.length) {
10680 this.el.select('.popover-title',true).hide();
10683 var placement = typeof this.placement == 'function' ?
10684 this.placement.call(this, this.el, on_el) :
10687 var autoToken = /\s?auto?\s?/i;
10688 var autoPlace = autoToken.test(placement);
10690 placement = placement.replace(autoToken, '') || 'top';
10694 //this.el.setXY([0,0]);
10696 this.el.dom.style.display='block';
10697 this.el.addClass(placement);
10699 //this.el.appendTo(on_el);
10701 var p = this.getPosition();
10702 var box = this.el.getBox();
10707 var align = Roo.bootstrap.Popover.alignment[placement]
10708 this.el.alignTo(on_el, align[0],align[1]);
10709 //var arrow = this.el.select('.arrow',true).first();
10710 //arrow.set(align[2],
10712 this.el.addClass('in');
10713 this.hoverState = null;
10715 if (this.el.hasClass('fade')) {
10722 this.el.setXY([0,0]);
10723 this.el.removeClass('in');
10730 Roo.bootstrap.Popover.alignment = {
10731 'left' : ['r-l', [-10,0], 'right'],
10732 'right' : ['l-r', [10,0], 'left'],
10733 'bottom' : ['t-b', [0,10], 'top'],
10734 'top' : [ 'b-t', [0,-10], 'bottom']
10745 * @class Roo.bootstrap.Progress
10746 * @extends Roo.bootstrap.Component
10747 * Bootstrap Progress class
10748 * @cfg {Boolean} striped striped of the progress bar
10749 * @cfg {Boolean} active animated of the progress bar
10753 * Create a new Progress
10754 * @param {Object} config The config object
10757 Roo.bootstrap.Progress = function(config){
10758 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10761 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10766 getAutoCreate : function(){
10774 cfg.cls += ' progress-striped';
10778 cfg.cls += ' active';
10797 * @class Roo.bootstrap.ProgressBar
10798 * @extends Roo.bootstrap.Component
10799 * Bootstrap ProgressBar class
10800 * @cfg {Number} aria_valuenow aria-value now
10801 * @cfg {Number} aria_valuemin aria-value min
10802 * @cfg {Number} aria_valuemax aria-value max
10803 * @cfg {String} label label for the progress bar
10804 * @cfg {String} panel (success | info | warning | danger )
10805 * @cfg {String} role role of the progress bar
10806 * @cfg {String} sr_only text
10810 * Create a new ProgressBar
10811 * @param {Object} config The config object
10814 Roo.bootstrap.ProgressBar = function(config){
10815 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10818 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10822 aria_valuemax : 100,
10828 getAutoCreate : function()
10833 cls: 'progress-bar',
10834 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10846 cfg.role = this.role;
10849 if(this.aria_valuenow){
10850 cfg['aria-valuenow'] = this.aria_valuenow;
10853 if(this.aria_valuemin){
10854 cfg['aria-valuemin'] = this.aria_valuemin;
10857 if(this.aria_valuemax){
10858 cfg['aria-valuemax'] = this.aria_valuemax;
10861 if(this.label && !this.sr_only){
10862 cfg.html = this.label;
10866 cfg.cls += ' progress-bar-' + this.panel;
10872 update : function(aria_valuenow)
10874 this.aria_valuenow = aria_valuenow;
10876 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10891 * @class Roo.bootstrap.TabPanel
10892 * @extends Roo.bootstrap.Component
10893 * Bootstrap TabPanel class
10894 * @cfg {Boolean} active panel active
10895 * @cfg {String} html panel content
10896 * @cfg {String} tabId tab relate id
10900 * Create a new TabPanel
10901 * @param {Object} config The config object
10904 Roo.bootstrap.TabPanel = function(config){
10905 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10908 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10914 getAutoCreate : function(){
10918 html: this.html || ''
10922 cfg.cls += ' active';
10926 cfg.tabId = this.tabId;
10944 * @class Roo.bootstrap.DateField
10945 * @extends Roo.bootstrap.Input
10946 * Bootstrap DateField class
10947 * @cfg {Number} weekStart default 0
10948 * @cfg {Number} weekStart default 0
10949 * @cfg {Number} viewMode default empty, (months|years)
10950 * @cfg {Number} minViewMode default empty, (months|years)
10951 * @cfg {Number} startDate default -Infinity
10952 * @cfg {Number} endDate default Infinity
10953 * @cfg {Boolean} todayHighlight default false
10954 * @cfg {Boolean} todayBtn default false
10955 * @cfg {Boolean} calendarWeeks default false
10956 * @cfg {Object} daysOfWeekDisabled default empty
10958 * @cfg {Boolean} keyboardNavigation default true
10959 * @cfg {String} language default en
10962 * Create a new DateField
10963 * @param {Object} config The config object
10966 Roo.bootstrap.DateField = function(config){
10967 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10971 * Fires when this field show.
10972 * @param {Roo.bootstrap.DateField} this
10973 * @param {Mixed} date The date value
10978 * Fires when this field hide.
10979 * @param {Roo.bootstrap.DateField} this
10980 * @param {Mixed} date The date value
10985 * Fires when select a date.
10986 * @param {Roo.bootstrap.DateField} this
10987 * @param {Mixed} date The date value
10993 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
10996 * @cfg {String} format
10997 * The default date format string which can be overriden for localization support. The format must be
10998 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11002 * @cfg {String} altFormats
11003 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11004 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11006 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11014 todayHighlight : false,
11020 keyboardNavigation: true,
11022 calendarWeeks: false,
11024 startDate: -Infinity,
11028 daysOfWeekDisabled: [],
11032 UTCDate: function()
11034 return new Date(Date.UTC.apply(Date, arguments));
11037 UTCToday: function()
11039 var today = new Date();
11040 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11043 getDate: function() {
11044 var d = this.getUTCDate();
11045 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11048 getUTCDate: function() {
11052 setDate: function(d) {
11053 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11056 setUTCDate: function(d) {
11058 this.setValue(this.formatDate(this.date));
11061 onRender: function(ct, position)
11064 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11066 this.language = this.language || 'en';
11067 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11068 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11070 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11071 this.format = this.format || 'm/d/y';
11072 this.isInline = false;
11073 this.isInput = true;
11074 this.component = this.el.select('.add-on', true).first() || false;
11075 this.component = (this.component && this.component.length === 0) ? false : this.component;
11076 this.hasInput = this.component && this.inputEL().length;
11078 if (typeof(this.minViewMode === 'string')) {
11079 switch (this.minViewMode) {
11081 this.minViewMode = 1;
11084 this.minViewMode = 2;
11087 this.minViewMode = 0;
11092 if (typeof(this.viewMode === 'string')) {
11093 switch (this.viewMode) {
11106 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11108 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11110 this.picker().on('mousedown', this.onMousedown, this);
11111 this.picker().on('click', this.onClick, this);
11113 this.picker().addClass('datepicker-dropdown');
11115 this.startViewMode = this.viewMode;
11118 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11119 if(!this.calendarWeeks){
11124 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11125 v.attr('colspan', function(i, val){
11126 return parseInt(val) + 1;
11131 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11133 this.setStartDate(this.startDate);
11134 this.setEndDate(this.endDate);
11136 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11143 if(this.isInline) {
11148 picker : function()
11150 return this.el.select('.datepicker', true).first();
11153 fillDow: function()
11155 var dowCnt = this.weekStart;
11164 if(this.calendarWeeks){
11172 while (dowCnt < this.weekStart + 7) {
11176 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11180 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11183 fillMonths: function()
11186 var months = this.picker().select('>.datepicker-months td', true).first();
11188 months.dom.innerHTML = '';
11194 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11197 months.createChild(month);
11202 update: function(){
11204 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11206 if (this.date < this.startDate) {
11207 this.viewDate = new Date(this.startDate);
11208 } else if (this.date > this.endDate) {
11209 this.viewDate = new Date(this.endDate);
11211 this.viewDate = new Date(this.date);
11218 var d = new Date(this.viewDate),
11219 year = d.getUTCFullYear(),
11220 month = d.getUTCMonth(),
11221 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11222 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11223 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11224 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11225 currentDate = this.date && this.date.valueOf(),
11226 today = this.UTCToday();
11228 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11230 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11232 // this.picker.select('>tfoot th.today').
11233 // .text(dates[this.language].today)
11234 // .toggle(this.todayBtn !== false);
11236 this.updateNavArrows();
11239 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11241 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11243 prevMonth.setUTCDate(day);
11245 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11247 var nextMonth = new Date(prevMonth);
11249 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11251 nextMonth = nextMonth.valueOf();
11253 var fillMonths = false;
11255 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11257 while(prevMonth.valueOf() < nextMonth) {
11260 if (prevMonth.getUTCDay() === this.weekStart) {
11262 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11270 if(this.calendarWeeks){
11271 // ISO 8601: First week contains first thursday.
11272 // ISO also states week starts on Monday, but we can be more abstract here.
11274 // Start of current week: based on weekstart/current date
11275 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11276 // Thursday of this week
11277 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11278 // First Thursday of year, year from thursday
11279 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11280 // Calendar week: ms between thursdays, div ms per day, div 7 days
11281 calWeek = (th - yth) / 864e5 / 7 + 1;
11283 fillMonths.cn.push({
11291 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11293 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11296 if (this.todayHighlight &&
11297 prevMonth.getUTCFullYear() == today.getFullYear() &&
11298 prevMonth.getUTCMonth() == today.getMonth() &&
11299 prevMonth.getUTCDate() == today.getDate()) {
11300 clsName += ' today';
11303 if (currentDate && prevMonth.valueOf() === currentDate) {
11304 clsName += ' active';
11307 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11308 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11309 clsName += ' disabled';
11312 fillMonths.cn.push({
11314 cls: 'day ' + clsName,
11315 html: prevMonth.getDate()
11318 prevMonth.setDate(prevMonth.getDate()+1);
11321 var currentYear = this.date && this.date.getUTCFullYear();
11322 var currentMonth = this.date && this.date.getUTCMonth();
11324 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11326 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11327 v.removeClass('active');
11329 if(currentYear === year && k === currentMonth){
11330 v.addClass('active');
11333 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11334 v.addClass('disabled');
11340 year = parseInt(year/10, 10) * 10;
11342 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11344 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11347 for (var i = -1; i < 11; i++) {
11348 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11350 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11358 showMode: function(dir) {
11360 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11362 Roo.each(this.picker().select('>div',true).elements, function(v){
11363 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11366 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11371 if(this.isInline) return;
11373 this.picker().removeClass(['bottom', 'top']);
11375 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11377 * place to the top of element!
11381 this.picker().addClass('top');
11382 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11387 this.picker().addClass('bottom');
11389 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11392 parseDate : function(value){
11393 if(!value || value instanceof Date){
11396 var v = Date.parseDate(value, this.format);
11397 if (!v && this.useIso) {
11398 v = Date.parseDate(value, 'Y-m-d');
11400 if(!v && this.altFormats){
11401 if(!this.altFormatsArray){
11402 this.altFormatsArray = this.altFormats.split("|");
11404 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11405 v = Date.parseDate(value, this.altFormatsArray[i]);
11411 formatDate : function(date, fmt){
11412 return (!date || !(date instanceof Date)) ?
11413 date : date.dateFormat(fmt || this.format);
11416 onFocus : function()
11418 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11422 onBlur : function()
11424 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11430 this.picker().show();
11434 this.fireEvent('show', this, this.date);
11439 if(this.isInline) return;
11440 this.picker().hide();
11441 this.viewMode = this.startViewMode;
11444 this.fireEvent('hide', this, this.date);
11448 onMousedown: function(e){
11449 e.stopPropagation();
11450 e.preventDefault();
11453 keyup: function(e){
11454 Roo.bootstrap.DateField.superclass.keyup.call(this);
11459 setValue: function(v){
11460 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11462 this.fireEvent('select', this, this.date);
11466 fireKey: function(e){
11467 if (!this.picker().isVisible()){
11468 if (e.keyCode == 27) // allow escape to hide and re-show picker
11472 var dateChanged = false,
11474 newDate, newViewDate;
11478 e.preventDefault();
11482 if (!this.keyboardNavigation) break;
11483 dir = e.keyCode == 37 ? -1 : 1;
11486 newDate = this.moveYear(this.date, dir);
11487 newViewDate = this.moveYear(this.viewDate, dir);
11488 } else if (e.shiftKey){
11489 newDate = this.moveMonth(this.date, dir);
11490 newViewDate = this.moveMonth(this.viewDate, dir);
11492 newDate = new Date(this.date);
11493 newDate.setUTCDate(this.date.getUTCDate() + dir);
11494 newViewDate = new Date(this.viewDate);
11495 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11497 if (this.dateWithinRange(newDate)){
11498 this.date = newDate;
11499 this.viewDate = newViewDate;
11500 this.setValue(this.formatDate(this.date));
11502 e.preventDefault();
11503 dateChanged = true;
11508 if (!this.keyboardNavigation) break;
11509 dir = e.keyCode == 38 ? -1 : 1;
11511 newDate = this.moveYear(this.date, dir);
11512 newViewDate = this.moveYear(this.viewDate, dir);
11513 } else if (e.shiftKey){
11514 newDate = this.moveMonth(this.date, dir);
11515 newViewDate = this.moveMonth(this.viewDate, dir);
11517 newDate = new Date(this.date);
11518 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11519 newViewDate = new Date(this.viewDate);
11520 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11522 if (this.dateWithinRange(newDate)){
11523 this.date = newDate;
11524 this.viewDate = newViewDate;
11525 this.setValue(this.formatDate(this.date));
11527 e.preventDefault();
11528 dateChanged = true;
11532 this.setValue(this.formatDate(this.date));
11534 e.preventDefault();
11537 this.setValue(this.formatDate(this.date));
11544 onClick: function(e) {
11545 e.stopPropagation();
11546 e.preventDefault();
11548 var target = e.getTarget();
11550 if(target.nodeName.toLowerCase() === 'i'){
11551 target = Roo.get(target).dom.parentNode;
11554 var nodeName = target.nodeName;
11555 var className = target.className;
11556 var html = target.innerHTML;
11558 switch(nodeName.toLowerCase()) {
11560 switch(className) {
11566 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11567 switch(this.viewMode){
11569 this.viewDate = this.moveMonth(this.viewDate, dir);
11573 this.viewDate = this.moveYear(this.viewDate, dir);
11579 var date = new Date();
11580 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11582 this.setValue(this.formatDate(this.date));
11588 if (className.indexOf('disabled') === -1) {
11589 this.viewDate.setUTCDate(1);
11590 if (className.indexOf('month') !== -1) {
11591 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11593 var year = parseInt(html, 10) || 0;
11594 this.viewDate.setUTCFullYear(year);
11603 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11604 var day = parseInt(html, 10) || 1;
11605 var year = this.viewDate.getUTCFullYear(),
11606 month = this.viewDate.getUTCMonth();
11608 if (className.indexOf('old') !== -1) {
11615 } else if (className.indexOf('new') !== -1) {
11623 this.date = this.UTCDate(year, month, day,0,0,0,0);
11624 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11626 this.setValue(this.formatDate(this.date));
11633 setStartDate: function(startDate){
11634 this.startDate = startDate || -Infinity;
11635 if (this.startDate !== -Infinity) {
11636 this.startDate = this.parseDate(this.startDate);
11639 this.updateNavArrows();
11642 setEndDate: function(endDate){
11643 this.endDate = endDate || Infinity;
11644 if (this.endDate !== Infinity) {
11645 this.endDate = this.parseDate(this.endDate);
11648 this.updateNavArrows();
11651 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11652 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11653 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11654 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11656 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11657 return parseInt(d, 10);
11660 this.updateNavArrows();
11663 updateNavArrows: function() {
11664 var d = new Date(this.viewDate),
11665 year = d.getUTCFullYear(),
11666 month = d.getUTCMonth();
11668 Roo.each(this.picker().select('.prev', true).elements, function(v){
11670 switch (this.viewMode) {
11673 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11679 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11686 Roo.each(this.picker().select('.next', true).elements, function(v){
11688 switch (this.viewMode) {
11691 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11697 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11705 moveMonth: function(date, dir){
11706 if (!dir) return date;
11707 var new_date = new Date(date.valueOf()),
11708 day = new_date.getUTCDate(),
11709 month = new_date.getUTCMonth(),
11710 mag = Math.abs(dir),
11712 dir = dir > 0 ? 1 : -1;
11715 // If going back one month, make sure month is not current month
11716 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11718 return new_date.getUTCMonth() == month;
11720 // If going forward one month, make sure month is as expected
11721 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11723 return new_date.getUTCMonth() != new_month;
11725 new_month = month + dir;
11726 new_date.setUTCMonth(new_month);
11727 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11728 if (new_month < 0 || new_month > 11)
11729 new_month = (new_month + 12) % 12;
11731 // For magnitudes >1, move one month at a time...
11732 for (var i=0; i<mag; i++)
11733 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11734 new_date = this.moveMonth(new_date, dir);
11735 // ...then reset the day, keeping it in the new month
11736 new_month = new_date.getUTCMonth();
11737 new_date.setUTCDate(day);
11739 return new_month != new_date.getUTCMonth();
11742 // Common date-resetting loop -- if date is beyond end of month, make it
11745 new_date.setUTCDate(--day);
11746 new_date.setUTCMonth(new_month);
11751 moveYear: function(date, dir){
11752 return this.moveMonth(date, dir*12);
11755 dateWithinRange: function(date){
11756 return date >= this.startDate && date <= this.endDate;
11760 remove: function() {
11761 this.picker().remove();
11766 Roo.apply(Roo.bootstrap.DateField, {
11777 html: '<i class="icon-arrow-left"/>'
11787 html: '<i class="icon-arrow-right"/>'
11829 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11830 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11831 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11832 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11833 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11846 navFnc: 'FullYear',
11851 navFnc: 'FullYear',
11856 Roo.apply(Roo.bootstrap.DateField, {
11860 cls: 'datepicker dropdown-menu',
11864 cls: 'datepicker-days',
11868 cls: 'table-condensed',
11870 Roo.bootstrap.DateField.head,
11874 Roo.bootstrap.DateField.footer
11881 cls: 'datepicker-months',
11885 cls: 'table-condensed',
11887 Roo.bootstrap.DateField.head,
11888 Roo.bootstrap.DateField.content,
11889 Roo.bootstrap.DateField.footer
11896 cls: 'datepicker-years',
11900 cls: 'table-condensed',
11902 Roo.bootstrap.DateField.head,
11903 Roo.bootstrap.DateField.content,
11904 Roo.bootstrap.DateField.footer
11923 * @class Roo.bootstrap.TimeField
11924 * @extends Roo.bootstrap.Input
11925 * Bootstrap DateField class
11929 * Create a new TimeField
11930 * @param {Object} config The config object
11933 Roo.bootstrap.TimeField = function(config){
11934 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11938 * Fires when this field show.
11939 * @param {Roo.bootstrap.DateField} this
11940 * @param {Mixed} date The date value
11945 * Fires when this field hide.
11946 * @param {Roo.bootstrap.DateField} this
11947 * @param {Mixed} date The date value
11952 * Fires when select a date.
11953 * @param {Roo.bootstrap.DateField} this
11954 * @param {Mixed} date The date value
11960 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
11963 * @cfg {String} format
11964 * The default time format string which can be overriden for localization support. The format must be
11965 * valid according to {@link Date#parseDate} (defaults to 'H:i').
11969 onRender: function(ct, position)
11972 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11974 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11976 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11978 this.pop = this.picker().select('>.datepicker-time',true).first();
11979 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
11981 this.picker().on('mousedown', this.onMousedown, this);
11982 this.picker().on('click', this.onClick, this);
11984 this.picker().addClass('datepicker-dropdown');
11989 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11990 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11991 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11992 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11993 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11994 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11998 fireKey: function(e){
11999 if (!this.picker().isVisible()){
12000 if (e.keyCode == 27) // allow escape to hide and re-show picker
12005 e.preventDefault();
12013 this.onTogglePeriod();
12016 this.onIncrementMinutes();
12019 this.onDecrementMinutes();
12028 onClick: function(e) {
12029 e.stopPropagation();
12030 e.preventDefault();
12033 picker : function()
12035 return this.el.select('.datepicker', true).first();
12038 fillTime: function()
12040 var time = this.pop.select('tbody', true).first();
12042 time.dom.innerHTML = '';
12057 cls: 'hours-up glyphicon glyphicon-chevron-up'
12077 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12098 cls: 'timepicker-hour',
12113 cls: 'timepicker-minute',
12128 cls: 'btn btn-primary period',
12150 cls: 'hours-down glyphicon glyphicon-chevron-down'
12170 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12188 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12195 var hours = this.time.getHours();
12196 var minutes = this.time.getMinutes();
12209 hours = hours - 12;
12213 hours = '0' + hours;
12217 minutes = '0' + minutes;
12220 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12221 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12222 this.pop.select('button', true).first().dom.innerHTML = period;
12228 this.picker().removeClass(['bottom', 'top']);
12230 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12232 * place to the top of element!
12236 this.picker().addClass('top');
12237 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12242 this.picker().addClass('bottom');
12244 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12247 onFocus : function()
12249 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12253 onBlur : function()
12255 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12261 this.picker().show();
12266 this.fireEvent('show', this, this.date);
12271 this.picker().hide();
12274 this.fireEvent('hide', this, this.date);
12277 setTime : function()
12280 this.setValue(this.time.format(this.format));
12282 this.fireEvent('select', this, this.date);
12287 onMousedown: function(e){
12288 e.stopPropagation();
12289 e.preventDefault();
12292 onIncrementHours: function()
12294 Roo.log('onIncrementHours');
12295 this.time = this.time.add(Date.HOUR, 1);
12300 onDecrementHours: function()
12302 Roo.log('onDecrementHours');
12303 this.time = this.time.add(Date.HOUR, -1);
12307 onIncrementMinutes: function()
12309 Roo.log('onIncrementMinutes');
12310 this.time = this.time.add(Date.MINUTE, 1);
12314 onDecrementMinutes: function()
12316 Roo.log('onDecrementMinutes');
12317 this.time = this.time.add(Date.MINUTE, -1);
12321 onTogglePeriod: function()
12323 Roo.log('onTogglePeriod');
12324 this.time = this.time.add(Date.HOUR, 12);
12331 Roo.apply(Roo.bootstrap.TimeField, {
12361 cls: 'btn btn-info ok',
12373 Roo.apply(Roo.bootstrap.TimeField, {
12377 cls: 'datepicker dropdown-menu',
12381 cls: 'datepicker-time',
12385 cls: 'table-condensed',
12387 Roo.bootstrap.TimeField.content,
12388 Roo.bootstrap.TimeField.footer
12407 * @class Roo.bootstrap.CheckBox
12408 * @extends Roo.bootstrap.Input
12409 * Bootstrap CheckBox class
12411 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12412 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12413 * @cfg {String} boxLabel The text that appears beside the checkbox
12414 * @cfg {Boolean} checked initnal the element
12417 * Create a new CheckBox
12418 * @param {Object} config The config object
12421 Roo.bootstrap.CheckBox = function(config){
12422 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12427 * Fires when the element is checked or unchecked.
12428 * @param {Roo.bootstrap.CheckBox} this This input
12429 * @param {Boolean} checked The new checked value
12435 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12437 inputType: 'checkbox',
12443 getAutoCreate : function()
12445 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12451 cfg.cls = 'form-group' //input-group
12456 type : this.inputType,
12457 value : (!this.checked) ? this.valueOff : this.inputValue,
12459 placeholder : this.placeholder || ''
12463 if (this.disabled) {
12464 input.disabled=true;
12468 input.checked = this.checked;
12472 input.name = this.name;
12476 input.cls += ' input-' + this.size;
12480 ['xs','sm','md','lg'].map(function(size){
12481 if (settings[size]) {
12482 cfg.cls += ' col-' + size + '-' + settings[size];
12486 var inputblock = input;
12488 if (this.before || this.after) {
12491 cls : 'input-group',
12495 inputblock.cn.push({
12497 cls : 'input-group-addon',
12501 inputblock.cn.push(input);
12503 inputblock.cn.push({
12505 cls : 'input-group-addon',
12512 if (align ==='left' && this.fieldLabel.length) {
12513 Roo.log("left and has label");
12519 cls : 'control-label col-md-' + this.labelWidth,
12520 html : this.fieldLabel
12524 cls : "col-md-" + (12 - this.labelWidth),
12531 } else if ( this.fieldLabel.length) {
12536 tag: this.boxLabel ? 'span' : 'label',
12538 cls: 'control-label box-input-label',
12539 //cls : 'input-group-addon',
12540 html : this.fieldLabel
12550 Roo.log(" no label && no align");
12565 html: this.boxLabel
12574 * return the real input element.
12576 inputEl: function ()
12578 return this.el.select('input.form-box',true).first();
12581 initEvents : function()
12583 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12585 this.inputEl().on('click', this.onClick, this);
12589 onClick : function()
12591 this.setChecked(!this.checked);
12594 setChecked : function(state,suppressEvent)
12596 this.checked = state;
12598 this.inputEl().dom.checked = state;
12600 if(suppressEvent !== true){
12601 this.fireEvent('check', this, state);
12604 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12608 setValue : function(v,suppressEvent)
12610 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12624 * @class Roo.bootstrap.Radio
12625 * @extends Roo.bootstrap.CheckBox
12626 * Bootstrap Radio class
12629 * Create a new Radio
12630 * @param {Object} config The config object
12633 Roo.bootstrap.Radio = function(config){
12634 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12638 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12640 inputType: 'radio',
12644 getAutoCreate : function()
12646 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12652 cfg.cls = 'form-group' //input-group
12657 type : this.inputType,
12658 value : (!this.checked) ? this.valueOff : this.inputValue,
12660 placeholder : this.placeholder || ''
12664 if (this.disabled) {
12665 input.disabled=true;
12669 input.checked = this.checked;
12673 input.name = this.name;
12677 input.cls += ' input-' + this.size;
12681 ['xs','sm','md','lg'].map(function(size){
12682 if (settings[size]) {
12683 cfg.cls += ' col-' + size + '-' + settings[size];
12687 var inputblock = input;
12689 if (this.before || this.after) {
12692 cls : 'input-group',
12696 inputblock.cn.push({
12698 cls : 'input-group-addon',
12702 inputblock.cn.push(input);
12704 inputblock.cn.push({
12706 cls : 'input-group-addon',
12713 if (align ==='left' && this.fieldLabel.length) {
12714 Roo.log("left and has label");
12720 cls : 'control-label col-md-' + this.labelWidth,
12721 html : this.fieldLabel
12725 cls : "col-md-" + (12 - this.labelWidth),
12732 } else if ( this.fieldLabel.length) {
12739 cls: 'control-label box-input-label',
12740 //cls : 'input-group-addon',
12741 html : this.fieldLabel
12751 Roo.log(" no label && no align");
12766 html: this.boxLabel
12774 onClick : function()
12776 this.setChecked(true);
12779 setChecked : function(state,suppressEvent)
12782 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12783 v.dom.checked = false;
12787 this.checked = state;
12788 this.inputEl().dom.checked = state;
12790 if(suppressEvent !== true){
12791 this.fireEvent('check', this, state);
12794 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12798 getGroupValue : function()
12801 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12802 if(v.dom.checked == true){
12803 value = v.dom.value;
12811 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12812 * @return {Mixed} value The field value
12814 getValue : function(){
12815 return this.getGroupValue();
12821 //<script type="text/javascript">
12824 * Based Ext JS Library 1.1.1
12825 * Copyright(c) 2006-2007, Ext JS, LLC.
12831 * @class Roo.HtmlEditorCore
12832 * @extends Roo.Component
12833 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12835 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12838 Roo.HtmlEditorCore = function(config){
12841 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12844 * @event initialize
12845 * Fires when the editor is fully initialized (including the iframe)
12846 * @param {Roo.HtmlEditorCore} this
12851 * Fires when the editor is first receives the focus. Any insertion must wait
12852 * until after this event.
12853 * @param {Roo.HtmlEditorCore} this
12857 * @event beforesync
12858 * Fires before the textarea is updated with content from the editor iframe. Return false
12859 * to cancel the sync.
12860 * @param {Roo.HtmlEditorCore} this
12861 * @param {String} html
12865 * @event beforepush
12866 * Fires before the iframe editor is updated with content from the textarea. Return false
12867 * to cancel the push.
12868 * @param {Roo.HtmlEditorCore} this
12869 * @param {String} html
12874 * Fires when the textarea is updated with content from the editor iframe.
12875 * @param {Roo.HtmlEditorCore} this
12876 * @param {String} html
12881 * Fires when the iframe editor is updated with content from the textarea.
12882 * @param {Roo.HtmlEditorCore} this
12883 * @param {String} html
12888 * @event editorevent
12889 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12890 * @param {Roo.HtmlEditorCore} this
12898 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12902 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12908 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12913 * @cfg {Number} height (in pixels)
12917 * @cfg {Number} width (in pixels)
12922 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12925 stylesheets: false,
12930 // private properties
12931 validationEvent : false,
12933 initialized : false,
12935 sourceEditMode : false,
12936 onFocus : Roo.emptyFn,
12938 hideMode:'offsets',
12946 * Protected method that will not generally be called directly. It
12947 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12948 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12950 getDocMarkup : function(){
12953 Roo.log(this.stylesheets);
12955 // inherit styels from page...??
12956 if (this.stylesheets === false) {
12958 Roo.get(document.head).select('style').each(function(node) {
12959 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12962 Roo.get(document.head).select('link').each(function(node) {
12963 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12966 } else if (!this.stylesheets.length) {
12968 st = '<style type="text/css">' +
12969 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12972 Roo.each(this.stylesheets, function(s) {
12973 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12978 st += '<style type="text/css">' +
12979 'IMG { cursor: pointer } ' +
12983 return '<html><head>' + st +
12984 //<style type="text/css">' +
12985 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12987 ' </head><body class="roo-htmleditor-body"></body></html>';
12991 onRender : function(ct, position)
12994 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12995 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12998 this.el.dom.style.border = '0 none';
12999 this.el.dom.setAttribute('tabIndex', -1);
13000 this.el.addClass('x-hidden hide');
13004 if(Roo.isIE){ // fix IE 1px bogus margin
13005 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13009 this.frameId = Roo.id();
13013 var iframe = this.owner.wrap.createChild({
13015 cls: 'form-control', // bootstrap..
13017 name: this.frameId,
13018 frameBorder : 'no',
13019 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13024 this.iframe = iframe.dom;
13026 this.assignDocWin();
13028 this.doc.designMode = 'on';
13031 this.doc.write(this.getDocMarkup());
13035 var task = { // must defer to wait for browser to be ready
13037 //console.log("run task?" + this.doc.readyState);
13038 this.assignDocWin();
13039 if(this.doc.body || this.doc.readyState == 'complete'){
13041 this.doc.designMode="on";
13045 Roo.TaskMgr.stop(task);
13046 this.initEditor.defer(10, this);
13053 Roo.TaskMgr.start(task);
13060 onResize : function(w, h)
13062 Roo.log('resize: ' +w + ',' + h );
13063 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13067 if(typeof w == 'number'){
13069 this.iframe.style.width = w + 'px';
13071 if(typeof h == 'number'){
13073 this.iframe.style.height = h + 'px';
13075 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13082 * Toggles the editor between standard and source edit mode.
13083 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13085 toggleSourceEdit : function(sourceEditMode){
13087 this.sourceEditMode = sourceEditMode === true;
13089 if(this.sourceEditMode){
13091 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13094 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13095 //this.iframe.className = '';
13098 //this.setSize(this.owner.wrap.getSize());
13099 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13106 * Protected method that will not generally be called directly. If you need/want
13107 * custom HTML cleanup, this is the method you should override.
13108 * @param {String} html The HTML to be cleaned
13109 * return {String} The cleaned HTML
13111 cleanHtml : function(html){
13112 html = String(html);
13113 if(html.length > 5){
13114 if(Roo.isSafari){ // strip safari nonsense
13115 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13118 if(html == ' '){
13125 * HTML Editor -> Textarea
13126 * Protected method that will not generally be called directly. Syncs the contents
13127 * of the editor iframe with the textarea.
13129 syncValue : function(){
13130 if(this.initialized){
13131 var bd = (this.doc.body || this.doc.documentElement);
13132 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13133 var html = bd.innerHTML;
13135 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13136 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13138 html = '<div style="'+m[0]+'">' + html + '</div>';
13141 html = this.cleanHtml(html);
13142 // fix up the special chars.. normaly like back quotes in word...
13143 // however we do not want to do this with chinese..
13144 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13145 var cc = b.charCodeAt();
13147 (cc >= 0x4E00 && cc < 0xA000 ) ||
13148 (cc >= 0x3400 && cc < 0x4E00 ) ||
13149 (cc >= 0xf900 && cc < 0xfb00 )
13155 if(this.owner.fireEvent('beforesync', this, html) !== false){
13156 this.el.dom.value = html;
13157 this.owner.fireEvent('sync', this, html);
13163 * Protected method that will not generally be called directly. Pushes the value of the textarea
13164 * into the iframe editor.
13166 pushValue : function(){
13167 if(this.initialized){
13168 var v = this.el.dom.value.trim();
13170 // if(v.length < 1){
13174 if(this.owner.fireEvent('beforepush', this, v) !== false){
13175 var d = (this.doc.body || this.doc.documentElement);
13177 this.cleanUpPaste();
13178 this.el.dom.value = d.innerHTML;
13179 this.owner.fireEvent('push', this, v);
13185 deferFocus : function(){
13186 this.focus.defer(10, this);
13190 focus : function(){
13191 if(this.win && !this.sourceEditMode){
13198 assignDocWin: function()
13200 var iframe = this.iframe;
13203 this.doc = iframe.contentWindow.document;
13204 this.win = iframe.contentWindow;
13206 if (!Roo.get(this.frameId)) {
13209 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13210 this.win = Roo.get(this.frameId).dom.contentWindow;
13215 initEditor : function(){
13216 //console.log("INIT EDITOR");
13217 this.assignDocWin();
13221 this.doc.designMode="on";
13223 this.doc.write(this.getDocMarkup());
13226 var dbody = (this.doc.body || this.doc.documentElement);
13227 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13228 // this copies styles from the containing element into thsi one..
13229 // not sure why we need all of this..
13230 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13231 ss['background-attachment'] = 'fixed'; // w3c
13232 dbody.bgProperties = 'fixed'; // ie
13233 Roo.DomHelper.applyStyles(dbody, ss);
13234 Roo.EventManager.on(this.doc, {
13235 //'mousedown': this.onEditorEvent,
13236 'mouseup': this.onEditorEvent,
13237 'dblclick': this.onEditorEvent,
13238 'click': this.onEditorEvent,
13239 'keyup': this.onEditorEvent,
13244 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13246 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13247 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13249 this.initialized = true;
13251 this.owner.fireEvent('initialize', this);
13256 onDestroy : function(){
13262 //for (var i =0; i < this.toolbars.length;i++) {
13263 // // fixme - ask toolbars for heights?
13264 // this.toolbars[i].onDestroy();
13267 //this.wrap.dom.innerHTML = '';
13268 //this.wrap.remove();
13273 onFirstFocus : function(){
13275 this.assignDocWin();
13278 this.activated = true;
13281 if(Roo.isGecko){ // prevent silly gecko errors
13283 var s = this.win.getSelection();
13284 if(!s.focusNode || s.focusNode.nodeType != 3){
13285 var r = s.getRangeAt(0);
13286 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13291 this.execCmd('useCSS', true);
13292 this.execCmd('styleWithCSS', false);
13295 this.owner.fireEvent('activate', this);
13299 adjustFont: function(btn){
13300 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13301 //if(Roo.isSafari){ // safari
13304 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13305 if(Roo.isSafari){ // safari
13306 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13307 v = (v < 10) ? 10 : v;
13308 v = (v > 48) ? 48 : v;
13309 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13314 v = Math.max(1, v+adjust);
13316 this.execCmd('FontSize', v );
13319 onEditorEvent : function(e){
13320 this.owner.fireEvent('editorevent', this, e);
13321 // this.updateToolbar();
13322 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13325 insertTag : function(tg)
13327 // could be a bit smarter... -> wrap the current selected tRoo..
13328 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13330 range = this.createRange(this.getSelection());
13331 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13332 wrappingNode.appendChild(range.extractContents());
13333 range.insertNode(wrappingNode);
13340 this.execCmd("formatblock", tg);
13344 insertText : function(txt)
13348 var range = this.createRange();
13349 range.deleteContents();
13350 //alert(Sender.getAttribute('label'));
13352 range.insertNode(this.doc.createTextNode(txt));
13358 * Executes a Midas editor command on the editor document and performs necessary focus and
13359 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13360 * @param {String} cmd The Midas command
13361 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13363 relayCmd : function(cmd, value){
13365 this.execCmd(cmd, value);
13366 this.owner.fireEvent('editorevent', this);
13367 //this.updateToolbar();
13368 this.owner.deferFocus();
13372 * Executes a Midas editor command directly on the editor document.
13373 * For visual commands, you should use {@link #relayCmd} instead.
13374 * <b>This should only be called after the editor is initialized.</b>
13375 * @param {String} cmd The Midas command
13376 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13378 execCmd : function(cmd, value){
13379 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13386 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13388 * @param {String} text | dom node..
13390 insertAtCursor : function(text)
13395 if(!this.activated){
13401 var r = this.doc.selection.createRange();
13412 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13416 // from jquery ui (MIT licenced)
13418 var win = this.win;
13420 if (win.getSelection && win.getSelection().getRangeAt) {
13421 range = win.getSelection().getRangeAt(0);
13422 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13423 range.insertNode(node);
13424 } else if (win.document.selection && win.document.selection.createRange) {
13425 // no firefox support
13426 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13427 win.document.selection.createRange().pasteHTML(txt);
13429 // no firefox support
13430 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13431 this.execCmd('InsertHTML', txt);
13440 mozKeyPress : function(e){
13442 var c = e.getCharCode(), cmd;
13445 c = String.fromCharCode(c).toLowerCase();
13459 this.cleanUpPaste.defer(100, this);
13467 e.preventDefault();
13475 fixKeys : function(){ // load time branching for fastest keydown performance
13477 return function(e){
13478 var k = e.getKey(), r;
13481 r = this.doc.selection.createRange();
13484 r.pasteHTML('    ');
13491 r = this.doc.selection.createRange();
13493 var target = r.parentElement();
13494 if(!target || target.tagName.toLowerCase() != 'li'){
13496 r.pasteHTML('<br />');
13502 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13503 this.cleanUpPaste.defer(100, this);
13509 }else if(Roo.isOpera){
13510 return function(e){
13511 var k = e.getKey();
13515 this.execCmd('InsertHTML','    ');
13518 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13519 this.cleanUpPaste.defer(100, this);
13524 }else if(Roo.isSafari){
13525 return function(e){
13526 var k = e.getKey();
13530 this.execCmd('InsertText','\t');
13534 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13535 this.cleanUpPaste.defer(100, this);
13543 getAllAncestors: function()
13545 var p = this.getSelectedNode();
13548 a.push(p); // push blank onto stack..
13549 p = this.getParentElement();
13553 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13557 a.push(this.doc.body);
13561 lastSelNode : false,
13564 getSelection : function()
13566 this.assignDocWin();
13567 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13570 getSelectedNode: function()
13572 // this may only work on Gecko!!!
13574 // should we cache this!!!!
13579 var range = this.createRange(this.getSelection()).cloneRange();
13582 var parent = range.parentElement();
13584 var testRange = range.duplicate();
13585 testRange.moveToElementText(parent);
13586 if (testRange.inRange(range)) {
13589 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13592 parent = parent.parentElement;
13597 // is ancestor a text element.
13598 var ac = range.commonAncestorContainer;
13599 if (ac.nodeType == 3) {
13600 ac = ac.parentNode;
13603 var ar = ac.childNodes;
13606 var other_nodes = [];
13607 var has_other_nodes = false;
13608 for (var i=0;i<ar.length;i++) {
13609 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13612 // fullly contained node.
13614 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13619 // probably selected..
13620 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13621 other_nodes.push(ar[i]);
13625 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13630 has_other_nodes = true;
13632 if (!nodes.length && other_nodes.length) {
13633 nodes= other_nodes;
13635 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13641 createRange: function(sel)
13643 // this has strange effects when using with
13644 // top toolbar - not sure if it's a great idea.
13645 //this.editor.contentWindow.focus();
13646 if (typeof sel != "undefined") {
13648 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13650 return this.doc.createRange();
13653 return this.doc.createRange();
13656 getParentElement: function()
13659 this.assignDocWin();
13660 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13662 var range = this.createRange(sel);
13665 var p = range.commonAncestorContainer;
13666 while (p.nodeType == 3) { // text node
13677 * Range intersection.. the hard stuff...
13681 * [ -- selected range --- ]
13685 * if end is before start or hits it. fail.
13686 * if start is after end or hits it fail.
13688 * if either hits (but other is outside. - then it's not
13694 // @see http://www.thismuchiknow.co.uk/?p=64.
13695 rangeIntersectsNode : function(range, node)
13697 var nodeRange = node.ownerDocument.createRange();
13699 nodeRange.selectNode(node);
13701 nodeRange.selectNodeContents(node);
13704 var rangeStartRange = range.cloneRange();
13705 rangeStartRange.collapse(true);
13707 var rangeEndRange = range.cloneRange();
13708 rangeEndRange.collapse(false);
13710 var nodeStartRange = nodeRange.cloneRange();
13711 nodeStartRange.collapse(true);
13713 var nodeEndRange = nodeRange.cloneRange();
13714 nodeEndRange.collapse(false);
13716 return rangeStartRange.compareBoundaryPoints(
13717 Range.START_TO_START, nodeEndRange) == -1 &&
13718 rangeEndRange.compareBoundaryPoints(
13719 Range.START_TO_START, nodeStartRange) == 1;
13723 rangeCompareNode : function(range, node)
13725 var nodeRange = node.ownerDocument.createRange();
13727 nodeRange.selectNode(node);
13729 nodeRange.selectNodeContents(node);
13733 range.collapse(true);
13735 nodeRange.collapse(true);
13737 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13738 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13740 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13742 var nodeIsBefore = ss == 1;
13743 var nodeIsAfter = ee == -1;
13745 if (nodeIsBefore && nodeIsAfter)
13747 if (!nodeIsBefore && nodeIsAfter)
13748 return 1; //right trailed.
13750 if (nodeIsBefore && !nodeIsAfter)
13751 return 2; // left trailed.
13756 // private? - in a new class?
13757 cleanUpPaste : function()
13759 // cleans up the whole document..
13760 Roo.log('cleanuppaste');
13762 this.cleanUpChildren(this.doc.body);
13763 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13764 if (clean != this.doc.body.innerHTML) {
13765 this.doc.body.innerHTML = clean;
13770 cleanWordChars : function(input) {// change the chars to hex code
13771 var he = Roo.HtmlEditorCore;
13773 var output = input;
13774 Roo.each(he.swapCodes, function(sw) {
13775 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13777 output = output.replace(swapper, sw[1]);
13784 cleanUpChildren : function (n)
13786 if (!n.childNodes.length) {
13789 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13790 this.cleanUpChild(n.childNodes[i]);
13797 cleanUpChild : function (node)
13800 //console.log(node);
13801 if (node.nodeName == "#text") {
13802 // clean up silly Windows -- stuff?
13805 if (node.nodeName == "#comment") {
13806 node.parentNode.removeChild(node);
13807 // clean up silly Windows -- stuff?
13811 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13813 node.parentNode.removeChild(node);
13818 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13820 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13821 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13823 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13824 // remove_keep_children = true;
13827 if (remove_keep_children) {
13828 this.cleanUpChildren(node);
13829 // inserts everything just before this node...
13830 while (node.childNodes.length) {
13831 var cn = node.childNodes[0];
13832 node.removeChild(cn);
13833 node.parentNode.insertBefore(cn, node);
13835 node.parentNode.removeChild(node);
13839 if (!node.attributes || !node.attributes.length) {
13840 this.cleanUpChildren(node);
13844 function cleanAttr(n,v)
13847 if (v.match(/^\./) || v.match(/^\//)) {
13850 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13853 if (v.match(/^#/)) {
13856 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13857 node.removeAttribute(n);
13861 function cleanStyle(n,v)
13863 if (v.match(/expression/)) { //XSS?? should we even bother..
13864 node.removeAttribute(n);
13867 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13868 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13871 var parts = v.split(/;/);
13874 Roo.each(parts, function(p) {
13875 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13879 var l = p.split(':').shift().replace(/\s+/g,'');
13880 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13882 if ( cblack.indexOf(l) > -1) {
13883 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13884 //node.removeAttribute(n);
13888 // only allow 'c whitelisted system attributes'
13889 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13890 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13891 //node.removeAttribute(n);
13901 if (clean.length) {
13902 node.setAttribute(n, clean.join(';'));
13904 node.removeAttribute(n);
13910 for (var i = node.attributes.length-1; i > -1 ; i--) {
13911 var a = node.attributes[i];
13914 if (a.name.toLowerCase().substr(0,2)=='on') {
13915 node.removeAttribute(a.name);
13918 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13919 node.removeAttribute(a.name);
13922 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13923 cleanAttr(a.name,a.value); // fixme..
13926 if (a.name == 'style') {
13927 cleanStyle(a.name,a.value);
13930 /// clean up MS crap..
13931 // tecnically this should be a list of valid class'es..
13934 if (a.name == 'class') {
13935 if (a.value.match(/^Mso/)) {
13936 node.className = '';
13939 if (a.value.match(/body/)) {
13940 node.className = '';
13951 this.cleanUpChildren(node);
13957 // hide stuff that is not compatible
13971 * @event specialkey
13975 * @cfg {String} fieldClass @hide
13978 * @cfg {String} focusClass @hide
13981 * @cfg {String} autoCreate @hide
13984 * @cfg {String} inputType @hide
13987 * @cfg {String} invalidClass @hide
13990 * @cfg {String} invalidText @hide
13993 * @cfg {String} msgFx @hide
13996 * @cfg {String} validateOnBlur @hide
14000 Roo.HtmlEditorCore.white = [
14001 'area', 'br', 'img', 'input', 'hr', 'wbr',
14003 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14004 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14005 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14006 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14007 'table', 'ul', 'xmp',
14009 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14012 'dir', 'menu', 'ol', 'ul', 'dl',
14018 Roo.HtmlEditorCore.black = [
14019 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14021 'base', 'basefont', 'bgsound', 'blink', 'body',
14022 'frame', 'frameset', 'head', 'html', 'ilayer',
14023 'iframe', 'layer', 'link', 'meta', 'object',
14024 'script', 'style' ,'title', 'xml' // clean later..
14026 Roo.HtmlEditorCore.clean = [
14027 'script', 'style', 'title', 'xml'
14029 Roo.HtmlEditorCore.remove = [
14034 Roo.HtmlEditorCore.ablack = [
14038 Roo.HtmlEditorCore.aclean = [
14039 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14043 Roo.HtmlEditorCore.pwhite= [
14044 'http', 'https', 'mailto'
14047 // white listed style attributes.
14048 Roo.HtmlEditorCore.cwhite= [
14049 // 'text-align', /// default is to allow most things..
14055 // black listed style attributes.
14056 Roo.HtmlEditorCore.cblack= [
14057 // 'font-size' -- this can be set by the project
14061 Roo.HtmlEditorCore.swapCodes =[
14080 * @class Roo.bootstrap.HtmlEditor
14081 * @extends Roo.bootstrap.TextArea
14082 * Bootstrap HtmlEditor class
14085 * Create a new HtmlEditor
14086 * @param {Object} config The config object
14089 Roo.bootstrap.HtmlEditor = function(config){
14090 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14091 if (!this.toolbars) {
14092 this.toolbars = [];
14094 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14097 * @event initialize
14098 * Fires when the editor is fully initialized (including the iframe)
14099 * @param {HtmlEditor} this
14104 * Fires when the editor is first receives the focus. Any insertion must wait
14105 * until after this event.
14106 * @param {HtmlEditor} this
14110 * @event beforesync
14111 * Fires before the textarea is updated with content from the editor iframe. Return false
14112 * to cancel the sync.
14113 * @param {HtmlEditor} this
14114 * @param {String} html
14118 * @event beforepush
14119 * Fires before the iframe editor is updated with content from the textarea. Return false
14120 * to cancel the push.
14121 * @param {HtmlEditor} this
14122 * @param {String} html
14127 * Fires when the textarea is updated with content from the editor iframe.
14128 * @param {HtmlEditor} this
14129 * @param {String} html
14134 * Fires when the iframe editor is updated with content from the textarea.
14135 * @param {HtmlEditor} this
14136 * @param {String} html
14140 * @event editmodechange
14141 * Fires when the editor switches edit modes
14142 * @param {HtmlEditor} this
14143 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14145 editmodechange: true,
14147 * @event editorevent
14148 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14149 * @param {HtmlEditor} this
14153 * @event firstfocus
14154 * Fires when on first focus - needed by toolbars..
14155 * @param {HtmlEditor} this
14160 * Auto save the htmlEditor value as a file into Events
14161 * @param {HtmlEditor} this
14165 * @event savedpreview
14166 * preview the saved version of htmlEditor
14167 * @param {HtmlEditor} this
14174 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14178 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14183 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14188 * @cfg {Number} height (in pixels)
14192 * @cfg {Number} width (in pixels)
14197 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14200 stylesheets: false,
14205 // private properties
14206 validationEvent : false,
14208 initialized : false,
14211 onFocus : Roo.emptyFn,
14213 hideMode:'offsets',
14216 tbContainer : false,
14218 toolbarContainer :function() {
14219 return this.wrap.select('.x-html-editor-tb',true).first();
14223 * Protected method that will not generally be called directly. It
14224 * is called when the editor creates its toolbar. Override this method if you need to
14225 * add custom toolbar buttons.
14226 * @param {HtmlEditor} editor
14228 createToolbar : function(){
14230 Roo.log("create toolbars");
14232 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14233 this.toolbars[0].render(this.toolbarContainer());
14237 // if (!editor.toolbars || !editor.toolbars.length) {
14238 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14241 // for (var i =0 ; i < editor.toolbars.length;i++) {
14242 // editor.toolbars[i] = Roo.factory(
14243 // typeof(editor.toolbars[i]) == 'string' ?
14244 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14245 // Roo.bootstrap.HtmlEditor);
14246 // editor.toolbars[i].init(editor);
14252 onRender : function(ct, position)
14254 // Roo.log("Call onRender: " + this.xtype);
14256 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14258 this.wrap = this.inputEl().wrap({
14259 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14262 this.editorcore.onRender(ct, position);
14264 if (this.resizable) {
14265 this.resizeEl = new Roo.Resizable(this.wrap, {
14269 minHeight : this.height,
14270 height: this.height,
14271 handles : this.resizable,
14274 resize : function(r, w, h) {
14275 _t.onResize(w,h); // -something
14281 this.createToolbar(this);
14284 if(!this.width && this.resizable){
14285 this.setSize(this.wrap.getSize());
14287 if (this.resizeEl) {
14288 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14289 // should trigger onReize..
14295 onResize : function(w, h)
14297 Roo.log('resize: ' +w + ',' + h );
14298 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14302 if(this.inputEl() ){
14303 if(typeof w == 'number'){
14304 var aw = w - this.wrap.getFrameWidth('lr');
14305 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14308 if(typeof h == 'number'){
14309 var tbh = -11; // fixme it needs to tool bar size!
14310 for (var i =0; i < this.toolbars.length;i++) {
14311 // fixme - ask toolbars for heights?
14312 tbh += this.toolbars[i].el.getHeight();
14313 //if (this.toolbars[i].footer) {
14314 // tbh += this.toolbars[i].footer.el.getHeight();
14322 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14323 ah -= 5; // knock a few pixes off for look..
14324 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14328 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14329 this.editorcore.onResize(ew,eh);
14334 * Toggles the editor between standard and source edit mode.
14335 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14337 toggleSourceEdit : function(sourceEditMode)
14339 this.editorcore.toggleSourceEdit(sourceEditMode);
14341 if(this.editorcore.sourceEditMode){
14342 Roo.log('editor - showing textarea');
14345 // Roo.log(this.syncValue());
14347 this.inputEl().removeClass('hide');
14348 this.inputEl().dom.removeAttribute('tabIndex');
14349 this.inputEl().focus();
14351 Roo.log('editor - hiding textarea');
14353 // Roo.log(this.pushValue());
14356 this.inputEl().addClass('hide');
14357 this.inputEl().dom.setAttribute('tabIndex', -1);
14358 //this.deferFocus();
14361 if(this.resizable){
14362 this.setSize(this.wrap.getSize());
14365 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14368 // private (for BoxComponent)
14369 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14371 // private (for BoxComponent)
14372 getResizeEl : function(){
14376 // private (for BoxComponent)
14377 getPositionEl : function(){
14382 initEvents : function(){
14383 this.originalValue = this.getValue();
14387 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14390 // markInvalid : Roo.emptyFn,
14392 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14395 // clearInvalid : Roo.emptyFn,
14397 setValue : function(v){
14398 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14399 this.editorcore.pushValue();
14404 deferFocus : function(){
14405 this.focus.defer(10, this);
14409 focus : function(){
14410 this.editorcore.focus();
14416 onDestroy : function(){
14422 for (var i =0; i < this.toolbars.length;i++) {
14423 // fixme - ask toolbars for heights?
14424 this.toolbars[i].onDestroy();
14427 this.wrap.dom.innerHTML = '';
14428 this.wrap.remove();
14433 onFirstFocus : function(){
14434 //Roo.log("onFirstFocus");
14435 this.editorcore.onFirstFocus();
14436 for (var i =0; i < this.toolbars.length;i++) {
14437 this.toolbars[i].onFirstFocus();
14443 syncValue : function()
14445 this.editorcore.syncValue();
14448 pushValue : function()
14450 this.editorcore.pushValue();
14454 // hide stuff that is not compatible
14468 * @event specialkey
14472 * @cfg {String} fieldClass @hide
14475 * @cfg {String} focusClass @hide
14478 * @cfg {String} autoCreate @hide
14481 * @cfg {String} inputType @hide
14484 * @cfg {String} invalidClass @hide
14487 * @cfg {String} invalidText @hide
14490 * @cfg {String} msgFx @hide
14493 * @cfg {String} validateOnBlur @hide
14504 * @class Roo.bootstrap.HtmlEditorToolbar1
14509 new Roo.bootstrap.HtmlEditor({
14512 new Roo.bootstrap.HtmlEditorToolbar1({
14513 disable : { fonts: 1 , format: 1, ..., ... , ...],
14519 * @cfg {Object} disable List of elements to disable..
14520 * @cfg {Array} btns List of additional buttons.
14524 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14527 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14530 Roo.apply(this, config);
14532 // default disabled, based on 'good practice'..
14533 this.disable = this.disable || {};
14534 Roo.applyIf(this.disable, {
14537 specialElements : true
14539 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14541 this.editor = config.editor;
14542 this.editorcore = config.editor.editorcore;
14544 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14546 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14547 // dont call parent... till later.
14549 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14555 editorcore : false,
14560 "h1","h2","h3","h4","h5","h6",
14562 "abbr", "acronym", "address", "cite", "samp", "var",
14566 onRender : function(ct, position)
14568 // Roo.log("Call onRender: " + this.xtype);
14570 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14572 this.el.dom.style.marginBottom = '0';
14574 var editorcore = this.editorcore;
14575 var editor= this.editor;
14578 var btn = function(id,cmd , toggle, handler){
14580 var event = toggle ? 'toggle' : 'click';
14585 xns: Roo.bootstrap,
14588 enableToggle:toggle !== false,
14590 pressed : toggle ? false : null,
14593 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14594 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14603 xns: Roo.bootstrap,
14604 glyphicon : 'font',
14608 xns: Roo.bootstrap,
14612 Roo.each(this.formats, function(f) {
14613 style.menu.items.push({
14615 xns: Roo.bootstrap,
14616 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14621 editorcore.insertTag(this.tagname);
14628 children.push(style);
14631 btn('bold',false,true);
14632 btn('italic',false,true);
14633 btn('align-left', 'justifyleft',true);
14634 btn('align-center', 'justifycenter',true);
14635 btn('align-right' , 'justifyright',true);
14636 btn('link', false, false, function(btn) {
14637 //Roo.log("create link?");
14638 var url = prompt(this.createLinkText, this.defaultLinkValue);
14639 if(url && url != 'http:/'+'/'){
14640 this.editorcore.relayCmd('createlink', url);
14643 btn('list','insertunorderedlist',true);
14644 btn('pencil', false,true, function(btn){
14647 this.toggleSourceEdit(btn.pressed);
14653 xns: Roo.bootstrap,
14658 xns: Roo.bootstrap,
14663 cog.menu.items.push({
14665 xns: Roo.bootstrap,
14666 html : Clean styles,
14671 editorcore.insertTag(this.tagname);
14680 this.xtype = 'Navbar';
14682 for(var i=0;i< children.length;i++) {
14684 this.buttons.add(this.addxtypeChild(children[i]));
14688 editor.on('editorevent', this.updateToolbar, this);
14690 onBtnClick : function(id)
14692 this.editorcore.relayCmd(id);
14693 this.editorcore.focus();
14697 * Protected method that will not generally be called directly. It triggers
14698 * a toolbar update by reading the markup state of the current selection in the editor.
14700 updateToolbar: function(){
14702 if(!this.editorcore.activated){
14703 this.editor.onFirstFocus(); // is this neeed?
14707 var btns = this.buttons;
14708 var doc = this.editorcore.doc;
14709 btns.get('bold').setActive(doc.queryCommandState('bold'));
14710 btns.get('italic').setActive(doc.queryCommandState('italic'));
14711 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14713 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14714 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14715 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14717 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14718 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14721 var ans = this.editorcore.getAllAncestors();
14722 if (this.formatCombo) {
14725 var store = this.formatCombo.store;
14726 this.formatCombo.setValue("");
14727 for (var i =0; i < ans.length;i++) {
14728 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14730 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14738 // hides menus... - so this cant be on a menu...
14739 Roo.bootstrap.MenuMgr.hideAll();
14741 Roo.bootstrap.MenuMgr.hideAll();
14742 //this.editorsyncValue();
14744 onFirstFocus: function() {
14745 this.buttons.each(function(item){
14749 toggleSourceEdit : function(sourceEditMode){
14752 if(sourceEditMode){
14753 Roo.log("disabling buttons");
14754 this.buttons.each( function(item){
14755 if(item.cmd != 'pencil'){
14761 Roo.log("enabling buttons");
14762 if(this.editorcore.initialized){
14763 this.buttons.each( function(item){
14769 Roo.log("calling toggole on editor");
14770 // tell the editor that it's been pressed..
14771 this.editor.toggleSourceEdit(sourceEditMode);
14781 * @class Roo.bootstrap.Table.AbstractSelectionModel
14782 * @extends Roo.util.Observable
14783 * Abstract base class for grid SelectionModels. It provides the interface that should be
14784 * implemented by descendant classes. This class should not be directly instantiated.
14787 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14788 this.locked = false;
14789 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14793 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14794 /** @ignore Called by the grid automatically. Do not call directly. */
14795 init : function(grid){
14801 * Locks the selections.
14804 this.locked = true;
14808 * Unlocks the selections.
14810 unlock : function(){
14811 this.locked = false;
14815 * Returns true if the selections are locked.
14816 * @return {Boolean}
14818 isLocked : function(){
14819 return this.locked;
14823 * @class Roo.bootstrap.Table.ColumnModel
14824 * @extends Roo.util.Observable
14825 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14826 * the columns in the table.
14829 * @param {Object} config An Array of column config objects. See this class's
14830 * config objects for details.
14832 Roo.bootstrap.Table.ColumnModel = function(config){
14834 * The config passed into the constructor
14836 this.config = config;
14839 // if no id, create one
14840 // if the column does not have a dataIndex mapping,
14841 // map it to the order it is in the config
14842 for(var i = 0, len = config.length; i < len; i++){
14844 if(typeof c.dataIndex == "undefined"){
14847 if(typeof c.renderer == "string"){
14848 c.renderer = Roo.util.Format[c.renderer];
14850 if(typeof c.id == "undefined"){
14853 // if(c.editor && c.editor.xtype){
14854 // c.editor = Roo.factory(c.editor, Roo.grid);
14856 // if(c.editor && c.editor.isFormField){
14857 // c.editor = new Roo.grid.GridEditor(c.editor);
14860 this.lookup[c.id] = c;
14864 * The width of columns which have no width specified (defaults to 100)
14867 this.defaultWidth = 100;
14870 * Default sortable of columns which have no sortable specified (defaults to false)
14873 this.defaultSortable = false;
14877 * @event widthchange
14878 * Fires when the width of a column changes.
14879 * @param {ColumnModel} this
14880 * @param {Number} columnIndex The column index
14881 * @param {Number} newWidth The new width
14883 "widthchange": true,
14885 * @event headerchange
14886 * Fires when the text of a header changes.
14887 * @param {ColumnModel} this
14888 * @param {Number} columnIndex The column index
14889 * @param {Number} newText The new header text
14891 "headerchange": true,
14893 * @event hiddenchange
14894 * Fires when a column is hidden or "unhidden".
14895 * @param {ColumnModel} this
14896 * @param {Number} columnIndex The column index
14897 * @param {Boolean} hidden true if hidden, false otherwise
14899 "hiddenchange": true,
14901 * @event columnmoved
14902 * Fires when a column is moved.
14903 * @param {ColumnModel} this
14904 * @param {Number} oldIndex
14905 * @param {Number} newIndex
14907 "columnmoved" : true,
14909 * @event columlockchange
14910 * Fires when a column's locked state is changed
14911 * @param {ColumnModel} this
14912 * @param {Number} colIndex
14913 * @param {Boolean} locked true if locked
14915 "columnlockchange" : true
14917 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14919 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14921 * @cfg {String} header The header text to display in the Grid view.
14924 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14925 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14926 * specified, the column's index is used as an index into the Record's data Array.
14929 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14930 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14933 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14934 * Defaults to the value of the {@link #defaultSortable} property.
14935 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14938 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14941 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14944 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14947 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14950 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14951 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14952 * default renderer uses the raw data value.
14955 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14959 * Returns the id of the column at the specified index.
14960 * @param {Number} index The column index
14961 * @return {String} the id
14963 getColumnId : function(index){
14964 return this.config[index].id;
14968 * Returns the column for a specified id.
14969 * @param {String} id The column id
14970 * @return {Object} the column
14972 getColumnById : function(id){
14973 return this.lookup[id];
14978 * Returns the column for a specified dataIndex.
14979 * @param {String} dataIndex The column dataIndex
14980 * @return {Object|Boolean} the column or false if not found
14982 getColumnByDataIndex: function(dataIndex){
14983 var index = this.findColumnIndex(dataIndex);
14984 return index > -1 ? this.config[index] : false;
14988 * Returns the index for a specified column id.
14989 * @param {String} id The column id
14990 * @return {Number} the index, or -1 if not found
14992 getIndexById : function(id){
14993 for(var i = 0, len = this.config.length; i < len; i++){
14994 if(this.config[i].id == id){
15002 * Returns the index for a specified column dataIndex.
15003 * @param {String} dataIndex The column dataIndex
15004 * @return {Number} the index, or -1 if not found
15007 findColumnIndex : function(dataIndex){
15008 for(var i = 0, len = this.config.length; i < len; i++){
15009 if(this.config[i].dataIndex == dataIndex){
15017 moveColumn : function(oldIndex, newIndex){
15018 var c = this.config[oldIndex];
15019 this.config.splice(oldIndex, 1);
15020 this.config.splice(newIndex, 0, c);
15021 this.dataMap = null;
15022 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15025 isLocked : function(colIndex){
15026 return this.config[colIndex].locked === true;
15029 setLocked : function(colIndex, value, suppressEvent){
15030 if(this.isLocked(colIndex) == value){
15033 this.config[colIndex].locked = value;
15034 if(!suppressEvent){
15035 this.fireEvent("columnlockchange", this, colIndex, value);
15039 getTotalLockedWidth : function(){
15040 var totalWidth = 0;
15041 for(var i = 0; i < this.config.length; i++){
15042 if(this.isLocked(i) && !this.isHidden(i)){
15043 this.totalWidth += this.getColumnWidth(i);
15049 getLockedCount : function(){
15050 for(var i = 0, len = this.config.length; i < len; i++){
15051 if(!this.isLocked(i)){
15058 * Returns the number of columns.
15061 getColumnCount : function(visibleOnly){
15062 if(visibleOnly === true){
15064 for(var i = 0, len = this.config.length; i < len; i++){
15065 if(!this.isHidden(i)){
15071 return this.config.length;
15075 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15076 * @param {Function} fn
15077 * @param {Object} scope (optional)
15078 * @return {Array} result
15080 getColumnsBy : function(fn, scope){
15082 for(var i = 0, len = this.config.length; i < len; i++){
15083 var c = this.config[i];
15084 if(fn.call(scope||this, c, i) === true){
15092 * Returns true if the specified column is sortable.
15093 * @param {Number} col The column index
15094 * @return {Boolean}
15096 isSortable : function(col){
15097 if(typeof this.config[col].sortable == "undefined"){
15098 return this.defaultSortable;
15100 return this.config[col].sortable;
15104 * Returns the rendering (formatting) function defined for the column.
15105 * @param {Number} col The column index.
15106 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15108 getRenderer : function(col){
15109 if(!this.config[col].renderer){
15110 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15112 return this.config[col].renderer;
15116 * Sets the rendering (formatting) function for a column.
15117 * @param {Number} col The column index
15118 * @param {Function} fn The function to use to process the cell's raw data
15119 * to return HTML markup for the grid view. The render function is called with
15120 * the following parameters:<ul>
15121 * <li>Data value.</li>
15122 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15123 * <li>css A CSS style string to apply to the table cell.</li>
15124 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15125 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15126 * <li>Row index</li>
15127 * <li>Column index</li>
15128 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15130 setRenderer : function(col, fn){
15131 this.config[col].renderer = fn;
15135 * Returns the width for the specified column.
15136 * @param {Number} col The column index
15139 getColumnWidth : function(col){
15140 return this.config[col].width * 1 || this.defaultWidth;
15144 * Sets the width for a column.
15145 * @param {Number} col The column index
15146 * @param {Number} width The new width
15148 setColumnWidth : function(col, width, suppressEvent){
15149 this.config[col].width = width;
15150 this.totalWidth = null;
15151 if(!suppressEvent){
15152 this.fireEvent("widthchange", this, col, width);
15157 * Returns the total width of all columns.
15158 * @param {Boolean} includeHidden True to include hidden column widths
15161 getTotalWidth : function(includeHidden){
15162 if(!this.totalWidth){
15163 this.totalWidth = 0;
15164 for(var i = 0, len = this.config.length; i < len; i++){
15165 if(includeHidden || !this.isHidden(i)){
15166 this.totalWidth += this.getColumnWidth(i);
15170 return this.totalWidth;
15174 * Returns the header for the specified column.
15175 * @param {Number} col The column index
15178 getColumnHeader : function(col){
15179 return this.config[col].header;
15183 * Sets the header for a column.
15184 * @param {Number} col The column index
15185 * @param {String} header The new header
15187 setColumnHeader : function(col, header){
15188 this.config[col].header = header;
15189 this.fireEvent("headerchange", this, col, header);
15193 * Returns the tooltip for the specified column.
15194 * @param {Number} col The column index
15197 getColumnTooltip : function(col){
15198 return this.config[col].tooltip;
15201 * Sets the tooltip for a column.
15202 * @param {Number} col The column index
15203 * @param {String} tooltip The new tooltip
15205 setColumnTooltip : function(col, tooltip){
15206 this.config[col].tooltip = tooltip;
15210 * Returns the dataIndex for the specified column.
15211 * @param {Number} col The column index
15214 getDataIndex : function(col){
15215 return this.config[col].dataIndex;
15219 * Sets the dataIndex for a column.
15220 * @param {Number} col The column index
15221 * @param {Number} dataIndex The new dataIndex
15223 setDataIndex : function(col, dataIndex){
15224 this.config[col].dataIndex = dataIndex;
15230 * Returns true if the cell is editable.
15231 * @param {Number} colIndex The column index
15232 * @param {Number} rowIndex The row index
15233 * @return {Boolean}
15235 isCellEditable : function(colIndex, rowIndex){
15236 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15240 * Returns the editor defined for the cell/column.
15241 * return false or null to disable editing.
15242 * @param {Number} colIndex The column index
15243 * @param {Number} rowIndex The row index
15246 getCellEditor : function(colIndex, rowIndex){
15247 return this.config[colIndex].editor;
15251 * Sets if a column is editable.
15252 * @param {Number} col The column index
15253 * @param {Boolean} editable True if the column is editable
15255 setEditable : function(col, editable){
15256 this.config[col].editable = editable;
15261 * Returns true if the column is hidden.
15262 * @param {Number} colIndex The column index
15263 * @return {Boolean}
15265 isHidden : function(colIndex){
15266 return this.config[colIndex].hidden;
15271 * Returns true if the column width cannot be changed
15273 isFixed : function(colIndex){
15274 return this.config[colIndex].fixed;
15278 * Returns true if the column can be resized
15279 * @return {Boolean}
15281 isResizable : function(colIndex){
15282 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15285 * Sets if a column is hidden.
15286 * @param {Number} colIndex The column index
15287 * @param {Boolean} hidden True if the column is hidden
15289 setHidden : function(colIndex, hidden){
15290 this.config[colIndex].hidden = hidden;
15291 this.totalWidth = null;
15292 this.fireEvent("hiddenchange", this, colIndex, hidden);
15296 * Sets the editor for a column.
15297 * @param {Number} col The column index
15298 * @param {Object} editor The editor object
15300 setEditor : function(col, editor){
15301 this.config[col].editor = editor;
15305 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15306 if(typeof value == "string" && value.length < 1){
15312 // Alias for backwards compatibility
15313 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15316 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15317 * @class Roo.bootstrap.Table.RowSelectionModel
15318 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15319 * It supports multiple selections and keyboard selection/navigation.
15321 * @param {Object} config
15324 Roo.bootstrap.Table.RowSelectionModel = function(config){
15325 Roo.apply(this, config);
15326 this.selections = new Roo.util.MixedCollection(false, function(o){
15331 this.lastActive = false;
15335 * @event selectionchange
15336 * Fires when the selection changes
15337 * @param {SelectionModel} this
15339 "selectionchange" : true,
15341 * @event afterselectionchange
15342 * Fires after the selection changes (eg. by key press or clicking)
15343 * @param {SelectionModel} this
15345 "afterselectionchange" : true,
15347 * @event beforerowselect
15348 * Fires when a row is selected being selected, return false to cancel.
15349 * @param {SelectionModel} this
15350 * @param {Number} rowIndex The selected index
15351 * @param {Boolean} keepExisting False if other selections will be cleared
15353 "beforerowselect" : true,
15356 * Fires when a row is selected.
15357 * @param {SelectionModel} this
15358 * @param {Number} rowIndex The selected index
15359 * @param {Roo.data.Record} r The record
15361 "rowselect" : true,
15363 * @event rowdeselect
15364 * Fires when a row is deselected.
15365 * @param {SelectionModel} this
15366 * @param {Number} rowIndex The selected index
15368 "rowdeselect" : true
15370 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15371 this.locked = false;
15374 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15376 * @cfg {Boolean} singleSelect
15377 * True to allow selection of only one row at a time (defaults to false)
15379 singleSelect : false,
15382 initEvents : function(){
15384 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15385 this.grid.on("mousedown", this.handleMouseDown, this);
15386 }else{ // allow click to work like normal
15387 this.grid.on("rowclick", this.handleDragableRowClick, this);
15390 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15391 "up" : function(e){
15393 this.selectPrevious(e.shiftKey);
15394 }else if(this.last !== false && this.lastActive !== false){
15395 var last = this.last;
15396 this.selectRange(this.last, this.lastActive-1);
15397 this.grid.getView().focusRow(this.lastActive);
15398 if(last !== false){
15402 this.selectFirstRow();
15404 this.fireEvent("afterselectionchange", this);
15406 "down" : function(e){
15408 this.selectNext(e.shiftKey);
15409 }else if(this.last !== false && this.lastActive !== false){
15410 var last = this.last;
15411 this.selectRange(this.last, this.lastActive+1);
15412 this.grid.getView().focusRow(this.lastActive);
15413 if(last !== false){
15417 this.selectFirstRow();
15419 this.fireEvent("afterselectionchange", this);
15424 var view = this.grid.view;
15425 view.on("refresh", this.onRefresh, this);
15426 view.on("rowupdated", this.onRowUpdated, this);
15427 view.on("rowremoved", this.onRemove, this);
15431 onRefresh : function(){
15432 var ds = this.grid.dataSource, i, v = this.grid.view;
15433 var s = this.selections;
15434 s.each(function(r){
15435 if((i = ds.indexOfId(r.id)) != -1){
15444 onRemove : function(v, index, r){
15445 this.selections.remove(r);
15449 onRowUpdated : function(v, index, r){
15450 if(this.isSelected(r)){
15451 v.onRowSelect(index);
15457 * @param {Array} records The records to select
15458 * @param {Boolean} keepExisting (optional) True to keep existing selections
15460 selectRecords : function(records, keepExisting){
15462 this.clearSelections();
15464 var ds = this.grid.dataSource;
15465 for(var i = 0, len = records.length; i < len; i++){
15466 this.selectRow(ds.indexOf(records[i]), true);
15471 * Gets the number of selected rows.
15474 getCount : function(){
15475 return this.selections.length;
15479 * Selects the first row in the grid.
15481 selectFirstRow : function(){
15486 * Select the last row.
15487 * @param {Boolean} keepExisting (optional) True to keep existing selections
15489 selectLastRow : function(keepExisting){
15490 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15494 * Selects the row immediately following the last selected row.
15495 * @param {Boolean} keepExisting (optional) True to keep existing selections
15497 selectNext : function(keepExisting){
15498 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15499 this.selectRow(this.last+1, keepExisting);
15500 this.grid.getView().focusRow(this.last);
15505 * Selects the row that precedes the last selected row.
15506 * @param {Boolean} keepExisting (optional) True to keep existing selections
15508 selectPrevious : function(keepExisting){
15510 this.selectRow(this.last-1, keepExisting);
15511 this.grid.getView().focusRow(this.last);
15516 * Returns the selected records
15517 * @return {Array} Array of selected records
15519 getSelections : function(){
15520 return [].concat(this.selections.items);
15524 * Returns the first selected record.
15527 getSelected : function(){
15528 return this.selections.itemAt(0);
15533 * Clears all selections.
15535 clearSelections : function(fast){
15536 if(this.locked) return;
15538 var ds = this.grid.dataSource;
15539 var s = this.selections;
15540 s.each(function(r){
15541 this.deselectRow(ds.indexOfId(r.id));
15545 this.selections.clear();
15552 * Selects all rows.
15554 selectAll : function(){
15555 if(this.locked) return;
15556 this.selections.clear();
15557 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15558 this.selectRow(i, true);
15563 * Returns True if there is a selection.
15564 * @return {Boolean}
15566 hasSelection : function(){
15567 return this.selections.length > 0;
15571 * Returns True if the specified row is selected.
15572 * @param {Number/Record} record The record or index of the record to check
15573 * @return {Boolean}
15575 isSelected : function(index){
15576 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15577 return (r && this.selections.key(r.id) ? true : false);
15581 * Returns True if the specified record id is selected.
15582 * @param {String} id The id of record to check
15583 * @return {Boolean}
15585 isIdSelected : function(id){
15586 return (this.selections.key(id) ? true : false);
15590 handleMouseDown : function(e, t){
15591 var view = this.grid.getView(), rowIndex;
15592 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15595 if(e.shiftKey && this.last !== false){
15596 var last = this.last;
15597 this.selectRange(last, rowIndex, e.ctrlKey);
15598 this.last = last; // reset the last
15599 view.focusRow(rowIndex);
15601 var isSelected = this.isSelected(rowIndex);
15602 if(e.button !== 0 && isSelected){
15603 view.focusRow(rowIndex);
15604 }else if(e.ctrlKey && isSelected){
15605 this.deselectRow(rowIndex);
15606 }else if(!isSelected){
15607 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15608 view.focusRow(rowIndex);
15611 this.fireEvent("afterselectionchange", this);
15614 handleDragableRowClick : function(grid, rowIndex, e)
15616 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15617 this.selectRow(rowIndex, false);
15618 grid.view.focusRow(rowIndex);
15619 this.fireEvent("afterselectionchange", this);
15624 * Selects multiple rows.
15625 * @param {Array} rows Array of the indexes of the row to select
15626 * @param {Boolean} keepExisting (optional) True to keep existing selections
15628 selectRows : function(rows, keepExisting){
15630 this.clearSelections();
15632 for(var i = 0, len = rows.length; i < len; i++){
15633 this.selectRow(rows[i], true);
15638 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15639 * @param {Number} startRow The index of the first row in the range
15640 * @param {Number} endRow The index of the last row in the range
15641 * @param {Boolean} keepExisting (optional) True to retain existing selections
15643 selectRange : function(startRow, endRow, keepExisting){
15644 if(this.locked) return;
15646 this.clearSelections();
15648 if(startRow <= endRow){
15649 for(var i = startRow; i <= endRow; i++){
15650 this.selectRow(i, true);
15653 for(var i = startRow; i >= endRow; i--){
15654 this.selectRow(i, true);
15660 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15661 * @param {Number} startRow The index of the first row in the range
15662 * @param {Number} endRow The index of the last row in the range
15664 deselectRange : function(startRow, endRow, preventViewNotify){
15665 if(this.locked) return;
15666 for(var i = startRow; i <= endRow; i++){
15667 this.deselectRow(i, preventViewNotify);
15673 * @param {Number} row The index of the row to select
15674 * @param {Boolean} keepExisting (optional) True to keep existing selections
15676 selectRow : function(index, keepExisting, preventViewNotify){
15677 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15678 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15679 if(!keepExisting || this.singleSelect){
15680 this.clearSelections();
15682 var r = this.grid.dataSource.getAt(index);
15683 this.selections.add(r);
15684 this.last = this.lastActive = index;
15685 if(!preventViewNotify){
15686 this.grid.getView().onRowSelect(index);
15688 this.fireEvent("rowselect", this, index, r);
15689 this.fireEvent("selectionchange", this);
15695 * @param {Number} row The index of the row to deselect
15697 deselectRow : function(index, preventViewNotify){
15698 if(this.locked) return;
15699 if(this.last == index){
15702 if(this.lastActive == index){
15703 this.lastActive = false;
15705 var r = this.grid.dataSource.getAt(index);
15706 this.selections.remove(r);
15707 if(!preventViewNotify){
15708 this.grid.getView().onRowDeselect(index);
15710 this.fireEvent("rowdeselect", this, index);
15711 this.fireEvent("selectionchange", this);
15715 restoreLast : function(){
15717 this.last = this._last;
15722 acceptsNav : function(row, col, cm){
15723 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15727 onEditorKey : function(field, e){
15728 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15733 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15735 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15737 }else if(k == e.ENTER && !e.ctrlKey){
15741 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15743 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15745 }else if(k == e.ESC){
15749 g.startEditing(newCell[0], newCell[1]);
15760 * @class Roo.bootstrap.MessageBar
15761 * @extends Roo.bootstrap.Component
15762 * Bootstrap MessageBar class
15763 * @cfg {String} html contents of the MessageBar
15764 * @cfg {String} weight (info | success | warning | danger) default info
15765 * @cfg {String} beforeClass insert the bar before the given class
15766 * @cfg {Boolean} closable (true | false) default false
15767 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15770 * Create a new Element
15771 * @param {Object} config The config object
15774 Roo.bootstrap.MessageBar = function(config){
15775 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15778 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15784 beforeClass: 'bootstrap-sticky-wrap',
15786 getAutoCreate : function(){
15790 cls: 'alert alert-dismissable alert-' + this.weight,
15795 html: this.html || ''
15801 cfg.cls += ' alert-messages-fixed';
15815 onRender : function(ct, position)
15817 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15820 var cfg = Roo.apply({}, this.getAutoCreate());
15824 cfg.cls += ' ' + this.cls;
15827 cfg.style = this.style;
15829 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15831 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15834 this.el.select('>button.close').on('click', this.hide, this);
15840 if (!this.rendered) {
15846 this.fireEvent('show', this);
15852 if (!this.rendered) {
15858 this.fireEvent('hide', this);
15861 update : function()
15863 // var e = this.el.dom.firstChild;
15865 // if(this.closable){
15866 // e = e.nextSibling;
15869 // e.data = this.html || '';
15871 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';