4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
21 * Do not use directly - it does not do anything..
22 * @param {Object} config The config object
27 Roo.bootstrap.Component = function(config){
28 Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
34 allowDomMove : false, // to stop relocations in parent onRender...
42 initEvents : function() { },
48 can_build_overlaid : true,
55 // returns the parent component..
56 return Roo.ComponentMgr.get(this.parentId)
62 onRender : function(ct, position)
64 // Roo.log("Call onRender: " + this.xtype);
66 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
69 if (this.el.attr('xtype')) {
70 this.el.attr('xtypex', this.el.attr('xtype'));
71 this.el.dom.removeAttribute('xtype');
81 var cfg = Roo.apply({}, this.getAutoCreate());
84 // fill in the extra attributes
85 if (this.xattr && typeof(this.xattr) =='object') {
86 for (var i in this.xattr) {
87 cfg[i] = this.xattr[i];
92 cfg.dataId = this.dataId;
96 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
99 if (this.style) { // fixme needs to support more complex style data.
100 cfg.style = this.style;
104 cfg.name = this.name;
107 this.el = ct.createChild(cfg, position);
109 if(this.tabIndex !== undefined){
110 this.el.dom.setAttribute('tabIndex', this.tabIndex);
117 getChildContainer : function()
123 addxtype : function(tree,cntr)
127 cn = Roo.factory(tree);
129 cn.parentType = this.xtype; //??
130 cn.parentId = this.id;
132 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
134 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
136 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
138 var build_from_html = Roo.XComponent.build_from_html;
140 var is_body = (tree.xtype == 'Body') ;
142 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
144 var self_cntr_el = Roo.get(this[cntr]());
146 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148 return this.addxtypeChild(tree,cntr);
151 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
154 return this.addxtypeChild(Roo.apply({}, tree),cntr);
157 Roo.log('skipping render');
165 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
171 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
175 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
180 addxtypeChild : function (tree, cntr)
182 Roo.log('addxtypeChild:' + cntr);
184 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
187 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188 (typeof(tree['flexy:foreach']) != 'undefined');
193 // render the element if it's not BODY.
194 if (tree.xtype != 'Body') {
196 cn = Roo.factory(tree);
198 cn.parentType = this.xtype; //??
199 cn.parentId = this.id;
201 var build_from_html = Roo.XComponent.build_from_html;
204 // does the container contain child eleemnts with 'xtype' attributes.
205 // that match this xtype..
206 // note - when we render we create these as well..
207 // so we should check to see if body has xtype set.
208 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
210 var self_cntr_el = Roo.get(this[cntr]());
211 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
213 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
220 //echild.dom.removeAttribute('xtype');
222 Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229 // if object has flexy:if - then it may or may not be rendered.
230 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
231 // skip a flexy if element.
232 Roo.log('skipping render');
235 // actually if flexy:foreach is found, we really want to create
236 // multiple copies here...
238 //Roo.log(this[cntr]());
239 cn.render(this[cntr]());
241 // then add the element..
248 if (typeof (tree.menu) != 'undefined') {
249 tree.menu.parentType = cn.xtype;
250 tree.menu.triggerEl = cn.el;
251 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
255 if (!tree.items || !tree.items.length) {
259 var items = tree.items;
262 //Roo.log(items.length);
264 for(var i =0;i < items.length;i++) {
265 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
286 * @class Roo.bootstrap.Body
287 * @extends Roo.bootstrap.Component
288 * Bootstrap Body class
292 * @param {Object} config The config object
295 Roo.bootstrap.Body = function(config){
296 Roo.bootstrap.Body.superclass.constructor.call(this, config);
297 this.el = Roo.get(document.body);
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
305 onRender : function(ct, position){
308 //this.el.addClass([this.fieldClass, this.cls]);
326 * @class Roo.bootstrap.ButtonGroup
327 * @extends Roo.bootstrap.Component
328 * Bootstrap ButtonGroup class
329 * @cfg {String} size lg | sm | xs (default empty normal)
330 * @cfg {String} align vertical | justified (default none)
331 * @cfg {String} direction up | down (default down)
332 * @cfg {Boolean} toolbar false | true
333 * @cfg {Boolean} btn true | false
338 * @param {Object} config The config object
341 Roo.bootstrap.ButtonGroup = function(config){
342 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
353 getAutoCreate : function(){
359 cfg.html = this.html || cfg.html;
370 if (['vertical','justified'].indexOf(this.align)!==-1) {
371 cfg.cls = 'btn-group-' + this.align;
373 if (this.align == 'justified') {
374 console.log(this.items);
378 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379 cfg.cls += ' btn-group-' + this.size;
382 if (this.direction == 'up') {
383 cfg.cls += ' dropup' ;
399 * @class Roo.bootstrap.Button
400 * @extends Roo.bootstrap.Component
401 * Bootstrap Button class
402 * @cfg {String} html The button content
403 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404 * @cfg {String} size empty | lg | sm | xs
405 * @cfg {String} tag empty | a | input | submit
406 * @cfg {String} href empty or href
407 * @cfg {Boolean} disabled false | true
408 * @cfg {Boolean} isClose false | true
409 * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410 * @cfg {String} badge text for badge
411 * @cfg {String} theme default (or empty) | glow
412 * @cfg {Boolean} inverse false | true
413 * @cfg {Boolean} toggle false | true
414 * @cfg {String} ontext text for on toggle state
415 * @cfg {String} offtext text for off toggle state
416 * @cfg {Boolean} defaulton true | false
417 * @cfg {Boolean} preventDefault (true | false) default true
418 * @cfg {Boolean} removeClass true | false remove the standard class..
419 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
422 * Create a new button
423 * @param {Object} config The config object
427 Roo.bootstrap.Button = function(config){
428 Roo.bootstrap.Button.superclass.constructor.call(this, config);
433 * When a butotn is pressed
434 * @param {Roo.EventObject} e
439 * After the button has been toggles
440 * @param {Roo.EventObject} e
441 * @param {boolean} pressed (also available as button.pressed)
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
465 preventDefault: true,
474 getAutoCreate : function(){
482 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
488 cfg.html = this.html || cfg.html;
490 if (this.toggle == true) {
493 cls: 'slider-frame roo-button',
498 'data-off-text':'OFF',
499 cls: 'slider-button',
505 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506 cfg.cls += ' '+this.weight;
515 cfg["aria-hidden"] = true;
517 cfg.html = "×";
523 if (this.theme==='default') {
524 cfg.cls = 'btn roo-button';
526 //if (this.parentType != 'Navbar') {
527 this.weight = this.weight.length ? this.weight : 'default';
529 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
531 cfg.cls += ' btn-' + this.weight;
533 } else if (this.theme==='glow') {
536 cfg.cls = 'btn-glow roo-button';
538 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
540 cfg.cls += ' ' + this.weight;
546 this.cls += ' inverse';
551 cfg.cls += ' active';
555 cfg.disabled = 'disabled';
559 Roo.log('changing to ul' );
561 this.glyphicon = 'caret';
564 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
566 //gsRoo.log(this.parentType);
567 if (this.parentType === 'Navbar' && !this.parent().bar) {
568 Roo.log('changing to li?');
577 href : this.href || '#'
580 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
581 cfg.cls += ' dropdown';
588 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
590 if (this.glyphicon) {
591 cfg.html = ' ' + cfg.html;
596 cls: 'glyphicon glyphicon-' + this.glyphicon
606 // cfg.cls='btn roo-button';
610 var value = cfg.html;
615 cls: 'glyphicon glyphicon-' + this.glyphicon,
634 cfg.cls += ' dropdown';
635 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
638 if (cfg.tag !== 'a' && this.href !== '') {
639 throw "Tag must be a to set href.";
640 } else if (this.href.length > 0) {
641 cfg.href = this.href;
644 if(this.removeClass){
649 cfg.target = this.target;
654 initEvents: function() {
655 // Roo.log('init events?');
656 // Roo.log(this.el.dom);
657 if (this.el.hasClass('roo-button')) {
658 this.el.on('click', this.onClick, this);
660 this.el.select('.roo-button').on('click', this.onClick, this);
666 onClick : function(e)
672 Roo.log('button on click ');
673 if(this.preventDefault){
676 if (this.pressed === true || this.pressed === false) {
677 this.pressed = !this.pressed;
678 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679 this.fireEvent('toggle', this, e, this.pressed);
683 this.fireEvent('click', this, e);
687 * Enables this button
691 this.disabled = false;
692 this.el.removeClass('disabled');
696 * Disable this button
700 this.disabled = true;
701 this.el.addClass('disabled');
704 * sets the active state on/off,
705 * @param {Boolean} state (optional) Force a particular state
707 setActive : function(v) {
709 this.el[v ? 'addClass' : 'removeClass']('active');
712 * toggles the current active state
714 toggleActive : function()
716 var active = this.el.hasClass('active');
717 this.setActive(!active);
734 * @class Roo.bootstrap.Column
735 * @extends Roo.bootstrap.Component
736 * Bootstrap Column class
737 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739 * @cfg {Number} md colspan out of 12 for computer-sized screens
740 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741 * @cfg {String} html content of column.
744 * Create a new Column
745 * @param {Object} config The config object
748 Roo.bootstrap.Column = function(config){
749 Roo.bootstrap.Column.superclass.constructor.call(this, config);
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
761 getAutoCreate : function(){
762 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
770 ['xs','sm','md','lg'].map(function(size){
771 if (settings[size]) {
772 cfg.cls += ' col-' + size + '-' + settings[size];
775 if (this.html.length) {
776 cfg.html = this.html;
795 * @class Roo.bootstrap.Container
796 * @extends Roo.bootstrap.Component
797 * Bootstrap Container class
798 * @cfg {Boolean} jumbotron is it a jumbotron element
799 * @cfg {String} html content of element
800 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802 * @cfg {String} header content of header (for panel)
803 * @cfg {String} footer content of footer (for panel)
804 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
807 * Create a new Container
808 * @param {Object} config The config object
811 Roo.bootstrap.Container = function(config){
812 Roo.bootstrap.Container.superclass.constructor.call(this, config);
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
825 getChildContainer : function() {
831 if (this.panel.length) {
832 return this.el.select('.panel-body',true).first();
839 getAutoCreate : function(){
845 if (this.jumbotron) {
846 cfg.cls = 'jumbotron';
849 cfg.cls = this.cls + '';
852 if (this.sticky.length) {
854 var bd = Roo.get(document.body);
855 if (!bd.hasClass('bootstrap-sticky')) {
856 bd.addClass('bootstrap-sticky');
857 Roo.select('html',true).setStyle('height', '100%');
860 cfg.cls += 'bootstrap-sticky-' + this.sticky;
864 if (this.well.length) {
868 cfg.cls +=' well well-' +this.well;
878 if (this.panel.length) {
879 cfg.cls += ' panel panel-' + this.panel;
881 if (this.header.length) {
884 cls : 'panel-heading',
900 if (this.footer.length) {
902 cls : 'panel-footer',
910 body.html = this.html || cfg.html;
912 if (!cfg.cls.length) {
913 cfg.cls = 'container';
930 * @class Roo.bootstrap.Img
931 * @extends Roo.bootstrap.Component
932 * Bootstrap Img class
933 * @cfg {Boolean} imgResponsive false | true
934 * @cfg {String} border rounded | circle | thumbnail
935 * @cfg {String} src image source
936 * @cfg {String} alt image alternative text
937 * @cfg {String} href a tag href
938 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
942 * @param {Object} config The config object
945 Roo.bootstrap.Img = function(config){
946 Roo.bootstrap.Img.superclass.constructor.call(this, config);
952 * The img click event for the img.
953 * @param {Roo.EventObject} e
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
967 getAutoCreate : function(){
971 cls: 'img-responsive',
975 cfg.html = this.html || cfg.html;
977 cfg.src = this.src || cfg.src;
979 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980 cfg.cls += ' img-' + this.border;
997 a.target = this.target;
1003 return (this.href) ? a : cfg;
1006 initEvents: function() {
1009 this.el.on('click', this.onClick, this);
1013 onClick : function(e)
1015 Roo.log('img onclick');
1016 this.fireEvent('click', this, e);
1029 * @class Roo.bootstrap.Header
1030 * @extends Roo.bootstrap.Component
1031 * Bootstrap Header class
1032 * @cfg {String} html content of header
1033 * @cfg {Number} level (1|2|3|4|5|6) default 1
1036 * Create a new Header
1037 * @param {Object} config The config object
1041 Roo.bootstrap.Header = function(config){
1042 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1053 getAutoCreate : function(){
1056 tag: 'h' + (1 *this.level),
1057 html: this.html || 'fill in html'
1069 * Ext JS Library 1.1.1
1070 * Copyright(c) 2006-2007, Ext JS, LLC.
1072 * Originally Released Under LGPL - original licence link has changed is not relivant.
1075 * <script type="text/javascript">
1079 * @class Roo.bootstrap.MenuMgr
1080 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1083 Roo.bootstrap.MenuMgr = function(){
1084 var menus, active, groups = {}, attached = false, lastShow = new Date();
1086 // private - called when first menu is created
1089 active = new Roo.util.MixedCollection();
1090 Roo.get(document).addKeyListener(27, function(){
1091 if(active.length > 0){
1099 if(active && active.length > 0){
1100 var c = active.clone();
1110 if(active.length < 1){
1111 Roo.get(document).un("mouseup", onMouseDown);
1119 var last = active.last();
1120 lastShow = new Date();
1123 Roo.get(document).on("mouseup", onMouseDown);
1128 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129 m.parentMenu.activeChild = m;
1130 }else if(last && last.isVisible()){
1131 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1136 function onBeforeHide(m){
1138 m.activeChild.hide();
1140 if(m.autoHideTimer){
1141 clearTimeout(m.autoHideTimer);
1142 delete m.autoHideTimer;
1147 function onBeforeShow(m){
1148 var pm = m.parentMenu;
1149 if(!pm && !m.allowOtherMenus){
1151 }else if(pm && pm.activeChild && active != m){
1152 pm.activeChild.hide();
1157 function onMouseDown(e){
1158 Roo.log("on MouseDown");
1159 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1167 function onBeforeCheck(mi, state){
1169 var g = groups[mi.group];
1170 for(var i = 0, l = g.length; i < l; i++){
1172 g[i].setChecked(false);
1181 * Hides all menus that are currently visible
1183 hideAll : function(){
1188 register : function(menu){
1192 menus[menu.id] = menu;
1193 menu.on("beforehide", onBeforeHide);
1194 menu.on("hide", onHide);
1195 menu.on("beforeshow", onBeforeShow);
1196 menu.on("show", onShow);
1198 if(g && menu.events["checkchange"]){
1202 groups[g].push(menu);
1203 menu.on("checkchange", onCheck);
1208 * Returns a {@link Roo.menu.Menu} object
1209 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210 * be used to generate and return a new Menu instance.
1212 get : function(menu){
1213 if(typeof menu == "string"){ // menu id
1215 }else if(menu.events){ // menu instance
1218 /*else if(typeof menu.length == 'number'){ // array of menu items?
1219 return new Roo.bootstrap.Menu({items:menu});
1220 }else{ // otherwise, must be a config
1221 return new Roo.bootstrap.Menu(menu);
1228 unregister : function(menu){
1229 delete menus[menu.id];
1230 menu.un("beforehide", onBeforeHide);
1231 menu.un("hide", onHide);
1232 menu.un("beforeshow", onBeforeShow);
1233 menu.un("show", onShow);
1235 if(g && menu.events["checkchange"]){
1236 groups[g].remove(menu);
1237 menu.un("checkchange", onCheck);
1242 registerCheckable : function(menuItem){
1243 var g = menuItem.group;
1248 groups[g].push(menuItem);
1249 menuItem.on("beforecheckchange", onBeforeCheck);
1254 unregisterCheckable : function(menuItem){
1255 var g = menuItem.group;
1257 groups[g].remove(menuItem);
1258 menuItem.un("beforecheckchange", onBeforeCheck);
1270 * @class Roo.bootstrap.Menu
1271 * @extends Roo.bootstrap.Component
1272 * Bootstrap Menu class - container for MenuItems
1273 * @cfg {String} type type of menu
1277 * @param {Object} config The config object
1281 Roo.bootstrap.Menu = function(config){
1282 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283 if (this.registerMenu) {
1284 Roo.bootstrap.MenuMgr.register(this);
1289 * Fires before this menu is displayed
1290 * @param {Roo.menu.Menu} this
1295 * Fires before this menu is hidden
1296 * @param {Roo.menu.Menu} this
1301 * Fires after this menu is displayed
1302 * @param {Roo.menu.Menu} this
1307 * Fires after this menu is hidden
1308 * @param {Roo.menu.Menu} this
1313 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314 * @param {Roo.menu.Menu} this
1315 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316 * @param {Roo.EventObject} e
1321 * Fires when the mouse is hovering over this menu
1322 * @param {Roo.menu.Menu} this
1323 * @param {Roo.EventObject} e
1324 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1329 * Fires when the mouse exits this menu
1330 * @param {Roo.menu.Menu} this
1331 * @param {Roo.EventObject} e
1332 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1337 * Fires when a menu item contained in this menu is clicked
1338 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339 * @param {Roo.EventObject} e
1343 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1350 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1353 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1355 registerMenu : true,
1357 menuItems :false, // stores the menu items..
1363 getChildContainer : function() {
1367 getAutoCreate : function(){
1369 //if (['right'].indexOf(this.align)!==-1) {
1370 // cfg.cn[1].cls += ' pull-right'
1374 cls : 'dropdown-menu' ,
1375 style : 'z-index:1000'
1379 if (this.type === 'submenu') {
1380 cfg.cls = 'submenu active'
1385 initEvents : function() {
1387 // Roo.log("ADD event");
1388 // Roo.log(this.triggerEl.dom);
1389 this.triggerEl.on('click', this.onTriggerPress, this);
1390 this.triggerEl.addClass('dropdown-toggle');
1391 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1393 this.el.on("mouseover", this.onMouseOver, this);
1394 this.el.on("mouseout", this.onMouseOut, this);
1398 findTargetItem : function(e){
1399 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1403 //Roo.log(t); Roo.log(t.id);
1405 //Roo.log(this.menuitems);
1406 return this.menuitems.get(t.id);
1408 //return this.items.get(t.menuItemId);
1413 onClick : function(e){
1414 Roo.log("menu.onClick");
1415 var t = this.findTargetItem(e);
1421 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1422 if(t == this.activeItem && t.shouldDeactivate(e)){
1423 this.activeItem.deactivate();
1424 delete this.activeItem;
1428 this.setActiveItem(t, true);
1435 Roo.log('pass click event');
1439 this.fireEvent("click", this, t, e);
1443 onMouseOver : function(e){
1444 var t = this.findTargetItem(e);
1447 // if(t.canActivate && !t.disabled){
1448 // this.setActiveItem(t, true);
1452 this.fireEvent("mouseover", this, e, t);
1454 isVisible : function(){
1455 return !this.hidden;
1457 onMouseOut : function(e){
1458 var t = this.findTargetItem(e);
1461 // if(t == this.activeItem && t.shouldDeactivate(e)){
1462 // this.activeItem.deactivate();
1463 // delete this.activeItem;
1466 this.fireEvent("mouseout", this, e, t);
1471 * Displays this menu relative to another element
1472 * @param {String/HTMLElement/Roo.Element} element The element to align to
1473 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474 * the element (defaults to this.defaultAlign)
1475 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1477 show : function(el, pos, parentMenu){
1478 this.parentMenu = parentMenu;
1482 this.fireEvent("beforeshow", this);
1483 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1486 * Displays this menu at a specific xy position
1487 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1490 showAt : function(xy, parentMenu, /* private: */_e){
1491 this.parentMenu = parentMenu;
1496 this.fireEvent("beforeshow", this);
1498 //xy = this.el.adjustForConstraints(xy);
1500 //this.el.setXY(xy);
1502 this.hideMenuItems();
1503 this.hidden = false;
1504 this.triggerEl.addClass('open');
1506 this.fireEvent("show", this);
1512 this.doFocus.defer(50, this);
1516 doFocus : function(){
1518 this.focusEl.focus();
1523 * Hides this menu and optionally all parent menus
1524 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1526 hide : function(deep){
1528 this.hideMenuItems();
1529 if(this.el && this.isVisible()){
1530 this.fireEvent("beforehide", this);
1531 if(this.activeItem){
1532 this.activeItem.deactivate();
1533 this.activeItem = null;
1535 this.triggerEl.removeClass('open');;
1537 this.fireEvent("hide", this);
1539 if(deep === true && this.parentMenu){
1540 this.parentMenu.hide(true);
1544 onTriggerPress : function(e)
1547 Roo.log('trigger press');
1548 //Roo.log(e.getTarget());
1549 // Roo.log(this.triggerEl.dom);
1550 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1553 if (this.isVisible()) {
1557 this.show(this.triggerEl, false, false);
1566 hideMenuItems : function()
1568 //$(backdrop).remove()
1569 Roo.select('.open',true).each(function(aa) {
1571 aa.removeClass('open');
1572 //var parent = getParent($(this))
1573 //var relatedTarget = { relatedTarget: this }
1575 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576 //if (e.isDefaultPrevented()) return
1577 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1580 addxtypeChild : function (tree, cntr) {
1581 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1583 this.menuitems.add(comp);
1604 * @class Roo.bootstrap.MenuItem
1605 * @extends Roo.bootstrap.Component
1606 * Bootstrap MenuItem class
1607 * @cfg {String} html the menu label
1608 * @cfg {String} href the link
1609 * @cfg {Boolean} preventDefault (true | false) default true
1613 * Create a new MenuItem
1614 * @param {Object} config The config object
1618 Roo.bootstrap.MenuItem = function(config){
1619 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1624 * The raw click event for the entire grid.
1625 * @param {Roo.EventObject} e
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1635 preventDefault: true,
1637 getAutoCreate : function(){
1640 cls: 'dropdown-menu-item',
1650 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1655 initEvents: function() {
1657 //this.el.select('a').on('click', this.onClick, this);
1660 onClick : function(e)
1662 Roo.log('item on click ');
1663 //if(this.preventDefault){
1664 // e.preventDefault();
1666 //this.parent().hideMenuItems();
1668 this.fireEvent('click', this, e);
1687 * @class Roo.bootstrap.MenuSeparator
1688 * @extends Roo.bootstrap.Component
1689 * Bootstrap MenuSeparator class
1692 * Create a new MenuItem
1693 * @param {Object} config The config object
1697 Roo.bootstrap.MenuSeparator = function(config){
1698 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1703 getAutoCreate : function(){
1718 <div class="modal fade">
1719 <div class="modal-dialog">
1720 <div class="modal-content">
1721 <div class="modal-header">
1722 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1723 <h4 class="modal-title">Modal title</h4>
1725 <div class="modal-body">
1726 <p>One fine body…</p>
1728 <div class="modal-footer">
1729 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730 <button type="button" class="btn btn-primary">Save changes</button>
1732 </div><!-- /.modal-content -->
1733 </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1744 * @class Roo.bootstrap.Modal
1745 * @extends Roo.bootstrap.Component
1746 * Bootstrap Modal class
1747 * @cfg {String} title Title of dialog
1748 * @cfg {Array} buttons Array of buttons or standard button set..
1751 * Create a new Modal Dialog
1752 * @param {Object} config The config object
1755 Roo.bootstrap.Modal = function(config){
1756 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1761 * The raw btnclick event for the button
1762 * @param {Roo.EventObject} e
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1770 title : 'test dialog',
1774 onRender : function(ct, position)
1776 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1779 var cfg = Roo.apply({}, this.getAutoCreate());
1782 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1784 //if (!cfg.name.length) {
1788 cfg.cls += ' ' + this.cls;
1791 cfg.style = this.style;
1793 this.el = Roo.get(document.body).createChild(cfg, position);
1795 //var type = this.el.dom.type;
1797 if(this.tabIndex !== undefined){
1798 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1803 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804 this.maskEl.enableDisplayMode("block");
1806 //this.el.addClass("x-dlg-modal");
1809 Roo.each(this.buttons, function(bb) {
1810 b = Roo.apply({}, bb);
1811 b.xns = b.xns || Roo.bootstrap;
1812 b.xtype = b.xtype || 'Button';
1813 if (typeof(b.listeners) == 'undefined') {
1814 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1817 var btn = Roo.factory(b);
1819 btn.onRender(this.el.select('.modal-footer').first());
1823 // render the children.
1826 if(typeof(this.items) != 'undefined'){
1827 var items = this.items;
1830 for(var i =0;i < items.length;i++) {
1831 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1835 this.items = nitems;
1837 //this.el.addClass([this.fieldClass, this.cls]);
1840 getAutoCreate : function(){
1845 html : this.html || ''
1853 cls: "modal-dialog",
1856 cls : "modal-content",
1859 cls : 'modal-header',
1868 cls : 'modal-title',
1876 cls : 'modal-footer'
1892 getChildContainer : function() {
1894 return this.el.select('.modal-body',true).first();
1897 getButtonContainer : function() {
1898 return this.el.select('.modal-footer',true).first();
1901 initEvents : function()
1903 this.el.select('.modal-header .close').on('click', this.hide, this);
1905 // this.addxtype(this);
1909 if (!this.rendered) {
1913 this.el.addClass('on');
1914 this.el.removeClass('fade');
1915 this.el.setStyle('display', 'block');
1916 Roo.get(document.body).addClass("x-body-masked");
1917 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1919 this.el.setStyle('zIndex', '10001');
1920 this.fireEvent('show', this);
1926 Roo.log('Modal hide?!');
1928 Roo.get(document.body).removeClass("x-body-masked");
1929 this.el.removeClass('on');
1930 this.el.addClass('fade');
1931 this.el.setStyle('display', 'none');
1932 this.fireEvent('hide', this);
1934 onButtonClick: function(btn,e)
1937 this.fireEvent('btnclick', btn.name, e);
1942 Roo.apply(Roo.bootstrap.Modal, {
1944 * Button config that displays a single OK button
1953 * Button config that displays Yes and No buttons
1969 * Button config that displays OK and Cancel buttons
1984 * Button config that displays Yes, No and Cancel buttons
2011 * @class Roo.bootstrap.Navbar
2012 * @extends Roo.bootstrap.Component
2013 * Bootstrap Navbar class
2014 * @cfg {Boolean} sidebar has side bar
2015 * @cfg {Boolean} bar is a bar?
2016 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017 * @cfg {String} brand what is brand
2018 * @cfg {Boolean} inverse is inverted color
2019 * @cfg {String} type (nav | pills | tabs)
2020 * @cfg {Boolean} arrangement stacked | justified
2021 * @cfg {String} align (left | right) alignment
2022 * @cfg {String} brand_href href of the brand
2023 * @cfg {Boolean} main (true|false) main nav bar? default false
2024 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2028 * Create a new Navbar
2029 * @param {Object} config The config object
2033 Roo.bootstrap.Navbar = function(config){
2034 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2052 getAutoCreate : function(){
2057 if (this.sidebar === true) {
2065 if (this.bar === true) {
2073 cls: 'navbar-header',
2078 cls: 'navbar-toggle',
2079 'data-toggle': 'collapse',
2084 html: 'Toggle navigation'
2104 cls: 'collapse navbar-collapse'
2109 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2111 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112 cfg.cls += ' navbar-' + this.position;
2113 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2116 if (this.brand !== '') {
2119 href: this.brand_href ? this.brand_href : '#',
2120 cls: 'navbar-brand',
2128 cfg.cls += ' main-nav';
2134 } else if (this.bar === false) {
2137 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2147 if (['tabs','pills'].indexOf(this.type)!==-1) {
2148 cfg.cn[0].cls += ' nav-' + this.type
2150 if (this.type!=='nav') {
2151 Roo.log('nav type must be nav/tabs/pills')
2153 cfg.cn[0].cls += ' navbar-nav'
2156 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157 cfg.cn[0].cls += ' nav-' + this.arrangement;
2160 if (this.align === 'right') {
2161 cfg.cn[0].cls += ' navbar-right';
2164 cfg.cls += ' navbar-inverse';
2172 initEvents :function ()
2174 //Roo.log(this.el.select('.navbar-toggle',true));
2175 this.el.select('.navbar-toggle',true).on('click', function() {
2176 // Roo.log('click');
2177 this.el.select('.navbar-collapse',true).toggleClass('in');
2185 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2187 var size = this.el.getSize();
2188 this.maskEl.setSize(size.width, size.height);
2189 this.maskEl.enableDisplayMode("block");
2198 getChildContainer : function()
2200 if (this.bar === true) {
2201 return this.el.select('.collapse',true).first();
2229 * @class Roo.bootstrap.NavGroup
2230 * @extends Roo.bootstrap.Component
2231 * Bootstrap NavGroup class
2232 * @cfg {String} align left | right
2233 * @cfg {Boolean} inverse false | true
2234 * @cfg {String} type (nav|pills|tab) default nav
2237 * Create a new nav group
2238 * @param {Object} config The config object
2241 Roo.bootstrap.NavGroup = function(config){
2242 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2252 getAutoCreate : function(){
2253 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2260 if (['tabs','pills'].indexOf(this.type)!==-1) {
2261 cfg.cls += ' nav-' + this.type
2263 if (this.type!=='nav') {
2264 Roo.log('nav type must be nav/tabs/pills')
2266 cfg.cls += ' navbar-nav'
2269 if (this.parent().sidebar === true) {
2272 cls: 'dashboard-menu'
2278 if (this.form === true) {
2284 if (this.align === 'right') {
2285 cfg.cls += ' navbar-right';
2287 cfg.cls += ' navbar-left';
2291 if (this.align === 'right') {
2292 cfg.cls += ' navbar-right';
2296 cfg.cls += ' navbar-inverse';
2316 * @class Roo.bootstrap.Navbar.Item
2317 * @extends Roo.bootstrap.Component
2318 * Bootstrap Navbar.Button class
2319 * @cfg {String} href link to
2320 * @cfg {String} html content of button
2321 * @cfg {String} badge text inside badge
2322 * @cfg {String} glyphicon name of glyphicon
2323 * @cfg {String} icon name of font awesome icon
2324 * @cfg {Boolena} active Is item active
2325 * @cfg {Boolean} preventDefault (true | false) default false
2328 * Create a new Navbar Button
2329 * @param {Object} config The config object
2331 Roo.bootstrap.Navbar.Item = function(config){
2332 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2337 * The raw click event for the entire grid.
2338 * @param {Roo.EventObject} e
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
2353 preventDefault : false,
2355 getAutoCreate : function(){
2357 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2359 if (this.parent().parent().sidebar === true) {
2372 cfg.cn[0].html = this.html;
2376 this.cls += ' active';
2380 cfg.cn[0].cls += ' dropdown-toggle';
2381 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2385 cfg.cn[0].tag = 'a',
2386 cfg.cn[0].href = this.href;
2389 if (this.glyphicon) {
2390 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2394 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2406 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2416 if (this.glyphicon) {
2417 if(cfg.html){cfg.html = ' ' + this.html};
2421 cls: 'glyphicon glyphicon-' + this.glyphicon
2426 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2431 cfg.cn[0].html += " <span class='caret'></span>";
2432 //}else if (!this.href) {
2433 // cfg.cn[0].tag='p';
2434 // cfg.cn[0].cls='navbar-text';
2437 cfg.cn[0].href=this.href||'#';
2438 cfg.cn[0].html=this.html;
2441 if (this.badge !== '') {
2444 cfg.cn[0].html + ' ',
2455 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2460 initEvents: function() {
2461 // Roo.log('init events?');
2462 // Roo.log(this.el.dom);
2463 this.el.select('a',true).on('click', this.onClick, this);
2466 onClick : function(e)
2468 if(this.preventDefault){
2472 if(this.fireEvent('click', this, e) === false){
2476 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477 this.onTabsClick(e);
2481 onTabsClick : function(e)
2483 Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484 v.removeClass('active');
2487 this.el.addClass('active');
2489 if(this.href && this.href.substring(0,1) == '#'){
2490 var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2492 Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493 v.removeClass('active');
2496 tab.addClass('active');
2511 * @class Roo.bootstrap.Row
2512 * @extends Roo.bootstrap.Component
2513 * Bootstrap Row class (contains columns...)
2517 * @param {Object} config The config object
2520 Roo.bootstrap.Row = function(config){
2521 Roo.bootstrap.Row.superclass.constructor.call(this, config);
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
2526 getAutoCreate : function(){
2545 * @class Roo.bootstrap.Element
2546 * @extends Roo.bootstrap.Component
2547 * Bootstrap Element class
2548 * @cfg {String} html contents of the element
2549 * @cfg {String} tag tag of the element
2550 * @cfg {String} cls class of the element
2553 * Create a new Element
2554 * @param {Object} config The config object
2557 Roo.bootstrap.Element = function(config){
2558 Roo.bootstrap.Element.superclass.constructor.call(this, config);
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
2568 getAutoCreate : function(){
2593 * @class Roo.bootstrap.Pagination
2594 * @extends Roo.bootstrap.Component
2595 * Bootstrap Pagination class
2596 * @cfg {String} size xs | sm | md | lg
2597 * @cfg {Boolean} inverse false | true
2600 * Create a new Pagination
2601 * @param {Object} config The config object
2604 Roo.bootstrap.Pagination = function(config){
2605 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
2614 getAutoCreate : function(){
2620 cfg.cls += ' inverse';
2626 cfg.cls += " " + this.cls;
2644 * @class Roo.bootstrap.PaginationItem
2645 * @extends Roo.bootstrap.Component
2646 * Bootstrap PaginationItem class
2647 * @cfg {String} html text
2648 * @cfg {String} href the link
2649 * @cfg {Boolean} preventDefault (true | false) default true
2650 * @cfg {Boolean} active (true | false) default false
2654 * Create a new PaginationItem
2655 * @param {Object} config The config object
2659 Roo.bootstrap.PaginationItem = function(config){
2660 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2665 * The raw click event for the entire grid.
2666 * @param {Roo.EventObject} e
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
2676 preventDefault: true,
2680 getAutoCreate : function(){
2686 href : this.href ? this.href : '#',
2687 html : this.html ? this.html : ''
2697 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2703 initEvents: function() {
2705 this.el.on('click', this.onClick, this);
2708 onClick : function(e)
2710 Roo.log('PaginationItem on click ');
2711 if(this.preventDefault){
2715 this.fireEvent('click', this, e);
2731 * @class Roo.bootstrap.Slider
2732 * @extends Roo.bootstrap.Component
2733 * Bootstrap Slider class
2736 * Create a new Slider
2737 * @param {Object} config The config object
2740 Roo.bootstrap.Slider = function(config){
2741 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
2746 getAutoCreate : function(){
2750 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2754 cls: 'ui-slider-handle ui-state-default ui-corner-all'
2772 * @class Roo.bootstrap.Table
2773 * @extends Roo.bootstrap.Component
2774 * Bootstrap Table class
2775 * @cfg {String} cls table class
2776 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777 * @cfg {String} bgcolor Specifies the background color for a table
2778 * @cfg {Number} border Specifies whether the table cells should have borders or not
2779 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780 * @cfg {Number} cellspacing Specifies the space between cells
2781 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783 * @cfg {String} sortable Specifies that the table should be sortable
2784 * @cfg {String} summary Specifies a summary of the content of a table
2785 * @cfg {Number} width Specifies the width of a table
2787 * @cfg {boolean} striped Should the rows be alternative striped
2788 * @cfg {boolean} bordered Add borders to the table
2789 * @cfg {boolean} hover Add hover highlighting
2790 * @cfg {boolean} condensed Format condensed
2791 * @cfg {boolean} responsive Format condensed
2797 * Create a new Table
2798 * @param {Object} config The config object
2801 Roo.bootstrap.Table = function(config){
2802 Roo.bootstrap.Table.superclass.constructor.call(this, config);
2805 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806 this.sm = this.selModel;
2807 this.sm.xmodule = this.xmodule || false;
2809 if (this.cm && typeof(this.cm.config) == 'undefined') {
2810 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811 this.cm = this.colModel;
2812 this.cm.xmodule = this.xmodule || false;
2815 this.store= Roo.factory(this.store, Roo.data);
2816 this.ds = this.store;
2817 this.ds.xmodule = this.xmodule || false;
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
2844 getAutoCreate : function(){
2845 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2854 cfg.cls += ' table-striped';
2857 cfg.cls += ' table-hover';
2859 if (this.bordered) {
2860 cfg.cls += ' table-bordered';
2862 if (this.condensed) {
2863 cfg.cls += ' table-condensed';
2865 if (this.responsive) {
2866 cfg.cls += ' table-responsive';
2873 cfg.cls+= ' ' +this.cls;
2876 // this lot should be simplifed...
2879 cfg.align=this.align;
2882 cfg.bgcolor=this.bgcolor;
2885 cfg.border=this.border;
2887 if (this.cellpadding) {
2888 cfg.cellpadding=this.cellpadding;
2890 if (this.cellspacing) {
2891 cfg.cellspacing=this.cellspacing;
2894 cfg.frame=this.frame;
2897 cfg.rules=this.rules;
2899 if (this.sortable) {
2900 cfg.sortable=this.sortable;
2903 cfg.summary=this.summary;
2906 cfg.width=this.width;
2909 if(this.store || this.cm){
2910 cfg.cn.push(this.renderHeader());
2911 cfg.cn.push(this.renderBody());
2912 cfg.cn.push(this.renderFooter());
2914 cfg.cls+= ' TableGrid';
2920 // initTableGrid : function()
2929 // var cm = this.cm;
2931 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2934 // html: cm.getColumnHeader(i)
2938 // cfg.push(header);
2945 initEvents : function()
2947 if(!this.store || !this.cm){
2951 Roo.log('initEvents with ds!!!!');
2953 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2954 // this.maskEl.enableDisplayMode("block");
2955 // this.maskEl.show();
2957 this.store.on('load', this.onLoad, this);
2958 this.store.on('beforeload', this.onBeforeLoad, this);
2966 renderHeader : function()
2975 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2978 html: cm.getColumnHeader(i)
2985 renderBody : function()
2995 renderFooter : function()
3007 Roo.log('ds onload');
3011 var tbody = this.el.select('tbody', true).first();
3015 if(this.store.getCount() > 0){
3016 this.store.data.each(function(d){
3022 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3023 var renderer = cm.getRenderer(i);
3024 var config = cm.config[i];
3028 if(typeof(renderer) !== 'undefined'){
3029 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3032 if(typeof(value) === 'object'){
3042 html: (typeof(value) === 'object') ? '' : value
3045 if(typeof(config.width) != 'undefined'){
3046 td.width = config.width;
3053 tbody.createChild(row);
3061 Roo.each(renders, function(r){
3062 _this.renderColumn(r);
3066 // if(this.loadMask){
3067 // this.maskEl.hide();
3071 onBeforeLoad : function()
3073 Roo.log('ds onBeforeLoad');
3077 // if(this.loadMask){
3078 // this.maskEl.show();
3084 this.el.select('tbody', true).first().dom.innerHTML = '';
3087 getSelectionModel : function(){
3089 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3091 return this.selModel;
3094 renderColumn : function(r)
3097 r.cfg.render(Roo.get(r.id));
3100 Roo.each(r.cfg.cn, function(c){
3105 _this.renderColumn(child);
3122 * @class Roo.bootstrap.TableCell
3123 * @extends Roo.bootstrap.Component
3124 * Bootstrap TableCell class
3125 * @cfg {String} html cell contain text
3126 * @cfg {String} cls cell class
3127 * @cfg {String} tag cell tag (td|th) default td
3128 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3129 * @cfg {String} align Aligns the content in a cell
3130 * @cfg {String} axis Categorizes cells
3131 * @cfg {String} bgcolor Specifies the background color of a cell
3132 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3133 * @cfg {Number} colspan Specifies the number of columns a cell should span
3134 * @cfg {String} headers Specifies one or more header cells a cell is related to
3135 * @cfg {Number} height Sets the height of a cell
3136 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3137 * @cfg {Number} rowspan Sets the number of rows a cell should span
3138 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3139 * @cfg {String} valign Vertical aligns the content in a cell
3140 * @cfg {Number} width Specifies the width of a cell
3143 * Create a new TableCell
3144 * @param {Object} config The config object
3147 Roo.bootstrap.TableCell = function(config){
3148 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3151 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3171 getAutoCreate : function(){
3172 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3192 cfg.align=this.align
3198 cfg.bgcolor=this.bgcolor
3201 cfg.charoff=this.charoff
3204 cfg.colspan=this.colspan
3207 cfg.headers=this.headers
3210 cfg.height=this.height
3213 cfg.nowrap=this.nowrap
3216 cfg.rowspan=this.rowspan
3219 cfg.scope=this.scope
3222 cfg.valign=this.valign
3225 cfg.width=this.width
3244 * @class Roo.bootstrap.TableRow
3245 * @extends Roo.bootstrap.Component
3246 * Bootstrap TableRow class
3247 * @cfg {String} cls row class
3248 * @cfg {String} align Aligns the content in a table row
3249 * @cfg {String} bgcolor Specifies a background color for a table row
3250 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3251 * @cfg {String} valign Vertical aligns the content in a table row
3254 * Create a new TableRow
3255 * @param {Object} config The config object
3258 Roo.bootstrap.TableRow = function(config){
3259 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3262 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3270 getAutoCreate : function(){
3271 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3281 cfg.align = this.align;
3284 cfg.bgcolor = this.bgcolor;
3287 cfg.charoff = this.charoff;
3290 cfg.valign = this.valign;
3308 * @class Roo.bootstrap.TableBody
3309 * @extends Roo.bootstrap.Component
3310 * Bootstrap TableBody class
3311 * @cfg {String} cls element class
3312 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3313 * @cfg {String} align Aligns the content inside the element
3314 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3315 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3318 * Create a new TableBody
3319 * @param {Object} config The config object
3322 Roo.bootstrap.TableBody = function(config){
3323 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3326 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3334 getAutoCreate : function(){
3335 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3349 cfg.align = this.align;
3352 cfg.charoff = this.charoff;
3355 cfg.valign = this.valign;
3362 // initEvents : function()
3369 // this.store = Roo.factory(this.store, Roo.data);
3370 // this.store.on('load', this.onLoad, this);
3372 // this.store.load();
3376 // onLoad: function ()
3378 // this.fireEvent('load', this);
3388 * Ext JS Library 1.1.1
3389 * Copyright(c) 2006-2007, Ext JS, LLC.
3391 * Originally Released Under LGPL - original licence link has changed is not relivant.
3394 * <script type="text/javascript">
3397 // as we use this in bootstrap.
3398 Roo.namespace('Roo.form');
3400 * @class Roo.form.Action
3401 * Internal Class used to handle form actions
3403 * @param {Roo.form.BasicForm} el The form element or its id
3404 * @param {Object} config Configuration options
3409 // define the action interface
3410 Roo.form.Action = function(form, options){
3412 this.options = options || {};
3415 * Client Validation Failed
3418 Roo.form.Action.CLIENT_INVALID = 'client';
3420 * Server Validation Failed
3423 Roo.form.Action.SERVER_INVALID = 'server';
3425 * Connect to Server Failed
3428 Roo.form.Action.CONNECT_FAILURE = 'connect';
3430 * Reading Data from Server Failed
3433 Roo.form.Action.LOAD_FAILURE = 'load';
3435 Roo.form.Action.prototype = {
3437 failureType : undefined,
3438 response : undefined,
3442 run : function(options){
3447 success : function(response){
3452 handleResponse : function(response){
3456 // default connection failure
3457 failure : function(response){
3459 this.response = response;
3460 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3461 this.form.afterAction(this, false);
3464 processResponse : function(response){
3465 this.response = response;
3466 if(!response.responseText){
3469 this.result = this.handleResponse(response);
3473 // utility functions used internally
3474 getUrl : function(appendParams){
3475 var url = this.options.url || this.form.url || this.form.el.dom.action;
3477 var p = this.getParams();
3479 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3485 getMethod : function(){
3486 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3489 getParams : function(){
3490 var bp = this.form.baseParams;
3491 var p = this.options.params;
3493 if(typeof p == "object"){
3494 p = Roo.urlEncode(Roo.applyIf(p, bp));
3495 }else if(typeof p == 'string' && bp){
3496 p += '&' + Roo.urlEncode(bp);
3499 p = Roo.urlEncode(bp);
3504 createCallback : function(){
3506 success: this.success,
3507 failure: this.failure,
3509 timeout: (this.form.timeout*1000),
3510 upload: this.form.fileUpload ? this.success : undefined
3515 Roo.form.Action.Submit = function(form, options){
3516 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3519 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3522 haveProgress : false,
3523 uploadComplete : false,
3525 // uploadProgress indicator.
3526 uploadProgress : function()
3528 if (!this.form.progressUrl) {
3532 if (!this.haveProgress) {
3533 Roo.MessageBox.progress("Uploading", "Uploading");
3535 if (this.uploadComplete) {
3536 Roo.MessageBox.hide();
3540 this.haveProgress = true;
3542 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3544 var c = new Roo.data.Connection();
3546 url : this.form.progressUrl,
3551 success : function(req){
3552 //console.log(data);
3556 rdata = Roo.decode(req.responseText)
3558 Roo.log("Invalid data from server..");
3562 if (!rdata || !rdata.success) {
3564 Roo.MessageBox.alert(Roo.encode(rdata));
3567 var data = rdata.data;
3569 if (this.uploadComplete) {
3570 Roo.MessageBox.hide();
3575 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3576 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3579 this.uploadProgress.defer(2000,this);
3582 failure: function(data) {
3583 Roo.log('progress url failed ');
3594 // run get Values on the form, so it syncs any secondary forms.
3595 this.form.getValues();
3597 var o = this.options;
3598 var method = this.getMethod();
3599 var isPost = method == 'POST';
3600 if(o.clientValidation === false || this.form.isValid()){
3602 if (this.form.progressUrl) {
3603 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3604 (new Date() * 1) + '' + Math.random());
3609 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3610 form:this.form.el.dom,
3611 url:this.getUrl(!isPost),
3613 params:isPost ? this.getParams() : null,
3614 isUpload: this.form.fileUpload
3617 this.uploadProgress();
3619 }else if (o.clientValidation !== false){ // client validation failed
3620 this.failureType = Roo.form.Action.CLIENT_INVALID;
3621 this.form.afterAction(this, false);
3625 success : function(response)
3627 this.uploadComplete= true;
3628 if (this.haveProgress) {
3629 Roo.MessageBox.hide();
3633 var result = this.processResponse(response);
3634 if(result === true || result.success){
3635 this.form.afterAction(this, true);
3639 this.form.markInvalid(result.errors);
3640 this.failureType = Roo.form.Action.SERVER_INVALID;
3642 this.form.afterAction(this, false);
3644 failure : function(response)
3646 this.uploadComplete= true;
3647 if (this.haveProgress) {
3648 Roo.MessageBox.hide();
3651 this.response = response;
3652 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3653 this.form.afterAction(this, false);
3656 handleResponse : function(response){
3657 if(this.form.errorReader){
3658 var rs = this.form.errorReader.read(response);
3661 for(var i = 0, len = rs.records.length; i < len; i++) {
3662 var r = rs.records[i];
3666 if(errors.length < 1){
3670 success : rs.success,
3676 ret = Roo.decode(response.responseText);
3680 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3690 Roo.form.Action.Load = function(form, options){
3691 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3692 this.reader = this.form.reader;
3695 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3700 Roo.Ajax.request(Roo.apply(
3701 this.createCallback(), {
3702 method:this.getMethod(),
3703 url:this.getUrl(false),
3704 params:this.getParams()
3708 success : function(response){
3710 var result = this.processResponse(response);
3711 if(result === true || !result.success || !result.data){
3712 this.failureType = Roo.form.Action.LOAD_FAILURE;
3713 this.form.afterAction(this, false);
3716 this.form.clearInvalid();
3717 this.form.setValues(result.data);
3718 this.form.afterAction(this, true);
3721 handleResponse : function(response){
3722 if(this.form.reader){
3723 var rs = this.form.reader.read(response);
3724 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3726 success : rs.success,
3730 return Roo.decode(response.responseText);
3734 Roo.form.Action.ACTION_TYPES = {
3735 'load' : Roo.form.Action.Load,
3736 'submit' : Roo.form.Action.Submit
3745 * @class Roo.bootstrap.Form
3746 * @extends Roo.bootstrap.Component
3747 * Bootstrap Form class
3748 * @cfg {String} method GET | POST (default POST)
3749 * @cfg {String} labelAlign top | left (default top)
3750 * @cfg {String} align left | right - for navbars
3755 * @param {Object} config The config object
3759 Roo.bootstrap.Form = function(config){
3760 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3763 * @event clientvalidation
3764 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3765 * @param {Form} this
3766 * @param {Boolean} valid true if the form has passed client-side validation
3768 clientvalidation: true,
3770 * @event beforeaction
3771 * Fires before any action is performed. Return false to cancel the action.
3772 * @param {Form} this
3773 * @param {Action} action The action to be performed
3777 * @event actionfailed
3778 * Fires when an action fails.
3779 * @param {Form} this
3780 * @param {Action} action The action that failed
3782 actionfailed : true,
3784 * @event actioncomplete
3785 * Fires when an action is completed.
3786 * @param {Form} this
3787 * @param {Action} action The action that completed
3789 actioncomplete : true
3794 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3797 * @cfg {String} method
3798 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3803 * The URL to use for form actions if one isn't supplied in the action options.
3806 * @cfg {Boolean} fileUpload
3807 * Set to true if this form is a file upload.
3811 * @cfg {Object} baseParams
3812 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3816 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3820 * @cfg {Sting} align (left|right) for navbar forms
3825 activeAction : null,
3828 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3829 * element by passing it or its id or mask the form itself by passing in true.
3832 waitMsgTarget : false,
3837 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3838 * element by passing it or its id or mask the form itself by passing in true.
3842 getAutoCreate : function(){
3846 method : this.method || 'POST',
3847 id : this.id || Roo.id(),
3850 if (this.parent().xtype.match(/^Nav/)) {
3851 cfg.cls = 'navbar-form navbar-' + this.align;
3855 if (this.labelAlign == 'left' ) {
3856 cfg.cls += ' form-horizontal';
3862 initEvents : function()
3864 this.el.on('submit', this.onSubmit, this);
3869 onSubmit : function(e){
3874 * Returns true if client-side validation on the form is successful.
3877 isValid : function(){
3878 var items = this.getItems();
3880 items.each(function(f){
3889 * Returns true if any fields in this form have changed since their original load.
3892 isDirty : function(){
3894 var items = this.getItems();
3895 items.each(function(f){
3905 * Performs a predefined action (submit or load) or custom actions you define on this form.
3906 * @param {String} actionName The name of the action type
3907 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3908 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3909 * accept other config options):
3911 Property Type Description
3912 ---------------- --------------- ----------------------------------------------------------------------------------
3913 url String The url for the action (defaults to the form's url)
3914 method String The form method to use (defaults to the form's method, or POST if not defined)
3915 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3916 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3917 validate the form on the client (defaults to false)
3919 * @return {BasicForm} this
3921 doAction : function(action, options){
3922 if(typeof action == 'string'){
3923 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3925 if(this.fireEvent('beforeaction', this, action) !== false){
3926 this.beforeAction(action);
3927 action.run.defer(100, action);
3933 beforeAction : function(action){
3934 var o = action.options;
3936 // not really supported yet.. ??
3938 //if(this.waitMsgTarget === true){
3939 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3940 //}else if(this.waitMsgTarget){
3941 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3942 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3944 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3950 afterAction : function(action, success){
3951 this.activeAction = null;
3952 var o = action.options;
3954 //if(this.waitMsgTarget === true){
3956 //}else if(this.waitMsgTarget){
3957 // this.waitMsgTarget.unmask();
3959 // Roo.MessageBox.updateProgress(1);
3960 // Roo.MessageBox.hide();
3967 Roo.callback(o.success, o.scope, [this, action]);
3968 this.fireEvent('actioncomplete', this, action);
3972 // failure condition..
3973 // we have a scenario where updates need confirming.
3974 // eg. if a locking scenario exists..
3975 // we look for { errors : { needs_confirm : true }} in the response.
3977 (typeof(action.result) != 'undefined') &&
3978 (typeof(action.result.errors) != 'undefined') &&
3979 (typeof(action.result.errors.needs_confirm) != 'undefined')
3982 Roo.log("not supported yet");
3985 Roo.MessageBox.confirm(
3986 "Change requires confirmation",
3987 action.result.errorMsg,
3992 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4002 Roo.callback(o.failure, o.scope, [this, action]);
4003 // show an error message if no failed handler is set..
4004 if (!this.hasListener('actionfailed')) {
4005 Roo.log("need to add dialog support");
4007 Roo.MessageBox.alert("Error",
4008 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4009 action.result.errorMsg :
4010 "Saving Failed, please check your entries or try again"
4015 this.fireEvent('actionfailed', this, action);
4020 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4021 * @param {String} id The value to search for
4024 findField : function(id){
4025 var items = this.getItems();
4026 var field = items.get(id);
4028 items.each(function(f){
4029 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4036 return field || null;
4039 * Mark fields in this form invalid in bulk.
4040 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4041 * @return {BasicForm} this
4043 markInvalid : function(errors){
4044 if(errors instanceof Array){
4045 for(var i = 0, len = errors.length; i < len; i++){
4046 var fieldError = errors[i];
4047 var f = this.findField(fieldError.id);
4049 f.markInvalid(fieldError.msg);
4055 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4056 field.markInvalid(errors[id]);
4060 //Roo.each(this.childForms || [], function (f) {
4061 // f.markInvalid(errors);
4068 * Set values for fields in this form in bulk.
4069 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4070 * @return {BasicForm} this
4072 setValues : function(values){
4073 if(values instanceof Array){ // array of objects
4074 for(var i = 0, len = values.length; i < len; i++){
4076 var f = this.findField(v.id);
4078 f.setValue(v.value);
4079 if(this.trackResetOnLoad){
4080 f.originalValue = f.getValue();
4084 }else{ // object hash
4087 if(typeof values[id] != 'function' && (field = this.findField(id))){
4089 if (field.setFromData &&
4091 field.displayField &&
4092 // combos' with local stores can
4093 // be queried via setValue()
4094 // to set their value..
4095 (field.store && !field.store.isLocal)
4099 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4100 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4101 field.setFromData(sd);
4104 field.setValue(values[id]);
4108 if(this.trackResetOnLoad){
4109 field.originalValue = field.getValue();
4115 //Roo.each(this.childForms || [], function (f) {
4116 // f.setValues(values);
4123 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4124 * they are returned as an array.
4125 * @param {Boolean} asString
4128 getValues : function(asString){
4129 //if (this.childForms) {
4130 // copy values from the child forms
4131 // Roo.each(this.childForms, function (f) {
4132 // this.setValues(f.getValues());
4138 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4139 if(asString === true){
4142 return Roo.urlDecode(fs);
4146 * Returns the fields in this form as an object with key/value pairs.
4147 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4150 getFieldValues : function(with_hidden)
4152 var items = this.getItems();
4154 items.each(function(f){
4158 var v = f.getValue();
4159 if (f.inputType =='radio') {
4160 if (typeof(ret[f.getName()]) == 'undefined') {
4161 ret[f.getName()] = ''; // empty..
4164 if (!f.el.dom.checked) {
4172 // not sure if this supported any more..
4173 if ((typeof(v) == 'object') && f.getRawValue) {
4174 v = f.getRawValue() ; // dates..
4176 // combo boxes where name != hiddenName...
4177 if (f.name != f.getName()) {
4178 ret[f.name] = f.getRawValue();
4180 ret[f.getName()] = v;
4187 * Clears all invalid messages in this form.
4188 * @return {BasicForm} this
4190 clearInvalid : function(){
4191 var items = this.getItems();
4193 items.each(function(f){
4204 * @return {BasicForm} this
4207 var items = this.getItems();
4208 items.each(function(f){
4212 Roo.each(this.childForms || [], function (f) {
4219 getItems : function()
4221 var r=new Roo.util.MixedCollection(false, function(o){
4222 return o.id || (o.id = Roo.id());
4224 var iter = function(el) {
4231 Roo.each(el.items,function(e) {
4250 * Ext JS Library 1.1.1
4251 * Copyright(c) 2006-2007, Ext JS, LLC.
4253 * Originally Released Under LGPL - original licence link has changed is not relivant.
4256 * <script type="text/javascript">
4259 * @class Roo.form.VTypes
4260 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4263 Roo.form.VTypes = function(){
4264 // closure these in so they are only created once.
4265 var alpha = /^[a-zA-Z_]+$/;
4266 var alphanum = /^[a-zA-Z0-9_]+$/;
4267 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4268 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4270 // All these messages and functions are configurable
4273 * The function used to validate email addresses
4274 * @param {String} value The email address
4276 'email' : function(v){
4277 return email.test(v);
4280 * The error text to display when the email validation function returns false
4283 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4285 * The keystroke filter mask to be applied on email input
4288 'emailMask' : /[a-z0-9_\.\-@]/i,
4291 * The function used to validate URLs
4292 * @param {String} value The URL
4294 'url' : function(v){
4298 * The error text to display when the url validation function returns false
4301 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4304 * The function used to validate alpha values
4305 * @param {String} value The value
4307 'alpha' : function(v){
4308 return alpha.test(v);
4311 * The error text to display when the alpha validation function returns false
4314 'alphaText' : 'This field should only contain letters and _',
4316 * The keystroke filter mask to be applied on alpha input
4319 'alphaMask' : /[a-z_]/i,
4322 * The function used to validate alphanumeric values
4323 * @param {String} value The value
4325 'alphanum' : function(v){
4326 return alphanum.test(v);
4329 * The error text to display when the alphanumeric validation function returns false
4332 'alphanumText' : 'This field should only contain letters, numbers and _',
4334 * The keystroke filter mask to be applied on alphanumeric input
4337 'alphanumMask' : /[a-z0-9_]/i
4347 * @class Roo.bootstrap.Input
4348 * @extends Roo.bootstrap.Component
4349 * Bootstrap Input class
4350 * @cfg {Boolean} disabled is it disabled
4351 * @cfg {String} fieldLabel - the label associated
4352 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4353 * @cfg {String} name name of the input
4354 * @cfg {string} fieldLabel - the label associated
4355 * @cfg {string} inputType - input / file submit ...
4356 * @cfg {string} placeholder - placeholder to put in text.
4357 * @cfg {string} before - input group add on before
4358 * @cfg {string} after - input group add on after
4359 * @cfg {string} size - (lg|sm) or leave empty..
4360 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4361 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4362 * @cfg {Number} md colspan out of 12 for computer-sized screens
4363 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4364 * @cfg {string} value default value of the input
4365 * @cfg {Number} labelWidth set the width of label (0-12)
4366 * @cfg {String} labelAlign (top|left)
4367 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4371 * Create a new Input
4372 * @param {Object} config The config object
4375 Roo.bootstrap.Input = function(config){
4376 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4381 * Fires when this field receives input focus.
4382 * @param {Roo.form.Field} this
4387 * Fires when this field loses input focus.
4388 * @param {Roo.form.Field} this
4393 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4394 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4395 * @param {Roo.form.Field} this
4396 * @param {Roo.EventObject} e The event object
4401 * Fires just before the field blurs if the field value has changed.
4402 * @param {Roo.form.Field} this
4403 * @param {Mixed} newValue The new value
4404 * @param {Mixed} oldValue The original value
4409 * Fires after the field has been marked as invalid.
4410 * @param {Roo.form.Field} this
4411 * @param {String} msg The validation message
4416 * Fires after the field has been validated with no errors.
4417 * @param {Roo.form.Field} this
4422 * Fires after the key up
4423 * @param {Roo.form.Field} this
4424 * @param {Roo.EventObject} e The event Object
4430 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4432 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4433 automatic validation (defaults to "keyup").
4435 validationEvent : "keyup",
4437 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4439 validateOnBlur : true,
4441 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4443 validationDelay : 250,
4445 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4447 focusClass : "x-form-focus", // not needed???
4451 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4453 invalidClass : "has-error",
4456 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4458 selectOnFocus : false,
4461 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4465 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4470 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4472 disableKeyFilter : false,
4475 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4479 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4483 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4485 blankText : "This field is required",
4488 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4492 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4494 maxLength : Number.MAX_VALUE,
4496 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4498 minLengthText : "The minimum length for this field is {0}",
4500 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4502 maxLengthText : "The maximum length for this field is {0}",
4506 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4507 * If available, this function will be called only after the basic validators all return true, and will be passed the
4508 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4512 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4513 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4514 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4518 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4541 parentLabelAlign : function()
4544 while (parent.parent()) {
4545 parent = parent.parent();
4546 if (typeof(parent.labelAlign) !='undefined') {
4547 return parent.labelAlign;
4554 getAutoCreate : function(){
4556 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4562 if(this.inputType != 'hidden'){
4563 cfg.cls = 'form-group' //input-group
4569 type : this.inputType,
4571 cls : 'form-control',
4572 placeholder : this.placeholder || ''
4576 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4577 input.maxLength = this.maxLength;
4580 if (this.disabled) {
4581 input.disabled=true;
4584 if (this.readOnly) {
4585 input.readonly=true;
4589 input.name = this.name;
4592 input.cls += ' input-' + this.size;
4595 ['xs','sm','md','lg'].map(function(size){
4596 if (settings[size]) {
4597 cfg.cls += ' col-' + size + '-' + settings[size];
4601 var inputblock = input;
4603 if (this.before || this.after) {
4606 cls : 'input-group',
4610 inputblock.cn.push({
4612 cls : 'input-group-addon',
4616 inputblock.cn.push(input);
4618 inputblock.cn.push({
4620 cls : 'input-group-addon',
4627 if (align ==='left' && this.fieldLabel.length) {
4628 Roo.log("left and has label");
4634 cls : 'control-label col-sm-' + this.labelWidth,
4635 html : this.fieldLabel
4639 cls : "col-sm-" + (12 - this.labelWidth),
4646 } else if ( this.fieldLabel.length) {
4652 //cls : 'input-group-addon',
4653 html : this.fieldLabel
4663 Roo.log(" no label && no align");
4672 Roo.log('input-parentType: ' + this.parentType);
4674 if (this.parentType === 'Navbar' && this.parent().bar) {
4675 cfg.cls += ' navbar-form';
4683 * return the real input element.
4685 inputEl: function ()
4687 return this.el.select('input.form-control',true).first();
4689 setDisabled : function(v)
4691 var i = this.inputEl().dom;
4693 i.removeAttribute('disabled');
4697 i.setAttribute('disabled','true');
4699 initEvents : function()
4702 this.inputEl().on("keydown" , this.fireKey, this);
4703 this.inputEl().on("focus", this.onFocus, this);
4704 this.inputEl().on("blur", this.onBlur, this);
4706 this.inputEl().relayEvent('keyup', this);
4708 // reference to original value for reset
4709 this.originalValue = this.getValue();
4710 //Roo.form.TextField.superclass.initEvents.call(this);
4711 if(this.validationEvent == 'keyup'){
4712 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4713 this.inputEl().on('keyup', this.filterValidation, this);
4715 else if(this.validationEvent !== false){
4716 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4719 if(this.selectOnFocus){
4720 this.on("focus", this.preFocus, this);
4723 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4724 this.inputEl().on("keypress", this.filterKeys, this);
4727 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4728 this.el.on("click", this.autoSize, this);
4731 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4732 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4736 filterValidation : function(e){
4737 if(!e.isNavKeyPress()){
4738 this.validationTask.delay(this.validationDelay);
4742 * Validates the field value
4743 * @return {Boolean} True if the value is valid, else false
4745 validate : function(){
4746 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4747 if(this.disabled || this.validateValue(this.getRawValue())){
4748 this.clearInvalid();
4756 * Validates a value according to the field's validation rules and marks the field as invalid
4757 * if the validation fails
4758 * @param {Mixed} value The value to validate
4759 * @return {Boolean} True if the value is valid, else false
4761 validateValue : function(value){
4762 if(value.length < 1) { // if it's blank
4763 if(this.allowBlank){
4764 this.clearInvalid();
4767 this.markInvalid(this.blankText);
4771 if(value.length < this.minLength){
4772 this.markInvalid(String.format(this.minLengthText, this.minLength));
4775 if(value.length > this.maxLength){
4776 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4780 var vt = Roo.form.VTypes;
4781 if(!vt[this.vtype](value, this)){
4782 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4786 if(typeof this.validator == "function"){
4787 var msg = this.validator(value);
4789 this.markInvalid(msg);
4793 if(this.regex && !this.regex.test(value)){
4794 this.markInvalid(this.regexText);
4803 fireKey : function(e){
4804 //Roo.log('field ' + e.getKey());
4805 if(e.isNavKeyPress()){
4806 this.fireEvent("specialkey", this, e);
4809 focus : function (selectText){
4811 this.inputEl().focus();
4812 if(selectText === true){
4813 this.inputEl().dom.select();
4819 onFocus : function(){
4820 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4821 // this.el.addClass(this.focusClass);
4824 this.hasFocus = true;
4825 this.startValue = this.getValue();
4826 this.fireEvent("focus", this);
4830 beforeBlur : Roo.emptyFn,
4834 onBlur : function(){
4836 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4837 //this.el.removeClass(this.focusClass);
4839 this.hasFocus = false;
4840 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4843 var v = this.getValue();
4844 if(String(v) !== String(this.startValue)){
4845 this.fireEvent('change', this, v, this.startValue);
4847 this.fireEvent("blur", this);
4851 * Resets the current field value to the originally loaded value and clears any validation messages
4854 this.setValue(this.originalValue);
4855 this.clearInvalid();
4858 * Returns the name of the field
4859 * @return {Mixed} name The name field
4861 getName: function(){
4865 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4866 * @return {Mixed} value The field value
4868 getValue : function(){
4869 return this.inputEl().getValue();
4872 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4873 * @return {Mixed} value The field value
4875 getRawValue : function(){
4876 var v = this.inputEl().getValue();
4882 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4883 * @param {Mixed} value The value to set
4885 setRawValue : function(v){
4886 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4889 selectText : function(start, end){
4890 var v = this.getRawValue();
4892 start = start === undefined ? 0 : start;
4893 end = end === undefined ? v.length : end;
4894 var d = this.inputEl().dom;
4895 if(d.setSelectionRange){
4896 d.setSelectionRange(start, end);
4897 }else if(d.createTextRange){
4898 var range = d.createTextRange();
4899 range.moveStart("character", start);
4900 range.moveEnd("character", v.length-end);
4907 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4908 * @param {Mixed} value The value to set
4910 setValue : function(v){
4913 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4919 processValue : function(value){
4920 if(this.stripCharsRe){
4921 var newValue = value.replace(this.stripCharsRe, '');
4922 if(newValue !== value){
4923 this.setRawValue(newValue);
4930 preFocus : function(){
4932 if(this.selectOnFocus){
4933 this.inputEl().dom.select();
4936 filterKeys : function(e){
4938 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4941 var c = e.getCharCode(), cc = String.fromCharCode(c);
4942 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4945 if(!this.maskRe.test(cc)){
4950 * Clear any invalid styles/messages for this field
4952 clearInvalid : function(){
4954 if(!this.el || this.preventMark){ // not rendered
4957 this.el.removeClass(this.invalidClass);
4959 switch(this.msgTarget){
4961 this.el.dom.qtip = '';
4964 this.el.dom.title = '';
4968 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4973 this.errorIcon.dom.qtip = '';
4974 this.errorIcon.hide();
4975 this.un('resize', this.alignErrorIcon, this);
4979 var t = Roo.getDom(this.msgTarget);
4981 t.style.display = 'none';
4985 this.fireEvent('valid', this);
4988 * Mark this field as invalid
4989 * @param {String} msg The validation message
4991 markInvalid : function(msg){
4992 if(!this.el || this.preventMark){ // not rendered
4995 this.el.addClass(this.invalidClass);
4997 msg = msg || this.invalidText;
4998 switch(this.msgTarget){
5000 this.el.dom.qtip = msg;
5001 this.el.dom.qclass = 'x-form-invalid-tip';
5002 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5003 Roo.QuickTips.enable();
5007 this.el.dom.title = msg;
5011 var elp = this.el.findParent('.x-form-element', 5, true);
5012 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5013 this.errorEl.setWidth(elp.getWidth(true)-20);
5015 this.errorEl.update(msg);
5016 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5019 if(!this.errorIcon){
5020 var elp = this.el.findParent('.x-form-element', 5, true);
5021 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5023 this.alignErrorIcon();
5024 this.errorIcon.dom.qtip = msg;
5025 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5026 this.errorIcon.show();
5027 this.on('resize', this.alignErrorIcon, this);
5030 var t = Roo.getDom(this.msgTarget);
5032 t.style.display = this.msgDisplay;
5036 this.fireEvent('invalid', this, msg);
5039 SafariOnKeyDown : function(event)
5041 // this is a workaround for a password hang bug on chrome/ webkit.
5043 var isSelectAll = false;
5045 if(this.inputEl().dom.selectionEnd > 0){
5046 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5048 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5049 event.preventDefault();
5054 if(isSelectAll){ // backspace and delete key
5056 event.preventDefault();
5057 // this is very hacky as keydown always get's upper case.
5059 var cc = String.fromCharCode(event.getCharCode());
5060 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5064 adjustWidth : function(tag, w){
5065 tag = tag.toLowerCase();
5066 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5067 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5071 if(tag == 'textarea'){
5074 }else if(Roo.isOpera){
5078 if(tag == 'textarea'){
5097 * @class Roo.bootstrap.TextArea
5098 * @extends Roo.bootstrap.Input
5099 * Bootstrap TextArea class
5100 * @cfg {Number} cols Specifies the visible width of a text area
5101 * @cfg {Number} rows Specifies the visible number of lines in a text area
5102 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5103 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5104 * @cfg {string} html text
5107 * Create a new TextArea
5108 * @param {Object} config The config object
5111 Roo.bootstrap.TextArea = function(config){
5112 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5116 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5126 getAutoCreate : function(){
5128 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5139 value : this.value || '',
5140 html: this.html || '',
5141 cls : 'form-control',
5142 placeholder : this.placeholder || ''
5146 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5147 input.maxLength = this.maxLength;
5151 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5155 input.cols = this.cols;
5158 if (this.readOnly) {
5159 input.readonly = true;
5163 input.name = this.name;
5167 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5171 ['xs','sm','md','lg'].map(function(size){
5172 if (settings[size]) {
5173 cfg.cls += ' col-' + size + '-' + settings[size];
5177 var inputblock = input;
5179 if (this.before || this.after) {
5182 cls : 'input-group',
5186 inputblock.cn.push({
5188 cls : 'input-group-addon',
5192 inputblock.cn.push(input);
5194 inputblock.cn.push({
5196 cls : 'input-group-addon',
5203 if (align ==='left' && this.fieldLabel.length) {
5204 Roo.log("left and has label");
5210 cls : 'control-label col-sm-' + this.labelWidth,
5211 html : this.fieldLabel
5215 cls : "col-sm-" + (12 - this.labelWidth),
5222 } else if ( this.fieldLabel.length) {
5228 //cls : 'input-group-addon',
5229 html : this.fieldLabel
5239 Roo.log(" no label && no align");
5249 if (this.disabled) {
5250 input.disabled=true;
5257 * return the real textarea element.
5259 inputEl: function ()
5261 return this.el.select('textarea.form-control',true).first();
5269 * trigger field - base class for combo..
5274 * @class Roo.bootstrap.TriggerField
5275 * @extends Roo.bootstrap.Input
5276 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5277 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5278 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5279 * for which you can provide a custom implementation. For example:
5281 var trigger = new Roo.bootstrap.TriggerField();
5282 trigger.onTriggerClick = myTriggerFn;
5283 trigger.applyTo('my-field');
5286 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5287 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5288 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5289 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5291 * Create a new TriggerField.
5292 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5293 * to the base TextField)
5295 Roo.bootstrap.TriggerField = function(config){
5296 this.mimicing = false;
5297 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5300 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5302 * @cfg {String} triggerClass A CSS class to apply to the trigger
5305 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5309 /** @cfg {Boolean} grow @hide */
5310 /** @cfg {Number} growMin @hide */
5311 /** @cfg {Number} growMax @hide */
5317 autoSize: Roo.emptyFn,
5324 actionMode : 'wrap',
5328 getAutoCreate : function(){
5330 var parent = this.parent();
5332 var align = this.parentLabelAlign();
5337 cls: 'form-group' //input-group
5344 type : this.inputType,
5345 cls : 'form-control',
5346 autocomplete: 'off',
5347 placeholder : this.placeholder || ''
5351 input.name = this.name;
5354 input.cls += ' input-' + this.size;
5357 if (this.disabled) {
5358 input.disabled=true;
5361 var inputblock = input;
5363 if (this.before || this.after) {
5366 cls : 'input-group',
5370 inputblock.cn.push({
5372 cls : 'input-group-addon',
5376 inputblock.cn.push(input);
5378 inputblock.cn.push({
5380 cls : 'input-group-addon',
5393 cls: 'form-hidden-field'
5401 Roo.log('multiple');
5409 cls: 'form-hidden-field'
5413 cls: 'select2-choices',
5417 cls: 'select2-search-field',
5430 cls: 'select2-container input-group',
5435 cls: 'typeahead typeahead-long dropdown-menu',
5436 style: 'display:none'
5444 cls : 'input-group-addon btn dropdown-toggle',
5452 cls: 'combobox-clear',
5466 combobox.cls += ' select2-container-multi';
5469 if (align ==='left' && this.fieldLabel.length) {
5471 Roo.log("left and has label");
5477 cls : 'control-label col-sm-' + this.labelWidth,
5478 html : this.fieldLabel
5482 cls : "col-sm-" + (12 - this.labelWidth),
5489 } else if ( this.fieldLabel.length) {
5495 //cls : 'input-group-addon',
5496 html : this.fieldLabel
5506 Roo.log(" no label && no align");
5513 ['xs','sm','md','lg'].map(function(size){
5514 if (settings[size]) {
5515 cfg.cls += ' col-' + size + '-' + settings[size];
5526 onResize : function(w, h){
5527 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5528 // if(typeof w == 'number'){
5529 // var x = w - this.trigger.getWidth();
5530 // this.inputEl().setWidth(this.adjustWidth('input', x));
5531 // this.trigger.setStyle('left', x+'px');
5536 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5539 getResizeEl : function(){
5540 return this.inputEl();
5544 getPositionEl : function(){
5545 return this.inputEl();
5549 alignErrorIcon : function(){
5550 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5554 initEvents : function(){
5556 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5557 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5559 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5560 if(this.hideTrigger){
5561 this.trigger.setDisplayed(false);
5563 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5567 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5570 //this.trigger.addClassOnOver('x-form-trigger-over');
5571 //this.trigger.addClassOnClick('x-form-trigger-click');
5574 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5579 initTrigger : function(){
5584 onDestroy : function(){
5586 this.trigger.removeAllListeners();
5587 // this.trigger.remove();
5590 // this.wrap.remove();
5592 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5596 onFocus : function(){
5597 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5600 this.wrap.addClass('x-trigger-wrap-focus');
5601 this.mimicing = true;
5602 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5603 if(this.monitorTab){
5604 this.el.on("keydown", this.checkTab, this);
5611 checkTab : function(e){
5612 if(e.getKey() == e.TAB){
5618 onBlur : function(){
5623 mimicBlur : function(e, t){
5625 if(!this.wrap.contains(t) && this.validateBlur()){
5632 triggerBlur : function(){
5633 this.mimicing = false;
5634 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5635 if(this.monitorTab){
5636 this.el.un("keydown", this.checkTab, this);
5638 //this.wrap.removeClass('x-trigger-wrap-focus');
5639 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5643 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5644 validateBlur : function(e, t){
5649 onDisable : function(){
5650 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5652 // this.wrap.addClass('x-item-disabled');
5657 onEnable : function(){
5658 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5660 // this.el.removeClass('x-item-disabled');
5665 onShow : function(){
5666 var ae = this.getActionEl();
5669 ae.dom.style.display = '';
5670 ae.dom.style.visibility = 'visible';
5676 onHide : function(){
5677 var ae = this.getActionEl();
5678 ae.dom.style.display = 'none';
5682 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5683 * by an implementing function.
5685 * @param {EventObject} e
5687 onTriggerClick : Roo.emptyFn
5691 * Ext JS Library 1.1.1
5692 * Copyright(c) 2006-2007, Ext JS, LLC.
5694 * Originally Released Under LGPL - original licence link has changed is not relivant.
5697 * <script type="text/javascript">
5702 * @class Roo.data.SortTypes
5704 * Defines the default sorting (casting?) comparison functions used when sorting data.
5706 Roo.data.SortTypes = {
5708 * Default sort that does nothing
5709 * @param {Mixed} s The value being converted
5710 * @return {Mixed} The comparison value
5717 * The regular expression used to strip tags
5721 stripTagsRE : /<\/?[^>]+>/gi,
5724 * Strips all HTML tags to sort on text only
5725 * @param {Mixed} s The value being converted
5726 * @return {String} The comparison value
5728 asText : function(s){
5729 return String(s).replace(this.stripTagsRE, "");
5733 * Strips all HTML tags to sort on text only - Case insensitive
5734 * @param {Mixed} s The value being converted
5735 * @return {String} The comparison value
5737 asUCText : function(s){
5738 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5742 * Case insensitive string
5743 * @param {Mixed} s The value being converted
5744 * @return {String} The comparison value
5746 asUCString : function(s) {
5747 return String(s).toUpperCase();
5752 * @param {Mixed} s The value being converted
5753 * @return {Number} The comparison value
5755 asDate : function(s) {
5759 if(s instanceof Date){
5762 return Date.parse(String(s));
5767 * @param {Mixed} s The value being converted
5768 * @return {Float} The comparison value
5770 asFloat : function(s) {
5771 var val = parseFloat(String(s).replace(/,/g, ""));
5772 if(isNaN(val)) val = 0;
5778 * @param {Mixed} s The value being converted
5779 * @return {Number} The comparison value
5781 asInt : function(s) {
5782 var val = parseInt(String(s).replace(/,/g, ""));
5783 if(isNaN(val)) val = 0;
5788 * Ext JS Library 1.1.1
5789 * Copyright(c) 2006-2007, Ext JS, LLC.
5791 * Originally Released Under LGPL - original licence link has changed is not relivant.
5794 * <script type="text/javascript">
5798 * @class Roo.data.Record
5799 * Instances of this class encapsulate both record <em>definition</em> information, and record
5800 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5801 * to access Records cached in an {@link Roo.data.Store} object.<br>
5803 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5804 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5807 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5809 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5810 * {@link #create}. The parameters are the same.
5811 * @param {Array} data An associative Array of data values keyed by the field name.
5812 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5813 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5814 * not specified an integer id is generated.
5816 Roo.data.Record = function(data, id){
5817 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5822 * Generate a constructor for a specific record layout.
5823 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5824 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5825 * Each field definition object may contain the following properties: <ul>
5826 * <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,
5827 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5828 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5829 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5830 * is being used, then this is a string containing the javascript expression to reference the data relative to
5831 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5832 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5833 * this may be omitted.</p></li>
5834 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5835 * <ul><li>auto (Default, implies no conversion)</li>
5840 * <li>date</li></ul></p></li>
5841 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5842 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5843 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5844 * by the Reader into an object that will be stored in the Record. It is passed the
5845 * following parameters:<ul>
5846 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5848 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5850 * <br>usage:<br><pre><code>
5851 var TopicRecord = Roo.data.Record.create(
5852 {name: 'title', mapping: 'topic_title'},
5853 {name: 'author', mapping: 'username'},
5854 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5855 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5856 {name: 'lastPoster', mapping: 'user2'},
5857 {name: 'excerpt', mapping: 'post_text'}
5860 var myNewRecord = new TopicRecord({
5861 title: 'Do my job please',
5864 lastPost: new Date(),
5865 lastPoster: 'Animal',
5866 excerpt: 'No way dude!'
5868 myStore.add(myNewRecord);
5873 Roo.data.Record.create = function(o){
5875 f.superclass.constructor.apply(this, arguments);
5877 Roo.extend(f, Roo.data.Record);
5878 var p = f.prototype;
5879 p.fields = new Roo.util.MixedCollection(false, function(field){
5882 for(var i = 0, len = o.length; i < len; i++){
5883 p.fields.add(new Roo.data.Field(o[i]));
5885 f.getField = function(name){
5886 return p.fields.get(name);
5891 Roo.data.Record.AUTO_ID = 1000;
5892 Roo.data.Record.EDIT = 'edit';
5893 Roo.data.Record.REJECT = 'reject';
5894 Roo.data.Record.COMMIT = 'commit';
5896 Roo.data.Record.prototype = {
5898 * Readonly flag - true if this record has been modified.
5907 join : function(store){
5912 * Set the named field to the specified value.
5913 * @param {String} name The name of the field to set.
5914 * @param {Object} value The value to set the field to.
5916 set : function(name, value){
5917 if(this.data[name] == value){
5924 if(typeof this.modified[name] == 'undefined'){
5925 this.modified[name] = this.data[name];
5927 this.data[name] = value;
5928 if(!this.editing && this.store){
5929 this.store.afterEdit(this);
5934 * Get the value of the named field.
5935 * @param {String} name The name of the field to get the value of.
5936 * @return {Object} The value of the field.
5938 get : function(name){
5939 return this.data[name];
5943 beginEdit : function(){
5944 this.editing = true;
5949 cancelEdit : function(){
5950 this.editing = false;
5951 delete this.modified;
5955 endEdit : function(){
5956 this.editing = false;
5957 if(this.dirty && this.store){
5958 this.store.afterEdit(this);
5963 * Usually called by the {@link Roo.data.Store} which owns the Record.
5964 * Rejects all changes made to the Record since either creation, or the last commit operation.
5965 * Modified fields are reverted to their original values.
5967 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5968 * of reject operations.
5970 reject : function(){
5971 var m = this.modified;
5973 if(typeof m[n] != "function"){
5974 this.data[n] = m[n];
5978 delete this.modified;
5979 this.editing = false;
5981 this.store.afterReject(this);
5986 * Usually called by the {@link Roo.data.Store} which owns the Record.
5987 * Commits all changes made to the Record since either creation, or the last commit operation.
5989 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5990 * of commit operations.
5992 commit : function(){
5994 delete this.modified;
5995 this.editing = false;
5997 this.store.afterCommit(this);
6002 hasError : function(){
6003 return this.error != null;
6007 clearError : function(){
6012 * Creates a copy of this record.
6013 * @param {String} id (optional) A new record id if you don't want to use this record's id
6016 copy : function(newId) {
6017 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6021 * Ext JS Library 1.1.1
6022 * Copyright(c) 2006-2007, Ext JS, LLC.
6024 * Originally Released Under LGPL - original licence link has changed is not relivant.
6027 * <script type="text/javascript">
6033 * @class Roo.data.Store
6034 * @extends Roo.util.Observable
6035 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6036 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6038 * 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
6039 * has no knowledge of the format of the data returned by the Proxy.<br>
6041 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6042 * instances from the data object. These records are cached and made available through accessor functions.
6044 * Creates a new Store.
6045 * @param {Object} config A config object containing the objects needed for the Store to access data,
6046 * and read the data into Records.
6048 Roo.data.Store = function(config){
6049 this.data = new Roo.util.MixedCollection(false);
6050 this.data.getKey = function(o){
6053 this.baseParams = {};
6060 "multisort" : "_multisort"
6063 if(config && config.data){
6064 this.inlineData = config.data;
6068 Roo.apply(this, config);
6070 if(this.reader){ // reader passed
6071 this.reader = Roo.factory(this.reader, Roo.data);
6072 this.reader.xmodule = this.xmodule || false;
6073 if(!this.recordType){
6074 this.recordType = this.reader.recordType;
6076 if(this.reader.onMetaChange){
6077 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6081 if(this.recordType){
6082 this.fields = this.recordType.prototype.fields;
6088 * @event datachanged
6089 * Fires when the data cache has changed, and a widget which is using this Store
6090 * as a Record cache should refresh its view.
6091 * @param {Store} this
6096 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6097 * @param {Store} this
6098 * @param {Object} meta The JSON metadata
6103 * Fires when Records have been added to the Store
6104 * @param {Store} this
6105 * @param {Roo.data.Record[]} records The array of Records added
6106 * @param {Number} index The index at which the record(s) were added
6111 * Fires when a Record has been removed from the Store
6112 * @param {Store} this
6113 * @param {Roo.data.Record} record The Record that was removed
6114 * @param {Number} index The index at which the record was removed
6119 * Fires when a Record has been updated
6120 * @param {Store} this
6121 * @param {Roo.data.Record} record The Record that was updated
6122 * @param {String} operation The update operation being performed. Value may be one of:
6124 Roo.data.Record.EDIT
6125 Roo.data.Record.REJECT
6126 Roo.data.Record.COMMIT
6132 * Fires when the data cache has been cleared.
6133 * @param {Store} this
6138 * Fires before a request is made for a new data object. If the beforeload handler returns false
6139 * the load action will be canceled.
6140 * @param {Store} this
6141 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6145 * @event beforeloadadd
6146 * Fires after a new set of Records has been loaded.
6147 * @param {Store} this
6148 * @param {Roo.data.Record[]} records The Records that were loaded
6149 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6151 beforeloadadd : true,
6154 * Fires after a new set of Records has been loaded, before they are added to the store.
6155 * @param {Store} this
6156 * @param {Roo.data.Record[]} records The Records that were loaded
6157 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6158 * @params {Object} return from reader
6162 * @event loadexception
6163 * Fires if an exception occurs in the Proxy during loading.
6164 * Called with the signature of the Proxy's "loadexception" event.
6165 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6168 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6169 * @param {Object} load options
6170 * @param {Object} jsonData from your request (normally this contains the Exception)
6172 loadexception : true
6176 this.proxy = Roo.factory(this.proxy, Roo.data);
6177 this.proxy.xmodule = this.xmodule || false;
6178 this.relayEvents(this.proxy, ["loadexception"]);
6180 this.sortToggle = {};
6181 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6183 Roo.data.Store.superclass.constructor.call(this);
6185 if(this.inlineData){
6186 this.loadData(this.inlineData);
6187 delete this.inlineData;
6191 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6193 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6194 * without a remote query - used by combo/forms at present.
6198 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6201 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6204 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6205 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6208 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6209 * on any HTTP request
6212 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6215 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6219 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6220 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6225 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6226 * loaded or when a record is removed. (defaults to false).
6228 pruneModifiedRecords : false,
6234 * Add Records to the Store and fires the add event.
6235 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6237 add : function(records){
6238 records = [].concat(records);
6239 for(var i = 0, len = records.length; i < len; i++){
6240 records[i].join(this);
6242 var index = this.data.length;
6243 this.data.addAll(records);
6244 this.fireEvent("add", this, records, index);
6248 * Remove a Record from the Store and fires the remove event.
6249 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6251 remove : function(record){
6252 var index = this.data.indexOf(record);
6253 this.data.removeAt(index);
6254 if(this.pruneModifiedRecords){
6255 this.modified.remove(record);
6257 this.fireEvent("remove", this, record, index);
6261 * Remove all Records from the Store and fires the clear event.
6263 removeAll : function(){
6265 if(this.pruneModifiedRecords){
6268 this.fireEvent("clear", this);
6272 * Inserts Records to the Store at the given index and fires the add event.
6273 * @param {Number} index The start index at which to insert the passed Records.
6274 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6276 insert : function(index, records){
6277 records = [].concat(records);
6278 for(var i = 0, len = records.length; i < len; i++){
6279 this.data.insert(index, records[i]);
6280 records[i].join(this);
6282 this.fireEvent("add", this, records, index);
6286 * Get the index within the cache of the passed Record.
6287 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6288 * @return {Number} The index of the passed Record. Returns -1 if not found.
6290 indexOf : function(record){
6291 return this.data.indexOf(record);
6295 * Get the index within the cache of the Record with the passed id.
6296 * @param {String} id The id of the Record to find.
6297 * @return {Number} The index of the Record. Returns -1 if not found.
6299 indexOfId : function(id){
6300 return this.data.indexOfKey(id);
6304 * Get the Record with the specified id.
6305 * @param {String} id The id of the Record to find.
6306 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6308 getById : function(id){
6309 return this.data.key(id);
6313 * Get the Record at the specified index.
6314 * @param {Number} index The index of the Record to find.
6315 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6317 getAt : function(index){
6318 return this.data.itemAt(index);
6322 * Returns a range of Records between specified indices.
6323 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6324 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6325 * @return {Roo.data.Record[]} An array of Records
6327 getRange : function(start, end){
6328 return this.data.getRange(start, end);
6332 storeOptions : function(o){
6333 o = Roo.apply({}, o);
6336 this.lastOptions = o;
6340 * Loads the Record cache from the configured Proxy using the configured Reader.
6342 * If using remote paging, then the first load call must specify the <em>start</em>
6343 * and <em>limit</em> properties in the options.params property to establish the initial
6344 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6346 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6347 * and this call will return before the new data has been loaded. Perform any post-processing
6348 * in a callback function, or in a "load" event handler.</strong>
6350 * @param {Object} options An object containing properties which control loading options:<ul>
6351 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6352 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6353 * passed the following arguments:<ul>
6354 * <li>r : Roo.data.Record[]</li>
6355 * <li>options: Options object from the load call</li>
6356 * <li>success: Boolean success indicator</li></ul></li>
6357 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6358 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6361 load : function(options){
6362 options = options || {};
6363 if(this.fireEvent("beforeload", this, options) !== false){
6364 this.storeOptions(options);
6365 var p = Roo.apply(options.params || {}, this.baseParams);
6366 // if meta was not loaded from remote source.. try requesting it.
6367 if (!this.reader.metaFromRemote) {
6370 if(this.sortInfo && this.remoteSort){
6371 var pn = this.paramNames;
6372 p[pn["sort"]] = this.sortInfo.field;
6373 p[pn["dir"]] = this.sortInfo.direction;
6375 if (this.multiSort) {
6376 var pn = this.paramNames;
6377 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6380 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6385 * Reloads the Record cache from the configured Proxy using the configured Reader and
6386 * the options from the last load operation performed.
6387 * @param {Object} options (optional) An object containing properties which may override the options
6388 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6389 * the most recently used options are reused).
6391 reload : function(options){
6392 this.load(Roo.applyIf(options||{}, this.lastOptions));
6396 // Called as a callback by the Reader during a load operation.
6397 loadRecords : function(o, options, success){
6398 if(!o || success === false){
6399 if(success !== false){
6400 this.fireEvent("load", this, [], options, o);
6402 if(options.callback){
6403 options.callback.call(options.scope || this, [], options, false);
6407 // if data returned failure - throw an exception.
6408 if (o.success === false) {
6409 // show a message if no listener is registered.
6410 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6411 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6413 // loadmask wil be hooked into this..
6414 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6417 var r = o.records, t = o.totalRecords || r.length;
6419 this.fireEvent("beforeloadadd", this, r, options, o);
6421 if(!options || options.add !== true){
6422 if(this.pruneModifiedRecords){
6425 for(var i = 0, len = r.length; i < len; i++){
6429 this.data = this.snapshot;
6430 delete this.snapshot;
6433 this.data.addAll(r);
6434 this.totalLength = t;
6436 this.fireEvent("datachanged", this);
6438 this.totalLength = Math.max(t, this.data.length+r.length);
6441 this.fireEvent("load", this, r, options, o);
6442 if(options.callback){
6443 options.callback.call(options.scope || this, r, options, true);
6449 * Loads data from a passed data block. A Reader which understands the format of the data
6450 * must have been configured in the constructor.
6451 * @param {Object} data The data block from which to read the Records. The format of the data expected
6452 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6453 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6455 loadData : function(o, append){
6456 var r = this.reader.readRecords(o);
6457 this.loadRecords(r, {add: append}, true);
6461 * Gets the number of cached records.
6463 * <em>If using paging, this may not be the total size of the dataset. If the data object
6464 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6465 * the data set size</em>
6467 getCount : function(){
6468 return this.data.length || 0;
6472 * Gets the total number of records in the dataset as returned by the server.
6474 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6475 * the dataset size</em>
6477 getTotalCount : function(){
6478 return this.totalLength || 0;
6482 * Returns the sort state of the Store as an object with two properties:
6484 field {String} The name of the field by which the Records are sorted
6485 direction {String} The sort order, "ASC" or "DESC"
6488 getSortState : function(){
6489 return this.sortInfo;
6493 applySort : function(){
6494 if(this.sortInfo && !this.remoteSort){
6495 var s = this.sortInfo, f = s.field;
6496 var st = this.fields.get(f).sortType;
6497 var fn = function(r1, r2){
6498 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6499 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6501 this.data.sort(s.direction, fn);
6502 if(this.snapshot && this.snapshot != this.data){
6503 this.snapshot.sort(s.direction, fn);
6509 * Sets the default sort column and order to be used by the next load operation.
6510 * @param {String} fieldName The name of the field to sort by.
6511 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6513 setDefaultSort : function(field, dir){
6514 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6519 * If remote sorting is used, the sort is performed on the server, and the cache is
6520 * reloaded. If local sorting is used, the cache is sorted internally.
6521 * @param {String} fieldName The name of the field to sort by.
6522 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6524 sort : function(fieldName, dir){
6525 var f = this.fields.get(fieldName);
6527 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6529 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6530 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6535 this.sortToggle[f.name] = dir;
6536 this.sortInfo = {field: f.name, direction: dir};
6537 if(!this.remoteSort){
6539 this.fireEvent("datachanged", this);
6541 this.load(this.lastOptions);
6546 * Calls the specified function for each of the Records in the cache.
6547 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6548 * Returning <em>false</em> aborts and exits the iteration.
6549 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6551 each : function(fn, scope){
6552 this.data.each(fn, scope);
6556 * Gets all records modified since the last commit. Modified records are persisted across load operations
6557 * (e.g., during paging).
6558 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6560 getModifiedRecords : function(){
6561 return this.modified;
6565 createFilterFn : function(property, value, anyMatch){
6566 if(!value.exec){ // not a regex
6567 value = String(value);
6568 if(value.length == 0){
6571 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6574 return value.test(r.data[property]);
6579 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6580 * @param {String} property A field on your records
6581 * @param {Number} start The record index to start at (defaults to 0)
6582 * @param {Number} end The last record index to include (defaults to length - 1)
6583 * @return {Number} The sum
6585 sum : function(property, start, end){
6586 var rs = this.data.items, v = 0;
6588 end = (end || end === 0) ? end : rs.length-1;
6590 for(var i = start; i <= end; i++){
6591 v += (rs[i].data[property] || 0);
6597 * Filter the records by a specified property.
6598 * @param {String} field A field on your records
6599 * @param {String/RegExp} value Either a string that the field
6600 * should start with or a RegExp to test against the field
6601 * @param {Boolean} anyMatch True to match any part not just the beginning
6603 filter : function(property, value, anyMatch){
6604 var fn = this.createFilterFn(property, value, anyMatch);
6605 return fn ? this.filterBy(fn) : this.clearFilter();
6609 * Filter by a function. The specified function will be called with each
6610 * record in this data source. If the function returns true the record is included,
6611 * otherwise it is filtered.
6612 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6613 * @param {Object} scope (optional) The scope of the function (defaults to this)
6615 filterBy : function(fn, scope){
6616 this.snapshot = this.snapshot || this.data;
6617 this.data = this.queryBy(fn, scope||this);
6618 this.fireEvent("datachanged", this);
6622 * Query the records by a specified property.
6623 * @param {String} field A field on your records
6624 * @param {String/RegExp} value Either a string that the field
6625 * should start with or a RegExp to test against the field
6626 * @param {Boolean} anyMatch True to match any part not just the beginning
6627 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6629 query : function(property, value, anyMatch){
6630 var fn = this.createFilterFn(property, value, anyMatch);
6631 return fn ? this.queryBy(fn) : this.data.clone();
6635 * Query by a function. The specified function will be called with each
6636 * record in this data source. If the function returns true the record is included
6638 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6639 * @param {Object} scope (optional) The scope of the function (defaults to this)
6640 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6642 queryBy : function(fn, scope){
6643 var data = this.snapshot || this.data;
6644 return data.filterBy(fn, scope||this);
6648 * Collects unique values for a particular dataIndex from this store.
6649 * @param {String} dataIndex The property to collect
6650 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6651 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6652 * @return {Array} An array of the unique values
6654 collect : function(dataIndex, allowNull, bypassFilter){
6655 var d = (bypassFilter === true && this.snapshot) ?
6656 this.snapshot.items : this.data.items;
6657 var v, sv, r = [], l = {};
6658 for(var i = 0, len = d.length; i < len; i++){
6659 v = d[i].data[dataIndex];
6661 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6670 * Revert to a view of the Record cache with no filtering applied.
6671 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6673 clearFilter : function(suppressEvent){
6674 if(this.snapshot && this.snapshot != this.data){
6675 this.data = this.snapshot;
6676 delete this.snapshot;
6677 if(suppressEvent !== true){
6678 this.fireEvent("datachanged", this);
6684 afterEdit : function(record){
6685 if(this.modified.indexOf(record) == -1){
6686 this.modified.push(record);
6688 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6692 afterReject : function(record){
6693 this.modified.remove(record);
6694 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6698 afterCommit : function(record){
6699 this.modified.remove(record);
6700 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6704 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6705 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6707 commitChanges : function(){
6708 var m = this.modified.slice(0);
6710 for(var i = 0, len = m.length; i < len; i++){
6716 * Cancel outstanding changes on all changed records.
6718 rejectChanges : function(){
6719 var m = this.modified.slice(0);
6721 for(var i = 0, len = m.length; i < len; i++){
6726 onMetaChange : function(meta, rtype, o){
6727 this.recordType = rtype;
6728 this.fields = rtype.prototype.fields;
6729 delete this.snapshot;
6730 this.sortInfo = meta.sortInfo || this.sortInfo;
6732 this.fireEvent('metachange', this, this.reader.meta);
6735 moveIndex : function(data, type)
6737 var index = this.indexOf(data);
6739 var newIndex = index + type;
6743 this.insert(newIndex, data);
6748 * Ext JS Library 1.1.1
6749 * Copyright(c) 2006-2007, Ext JS, LLC.
6751 * Originally Released Under LGPL - original licence link has changed is not relivant.
6754 * <script type="text/javascript">
6758 * @class Roo.data.SimpleStore
6759 * @extends Roo.data.Store
6760 * Small helper class to make creating Stores from Array data easier.
6761 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6762 * @cfg {Array} fields An array of field definition objects, or field name strings.
6763 * @cfg {Array} data The multi-dimensional array of data
6765 * @param {Object} config
6767 Roo.data.SimpleStore = function(config){
6768 Roo.data.SimpleStore.superclass.constructor.call(this, {
6770 reader: new Roo.data.ArrayReader({
6773 Roo.data.Record.create(config.fields)
6775 proxy : new Roo.data.MemoryProxy(config.data)
6779 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6781 * Ext JS Library 1.1.1
6782 * Copyright(c) 2006-2007, Ext JS, LLC.
6784 * Originally Released Under LGPL - original licence link has changed is not relivant.
6787 * <script type="text/javascript">
6792 * @extends Roo.data.Store
6793 * @class Roo.data.JsonStore
6794 * Small helper class to make creating Stores for JSON data easier. <br/>
6796 var store = new Roo.data.JsonStore({
6797 url: 'get-images.php',
6799 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6802 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6803 * JsonReader and HttpProxy (unless inline data is provided).</b>
6804 * @cfg {Array} fields An array of field definition objects, or field name strings.
6806 * @param {Object} config
6808 Roo.data.JsonStore = function(c){
6809 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6810 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6811 reader: new Roo.data.JsonReader(c, c.fields)
6814 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6816 * Ext JS Library 1.1.1
6817 * Copyright(c) 2006-2007, Ext JS, LLC.
6819 * Originally Released Under LGPL - original licence link has changed is not relivant.
6822 * <script type="text/javascript">
6826 Roo.data.Field = function(config){
6827 if(typeof config == "string"){
6828 config = {name: config};
6830 Roo.apply(this, config);
6836 var st = Roo.data.SortTypes;
6837 // named sortTypes are supported, here we look them up
6838 if(typeof this.sortType == "string"){
6839 this.sortType = st[this.sortType];
6842 // set default sortType for strings and dates
6846 this.sortType = st.asUCString;
6849 this.sortType = st.asDate;
6852 this.sortType = st.none;
6857 var stripRe = /[\$,%]/g;
6859 // prebuilt conversion function for this field, instead of
6860 // switching every time we're reading a value
6862 var cv, dateFormat = this.dateFormat;
6867 cv = function(v){ return v; };
6870 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6874 return v !== undefined && v !== null && v !== '' ?
6875 parseInt(String(v).replace(stripRe, ""), 10) : '';
6880 return v !== undefined && v !== null && v !== '' ?
6881 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6886 cv = function(v){ return v === true || v === "true" || v == 1; };
6893 if(v instanceof Date){
6897 if(dateFormat == "timestamp"){
6898 return new Date(v*1000);
6900 return Date.parseDate(v, dateFormat);
6902 var parsed = Date.parse(v);
6903 return parsed ? new Date(parsed) : null;
6912 Roo.data.Field.prototype = {
6920 * Ext JS Library 1.1.1
6921 * Copyright(c) 2006-2007, Ext JS, LLC.
6923 * Originally Released Under LGPL - original licence link has changed is not relivant.
6926 * <script type="text/javascript">
6929 // Base class for reading structured data from a data source. This class is intended to be
6930 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6933 * @class Roo.data.DataReader
6934 * Base class for reading structured data from a data source. This class is intended to be
6935 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6938 Roo.data.DataReader = function(meta, recordType){
6942 this.recordType = recordType instanceof Array ?
6943 Roo.data.Record.create(recordType) : recordType;
6946 Roo.data.DataReader.prototype = {
6948 * Create an empty record
6949 * @param {Object} data (optional) - overlay some values
6950 * @return {Roo.data.Record} record created.
6952 newRow : function(d) {
6954 this.recordType.prototype.fields.each(function(c) {
6956 case 'int' : da[c.name] = 0; break;
6957 case 'date' : da[c.name] = new Date(); break;
6958 case 'float' : da[c.name] = 0.0; break;
6959 case 'boolean' : da[c.name] = false; break;
6960 default : da[c.name] = ""; break;
6964 return new this.recordType(Roo.apply(da, d));
6969 * Ext JS Library 1.1.1
6970 * Copyright(c) 2006-2007, Ext JS, LLC.
6972 * Originally Released Under LGPL - original licence link has changed is not relivant.
6975 * <script type="text/javascript">
6979 * @class Roo.data.DataProxy
6980 * @extends Roo.data.Observable
6981 * This class is an abstract base class for implementations which provide retrieval of
6982 * unformatted data objects.<br>
6984 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6985 * (of the appropriate type which knows how to parse the data object) to provide a block of
6986 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6988 * Custom implementations must implement the load method as described in
6989 * {@link Roo.data.HttpProxy#load}.
6991 Roo.data.DataProxy = function(){
6995 * Fires before a network request is made to retrieve a data object.
6996 * @param {Object} This DataProxy object.
6997 * @param {Object} params The params parameter to the load function.
7002 * Fires before the load method's callback is called.
7003 * @param {Object} This DataProxy object.
7004 * @param {Object} o The data object.
7005 * @param {Object} arg The callback argument object passed to the load function.
7009 * @event loadexception
7010 * Fires if an Exception occurs during data retrieval.
7011 * @param {Object} This DataProxy object.
7012 * @param {Object} o The data object.
7013 * @param {Object} arg The callback argument object passed to the load function.
7014 * @param {Object} e The Exception.
7016 loadexception : true
7018 Roo.data.DataProxy.superclass.constructor.call(this);
7021 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7024 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7028 * Ext JS Library 1.1.1
7029 * Copyright(c) 2006-2007, Ext JS, LLC.
7031 * Originally Released Under LGPL - original licence link has changed is not relivant.
7034 * <script type="text/javascript">
7037 * @class Roo.data.MemoryProxy
7038 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7039 * to the Reader when its load method is called.
7041 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7043 Roo.data.MemoryProxy = function(data){
7047 Roo.data.MemoryProxy.superclass.constructor.call(this);
7051 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7053 * Load data from the requested source (in this case an in-memory
7054 * data object passed to the constructor), read the data object into
7055 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7056 * process that block using the passed callback.
7057 * @param {Object} params This parameter is not used by the MemoryProxy class.
7058 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7059 * object into a block of Roo.data.Records.
7060 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7061 * The function must be passed <ul>
7062 * <li>The Record block object</li>
7063 * <li>The "arg" argument from the load function</li>
7064 * <li>A boolean success indicator</li>
7066 * @param {Object} scope The scope in which to call the callback
7067 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7069 load : function(params, reader, callback, scope, arg){
7070 params = params || {};
7073 result = reader.readRecords(this.data);
7075 this.fireEvent("loadexception", this, arg, null, e);
7076 callback.call(scope, null, arg, false);
7079 callback.call(scope, result, arg, true);
7083 update : function(params, records){
7088 * Ext JS Library 1.1.1
7089 * Copyright(c) 2006-2007, Ext JS, LLC.
7091 * Originally Released Under LGPL - original licence link has changed is not relivant.
7094 * <script type="text/javascript">
7097 * @class Roo.data.HttpProxy
7098 * @extends Roo.data.DataProxy
7099 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7100 * configured to reference a certain URL.<br><br>
7102 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7103 * from which the running page was served.<br><br>
7105 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7107 * Be aware that to enable the browser to parse an XML document, the server must set
7108 * the Content-Type header in the HTTP response to "text/xml".
7110 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7111 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7112 * will be used to make the request.
7114 Roo.data.HttpProxy = function(conn){
7115 Roo.data.HttpProxy.superclass.constructor.call(this);
7116 // is conn a conn config or a real conn?
7118 this.useAjax = !conn || !conn.events;
7122 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7123 // thse are take from connection...
7126 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7129 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7130 * extra parameters to each request made by this object. (defaults to undefined)
7133 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7134 * to each request made by this object. (defaults to undefined)
7137 * @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)
7140 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7143 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7149 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7153 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7154 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7155 * a finer-grained basis than the DataProxy events.
7157 getConnection : function(){
7158 return this.useAjax ? Roo.Ajax : this.conn;
7162 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7163 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7164 * process that block using the passed callback.
7165 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7166 * for the request to the remote server.
7167 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7168 * object into a block of Roo.data.Records.
7169 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7170 * The function must be passed <ul>
7171 * <li>The Record block object</li>
7172 * <li>The "arg" argument from the load function</li>
7173 * <li>A boolean success indicator</li>
7175 * @param {Object} scope The scope in which to call the callback
7176 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7178 load : function(params, reader, callback, scope, arg){
7179 if(this.fireEvent("beforeload", this, params) !== false){
7181 params : params || {},
7183 callback : callback,
7188 callback : this.loadResponse,
7192 Roo.applyIf(o, this.conn);
7193 if(this.activeRequest){
7194 Roo.Ajax.abort(this.activeRequest);
7196 this.activeRequest = Roo.Ajax.request(o);
7198 this.conn.request(o);
7201 callback.call(scope||this, null, arg, false);
7206 loadResponse : function(o, success, response){
7207 delete this.activeRequest;
7209 this.fireEvent("loadexception", this, o, response);
7210 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7215 result = o.reader.read(response);
7217 this.fireEvent("loadexception", this, o, response, e);
7218 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7222 this.fireEvent("load", this, o, o.request.arg);
7223 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7227 update : function(dataSet){
7232 updateResponse : function(dataSet){
7237 * Ext JS Library 1.1.1
7238 * Copyright(c) 2006-2007, Ext JS, LLC.
7240 * Originally Released Under LGPL - original licence link has changed is not relivant.
7243 * <script type="text/javascript">
7247 * @class Roo.data.ScriptTagProxy
7248 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7249 * other than the originating domain of the running page.<br><br>
7251 * <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
7252 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7254 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7255 * source code that is used as the source inside a <script> tag.<br><br>
7257 * In order for the browser to process the returned data, the server must wrap the data object
7258 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7259 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7260 * depending on whether the callback name was passed:
7263 boolean scriptTag = false;
7264 String cb = request.getParameter("callback");
7267 response.setContentType("text/javascript");
7269 response.setContentType("application/x-json");
7271 Writer out = response.getWriter();
7273 out.write(cb + "(");
7275 out.print(dataBlock.toJsonString());
7282 * @param {Object} config A configuration object.
7284 Roo.data.ScriptTagProxy = function(config){
7285 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7286 Roo.apply(this, config);
7287 this.head = document.getElementsByTagName("head")[0];
7290 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7292 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7294 * @cfg {String} url The URL from which to request the data object.
7297 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7301 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7302 * the server the name of the callback function set up by the load call to process the returned data object.
7303 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7304 * javascript output which calls this named function passing the data object as its only parameter.
7306 callbackParam : "callback",
7308 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7309 * name to the request.
7314 * Load data from the configured URL, read the data object into
7315 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7316 * process that block using the passed callback.
7317 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7318 * for the request to the remote server.
7319 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7320 * object into a block of Roo.data.Records.
7321 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7322 * The function must be passed <ul>
7323 * <li>The Record block object</li>
7324 * <li>The "arg" argument from the load function</li>
7325 * <li>A boolean success indicator</li>
7327 * @param {Object} scope The scope in which to call the callback
7328 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7330 load : function(params, reader, callback, scope, arg){
7331 if(this.fireEvent("beforeload", this, params) !== false){
7333 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7336 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7338 url += "&_dc=" + (new Date().getTime());
7340 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7343 cb : "stcCallback"+transId,
7344 scriptId : "stcScript"+transId,
7348 callback : callback,
7354 window[trans.cb] = function(o){
7355 conn.handleResponse(o, trans);
7358 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7360 if(this.autoAbort !== false){
7364 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7366 var script = document.createElement("script");
7367 script.setAttribute("src", url);
7368 script.setAttribute("type", "text/javascript");
7369 script.setAttribute("id", trans.scriptId);
7370 this.head.appendChild(script);
7374 callback.call(scope||this, null, arg, false);
7379 isLoading : function(){
7380 return this.trans ? true : false;
7384 * Abort the current server request.
7387 if(this.isLoading()){
7388 this.destroyTrans(this.trans);
7393 destroyTrans : function(trans, isLoaded){
7394 this.head.removeChild(document.getElementById(trans.scriptId));
7395 clearTimeout(trans.timeoutId);
7397 window[trans.cb] = undefined;
7399 delete window[trans.cb];
7402 // if hasn't been loaded, wait for load to remove it to prevent script error
7403 window[trans.cb] = function(){
7404 window[trans.cb] = undefined;
7406 delete window[trans.cb];
7413 handleResponse : function(o, trans){
7415 this.destroyTrans(trans, true);
7418 result = trans.reader.readRecords(o);
7420 this.fireEvent("loadexception", this, o, trans.arg, e);
7421 trans.callback.call(trans.scope||window, null, trans.arg, false);
7424 this.fireEvent("load", this, o, trans.arg);
7425 trans.callback.call(trans.scope||window, result, trans.arg, true);
7429 handleFailure : function(trans){
7431 this.destroyTrans(trans, false);
7432 this.fireEvent("loadexception", this, null, trans.arg);
7433 trans.callback.call(trans.scope||window, null, trans.arg, false);
7437 * Ext JS Library 1.1.1
7438 * Copyright(c) 2006-2007, Ext JS, LLC.
7440 * Originally Released Under LGPL - original licence link has changed is not relivant.
7443 * <script type="text/javascript">
7447 * @class Roo.data.JsonReader
7448 * @extends Roo.data.DataReader
7449 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7450 * based on mappings in a provided Roo.data.Record constructor.
7452 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7453 * in the reply previously.
7458 var RecordDef = Roo.data.Record.create([
7459 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7460 {name: 'occupation'} // This field will use "occupation" as the mapping.
7462 var myReader = new Roo.data.JsonReader({
7463 totalProperty: "results", // The property which contains the total dataset size (optional)
7464 root: "rows", // The property which contains an Array of row objects
7465 id: "id" // The property within each row object that provides an ID for the record (optional)
7469 * This would consume a JSON file like this:
7471 { 'results': 2, 'rows': [
7472 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7473 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7476 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7477 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7478 * paged from the remote server.
7479 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7480 * @cfg {String} root name of the property which contains the Array of row objects.
7481 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7483 * Create a new JsonReader
7484 * @param {Object} meta Metadata configuration options
7485 * @param {Object} recordType Either an Array of field definition objects,
7486 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7488 Roo.data.JsonReader = function(meta, recordType){
7491 // set some defaults:
7493 totalProperty: 'total',
7494 successProperty : 'success',
7499 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7501 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7504 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7505 * Used by Store query builder to append _requestMeta to params.
7508 metaFromRemote : false,
7510 * This method is only used by a DataProxy which has retrieved data from a remote server.
7511 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7512 * @return {Object} data A data block which is used by an Roo.data.Store object as
7513 * a cache of Roo.data.Records.
7515 read : function(response){
7516 var json = response.responseText;
7518 var o = /* eval:var:o */ eval("("+json+")");
7520 throw {message: "JsonReader.read: Json object not found"};
7526 this.metaFromRemote = true;
7527 this.meta = o.metaData;
7528 this.recordType = Roo.data.Record.create(o.metaData.fields);
7529 this.onMetaChange(this.meta, this.recordType, o);
7531 return this.readRecords(o);
7534 // private function a store will implement
7535 onMetaChange : function(meta, recordType, o){
7542 simpleAccess: function(obj, subsc) {
7549 getJsonAccessor: function(){
7551 return function(expr) {
7553 return(re.test(expr))
7554 ? new Function("obj", "return obj." + expr)
7564 * Create a data block containing Roo.data.Records from an XML document.
7565 * @param {Object} o An object which contains an Array of row objects in the property specified
7566 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7567 * which contains the total size of the dataset.
7568 * @return {Object} data A data block which is used by an Roo.data.Store object as
7569 * a cache of Roo.data.Records.
7571 readRecords : function(o){
7573 * After any data loads, the raw JSON data is available for further custom processing.
7577 var s = this.meta, Record = this.recordType,
7578 f = Record.prototype.fields, fi = f.items, fl = f.length;
7580 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7582 if(s.totalProperty) {
7583 this.getTotal = this.getJsonAccessor(s.totalProperty);
7585 if(s.successProperty) {
7586 this.getSuccess = this.getJsonAccessor(s.successProperty);
7588 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7590 var g = this.getJsonAccessor(s.id);
7591 this.getId = function(rec) {
7593 return (r === undefined || r === "") ? null : r;
7596 this.getId = function(){return null;};
7599 for(var jj = 0; jj < fl; jj++){
7601 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7602 this.ef[jj] = this.getJsonAccessor(map);
7606 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7607 if(s.totalProperty){
7608 var vt = parseInt(this.getTotal(o), 10);
7613 if(s.successProperty){
7614 var vs = this.getSuccess(o);
7615 if(vs === false || vs === 'false'){
7620 for(var i = 0; i < c; i++){
7623 var id = this.getId(n);
7624 for(var j = 0; j < fl; j++){
7626 var v = this.ef[j](n);
7628 Roo.log('missing convert for ' + f.name);
7632 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7634 var record = new Record(values, id);
7636 records[i] = record;
7642 totalRecords : totalRecords
7647 * Ext JS Library 1.1.1
7648 * Copyright(c) 2006-2007, Ext JS, LLC.
7650 * Originally Released Under LGPL - original licence link has changed is not relivant.
7653 * <script type="text/javascript">
7657 * @class Roo.data.ArrayReader
7658 * @extends Roo.data.DataReader
7659 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7660 * Each element of that Array represents a row of data fields. The
7661 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7662 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7666 var RecordDef = Roo.data.Record.create([
7667 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7668 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7670 var myReader = new Roo.data.ArrayReader({
7671 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7675 * This would consume an Array like this:
7677 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7679 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7681 * Create a new JsonReader
7682 * @param {Object} meta Metadata configuration options.
7683 * @param {Object} recordType Either an Array of field definition objects
7684 * as specified to {@link Roo.data.Record#create},
7685 * or an {@link Roo.data.Record} object
7686 * created using {@link Roo.data.Record#create}.
7688 Roo.data.ArrayReader = function(meta, recordType){
7689 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7692 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7694 * Create a data block containing Roo.data.Records from an XML document.
7695 * @param {Object} o An Array of row objects which represents the dataset.
7696 * @return {Object} data A data block which is used by an Roo.data.Store object as
7697 * a cache of Roo.data.Records.
7699 readRecords : function(o){
7700 var sid = this.meta ? this.meta.id : null;
7701 var recordType = this.recordType, fields = recordType.prototype.fields;
7704 for(var i = 0; i < root.length; i++){
7707 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7708 for(var j = 0, jlen = fields.length; j < jlen; j++){
7709 var f = fields.items[j];
7710 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7711 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7715 var record = new recordType(values, id);
7717 records[records.length] = record;
7721 totalRecords : records.length
7730 * @class Roo.bootstrap.ComboBox
7731 * @extends Roo.bootstrap.TriggerField
7732 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7733 * @cfg {Boolean} append (true|false) default false
7735 * Create a new ComboBox.
7736 * @param {Object} config Configuration options
7738 Roo.bootstrap.ComboBox = function(config){
7739 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7743 * Fires when the dropdown list is expanded
7744 * @param {Roo.bootstrap.ComboBox} combo This combo box
7749 * Fires when the dropdown list is collapsed
7750 * @param {Roo.bootstrap.ComboBox} combo This combo box
7754 * @event beforeselect
7755 * Fires before a list item is selected. Return false to cancel the selection.
7756 * @param {Roo.bootstrap.ComboBox} combo This combo box
7757 * @param {Roo.data.Record} record The data record returned from the underlying store
7758 * @param {Number} index The index of the selected item in the dropdown list
7760 'beforeselect' : true,
7763 * Fires when a list item is selected
7764 * @param {Roo.bootstrap.ComboBox} combo This combo box
7765 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7766 * @param {Number} index The index of the selected item in the dropdown list
7770 * @event beforequery
7771 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7772 * The event object passed has these properties:
7773 * @param {Roo.bootstrap.ComboBox} combo This combo box
7774 * @param {String} query The query
7775 * @param {Boolean} forceAll true to force "all" query
7776 * @param {Boolean} cancel true to cancel the query
7777 * @param {Object} e The query event object
7779 'beforequery': true,
7782 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7783 * @param {Roo.bootstrap.ComboBox} combo This combo box
7788 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7789 * @param {Roo.bootstrap.ComboBox} combo This combo box
7790 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7795 * Fires when the remove value from the combobox array
7796 * @param {Roo.bootstrap.ComboBox} combo This combo box
7803 this.selectedIndex = -1;
7804 if(this.mode == 'local'){
7805 if(config.queryDelay === undefined){
7806 this.queryDelay = 10;
7808 if(config.minChars === undefined){
7814 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7817 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7818 * rendering into an Roo.Editor, defaults to false)
7821 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7822 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7825 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7828 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7829 * the dropdown list (defaults to undefined, with no header element)
7833 * @cfg {String/Roo.Template} tpl The template to use to render the output
7837 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7839 listWidth: undefined,
7841 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7842 * mode = 'remote' or 'text' if mode = 'local')
7844 displayField: undefined,
7846 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7847 * mode = 'remote' or 'value' if mode = 'local').
7848 * Note: use of a valueField requires the user make a selection
7849 * in order for a value to be mapped.
7851 valueField: undefined,
7855 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7856 * field's data value (defaults to the underlying DOM element's name)
7858 hiddenName: undefined,
7860 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7864 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7866 selectedClass: 'active',
7869 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7873 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7874 * anchor positions (defaults to 'tl-bl')
7876 listAlign: 'tl-bl?',
7878 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7882 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7883 * query specified by the allQuery config option (defaults to 'query')
7885 triggerAction: 'query',
7887 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7888 * (defaults to 4, does not apply if editable = false)
7892 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7893 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7897 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7898 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7902 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7903 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7907 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7908 * when editable = true (defaults to false)
7910 selectOnFocus:false,
7912 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7914 queryParam: 'query',
7916 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7917 * when mode = 'remote' (defaults to 'Loading...')
7919 loadingText: 'Loading...',
7921 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7925 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7929 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7930 * traditional select (defaults to true)
7934 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7938 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7942 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7943 * listWidth has a higher value)
7947 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7948 * allow the user to set arbitrary text into the field (defaults to false)
7950 forceSelection:false,
7952 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7953 * if typeAhead = true (defaults to 250)
7955 typeAheadDelay : 250,
7957 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7958 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7960 valueNotFoundText : undefined,
7962 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7967 * @cfg {Boolean} disableClear Disable showing of clear button.
7969 disableClear : false,
7971 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
7973 alwaysQuery : false,
7976 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
7990 // element that contains real text value.. (when hidden is used..)
7993 initEvents: function(){
7996 throw "can not find store for combo";
7998 this.store = Roo.factory(this.store, Roo.data);
8002 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8005 if(this.hiddenName){
8007 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8009 this.hiddenField.dom.value =
8010 this.hiddenValue !== undefined ? this.hiddenValue :
8011 this.value !== undefined ? this.value : '';
8013 // prevent input submission
8014 this.el.dom.removeAttribute('name');
8015 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8020 // this.el.dom.setAttribute('autocomplete', 'off');
8023 var cls = 'x-combo-list';
8024 this.list = this.el.select('ul.dropdown-menu',true).first();
8026 //this.list = new Roo.Layer({
8027 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8030 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8031 this.list.setWidth(lw);
8033 this.list.on('mouseover', this.onViewOver, this);
8034 this.list.on('mousemove', this.onViewMove, this);
8036 this.list.on('scroll', this.onViewScroll, this);
8039 this.list.swallowEvent('mousewheel');
8040 this.assetHeight = 0;
8043 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8044 this.assetHeight += this.header.getHeight();
8047 this.innerList = this.list.createChild({cls:cls+'-inner'});
8048 this.innerList.on('mouseover', this.onViewOver, this);
8049 this.innerList.on('mousemove', this.onViewMove, this);
8050 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8052 if(this.allowBlank && !this.pageSize && !this.disableClear){
8053 this.footer = this.list.createChild({cls:cls+'-ft'});
8054 this.pageTb = new Roo.Toolbar(this.footer);
8058 this.footer = this.list.createChild({cls:cls+'-ft'});
8059 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8060 {pageSize: this.pageSize});
8064 if (this.pageTb && this.allowBlank && !this.disableClear) {
8066 this.pageTb.add(new Roo.Toolbar.Fill(), {
8067 cls: 'x-btn-icon x-btn-clear',
8073 _this.onSelect(false, -1);
8078 this.assetHeight += this.footer.getHeight();
8083 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8086 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8087 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8089 //this.view.wrapEl.setDisplayed(false);
8090 this.view.on('click', this.onViewClick, this);
8094 this.store.on('beforeload', this.onBeforeLoad, this);
8095 this.store.on('load', this.onLoad, this);
8096 this.store.on('loadexception', this.onLoadException, this);
8099 this.resizer = new Roo.Resizable(this.list, {
8100 pinned:true, handles:'se'
8102 this.resizer.on('resize', function(r, w, h){
8103 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8105 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8106 this.restrictHeight();
8108 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8112 this.editable = true;
8113 this.setEditable(false);
8118 if (typeof(this.events.add.listeners) != 'undefined') {
8120 this.addicon = this.wrap.createChild(
8121 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8123 this.addicon.on('click', function(e) {
8124 this.fireEvent('add', this);
8127 if (typeof(this.events.edit.listeners) != 'undefined') {
8129 this.editicon = this.wrap.createChild(
8130 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8132 this.editicon.setStyle('margin-left', '40px');
8134 this.editicon.on('click', function(e) {
8136 // we fire even if inothing is selected..
8137 this.fireEvent('edit', this, this.lastData );
8143 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8145 this.inKeyMode = true;
8149 "down" : function(e){
8150 if(!this.isExpanded()){
8151 this.onTriggerClick();
8153 this.inKeyMode = true;
8158 "enter" : function(e){
8163 "esc" : function(e){
8167 "tab" : function(e){
8170 if(this.fireEvent("specialkey", this, e)){
8171 this.onViewClick(false);
8179 doRelay : function(foo, bar, hname){
8180 if(hname == 'down' || this.scope.isExpanded()){
8181 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8190 this.queryDelay = Math.max(this.queryDelay || 10,
8191 this.mode == 'local' ? 10 : 250);
8194 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8197 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8199 if(this.editable !== false){
8200 this.inputEl().on("keyup", this.onKeyUp, this);
8202 if(this.forceSelection){
8203 this.on('blur', this.doForce, this);
8207 this.choices = this.el.select('ul.select2-choices', true).first();
8208 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8212 onDestroy : function(){
8214 this.view.setStore(null);
8215 this.view.el.removeAllListeners();
8216 this.view.el.remove();
8217 this.view.purgeListeners();
8220 this.list.dom.innerHTML = '';
8223 this.store.un('beforeload', this.onBeforeLoad, this);
8224 this.store.un('load', this.onLoad, this);
8225 this.store.un('loadexception', this.onLoadException, this);
8227 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8231 fireKey : function(e){
8232 if(e.isNavKeyPress() && !this.list.isVisible()){
8233 this.fireEvent("specialkey", this, e);
8238 onResize: function(w, h){
8239 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8241 // if(typeof w != 'number'){
8242 // // we do not handle it!?!?
8245 // var tw = this.trigger.getWidth();
8246 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8247 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8249 // this.inputEl().setWidth( this.adjustWidth('input', x));
8251 // //this.trigger.setStyle('left', x+'px');
8253 // if(this.list && this.listWidth === undefined){
8254 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8255 // this.list.setWidth(lw);
8256 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8264 * Allow or prevent the user from directly editing the field text. If false is passed,
8265 * the user will only be able to select from the items defined in the dropdown list. This method
8266 * is the runtime equivalent of setting the 'editable' config option at config time.
8267 * @param {Boolean} value True to allow the user to directly edit the field text
8269 setEditable : function(value){
8270 if(value == this.editable){
8273 this.editable = value;
8275 this.inputEl().dom.setAttribute('readOnly', true);
8276 this.inputEl().on('mousedown', this.onTriggerClick, this);
8277 this.inputEl().addClass('x-combo-noedit');
8279 this.inputEl().dom.setAttribute('readOnly', false);
8280 this.inputEl().un('mousedown', this.onTriggerClick, this);
8281 this.inputEl().removeClass('x-combo-noedit');
8287 onBeforeLoad : function(combo,opts){
8292 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8294 this.restrictHeight();
8295 this.selectedIndex = -1;
8299 onLoad : function(){
8301 this.hasQuery = false;
8307 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8308 this.loading.hide();
8311 if(this.store.getCount() > 0){
8313 this.restrictHeight();
8314 if(this.lastQuery == this.allQuery){
8316 this.inputEl().dom.select();
8318 if(!this.selectByValue(this.value, true)){
8319 this.select(0, true);
8323 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8324 this.taTask.delay(this.typeAheadDelay);
8328 this.onEmptyResults();
8334 onLoadException : function()
8336 this.hasQuery = false;
8338 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8339 this.loading.hide();
8343 Roo.log(this.store.reader.jsonData);
8344 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8346 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8352 onTypeAhead : function(){
8353 if(this.store.getCount() > 0){
8354 var r = this.store.getAt(0);
8355 var newValue = r.data[this.displayField];
8356 var len = newValue.length;
8357 var selStart = this.getRawValue().length;
8359 if(selStart != len){
8360 this.setRawValue(newValue);
8361 this.selectText(selStart, newValue.length);
8367 onSelect : function(record, index){
8369 if(this.fireEvent('beforeselect', this, record, index) !== false){
8371 this.setFromData(index > -1 ? record.data : false);
8374 this.fireEvent('select', this, record, index);
8379 * Returns the currently selected field value or empty string if no value is set.
8380 * @return {String} value The selected value
8382 getValue : function(){
8385 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8388 if(this.valueField){
8389 return typeof this.value != 'undefined' ? this.value : '';
8391 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8396 * Clears any text/value currently set in the field
8398 clearValue : function(){
8399 if(this.hiddenField){
8400 this.hiddenField.dom.value = '';
8403 this.setRawValue('');
8404 this.lastSelectionText = '';
8409 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8410 * will be displayed in the field. If the value does not match the data value of an existing item,
8411 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8412 * Otherwise the field will be blank (although the value will still be set).
8413 * @param {String} value The value to match
8415 setValue : function(v){
8422 if(this.valueField){
8423 var r = this.findRecord(this.valueField, v);
8425 text = r.data[this.displayField];
8426 }else if(this.valueNotFoundText !== undefined){
8427 text = this.valueNotFoundText;
8430 this.lastSelectionText = text;
8431 if(this.hiddenField){
8432 this.hiddenField.dom.value = v;
8434 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8438 * @property {Object} the last set data for the element
8443 * Sets the value of the field based on a object which is related to the record format for the store.
8444 * @param {Object} value the value to set as. or false on reset?
8446 setFromData : function(o){
8453 var dv = ''; // display value
8454 var vv = ''; // value value..
8456 if (this.displayField) {
8457 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8459 // this is an error condition!!!
8460 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8463 if(this.valueField){
8464 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8467 if(this.hiddenField){
8468 this.hiddenField.dom.value = vv;
8470 this.lastSelectionText = dv;
8471 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8475 // no hidden field.. - we store the value in 'value', but still display
8476 // display field!!!!
8477 this.lastSelectionText = dv;
8478 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8485 // overridden so that last data is reset..
8486 this.setValue(this.originalValue);
8487 this.clearInvalid();
8488 this.lastData = false;
8490 this.view.clearSelections();
8494 findRecord : function(prop, value){
8496 if(this.store.getCount() > 0){
8497 this.store.each(function(r){
8498 if(r.data[prop] == value){
8510 // returns hidden if it's set..
8511 if (!this.rendered) {return ''};
8512 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8516 onViewMove : function(e, t){
8517 this.inKeyMode = false;
8521 onViewOver : function(e, t){
8522 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8525 var item = this.view.findItemFromChild(t);
8527 var index = this.view.indexOf(item);
8528 this.select(index, false);
8533 onViewClick : function(doFocus)
8535 var index = this.view.getSelectedIndexes()[0];
8536 var r = this.store.getAt(index);
8538 this.onSelect(r, index);
8540 if(doFocus !== false && !this.blockFocus){
8541 this.inputEl().focus();
8546 restrictHeight : function(){
8547 //this.innerList.dom.style.height = '';
8548 //var inner = this.innerList.dom;
8549 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8550 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8551 //this.list.beginUpdate();
8552 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8553 this.list.alignTo(this.inputEl(), this.listAlign);
8554 //this.list.endUpdate();
8558 onEmptyResults : function(){
8563 * Returns true if the dropdown list is expanded, else false.
8565 isExpanded : function(){
8566 return this.list.isVisible();
8570 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8571 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8572 * @param {String} value The data value of the item to select
8573 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8574 * selected item if it is not currently in view (defaults to true)
8575 * @return {Boolean} True if the value matched an item in the list, else false
8577 selectByValue : function(v, scrollIntoView){
8578 if(v !== undefined && v !== null){
8579 var r = this.findRecord(this.valueField || this.displayField, v);
8581 this.select(this.store.indexOf(r), scrollIntoView);
8589 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8590 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8591 * @param {Number} index The zero-based index of the list item to select
8592 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8593 * selected item if it is not currently in view (defaults to true)
8595 select : function(index, scrollIntoView){
8596 this.selectedIndex = index;
8597 this.view.select(index);
8598 if(scrollIntoView !== false){
8599 var el = this.view.getNode(index);
8601 //this.innerList.scrollChildIntoView(el, false);
8608 selectNext : function(){
8609 var ct = this.store.getCount();
8611 if(this.selectedIndex == -1){
8613 }else if(this.selectedIndex < ct-1){
8614 this.select(this.selectedIndex+1);
8620 selectPrev : function(){
8621 var ct = this.store.getCount();
8623 if(this.selectedIndex == -1){
8625 }else if(this.selectedIndex != 0){
8626 this.select(this.selectedIndex-1);
8632 onKeyUp : function(e){
8633 if(this.editable !== false && !e.isSpecialKey()){
8634 this.lastKey = e.getKey();
8635 this.dqTask.delay(this.queryDelay);
8640 validateBlur : function(){
8641 return !this.list || !this.list.isVisible();
8645 initQuery : function(){
8646 this.doQuery(this.getRawValue());
8650 doForce : function(){
8651 if(this.el.dom.value.length > 0){
8653 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8659 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8660 * query allowing the query action to be canceled if needed.
8661 * @param {String} query The SQL query to execute
8662 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8663 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8664 * saved in the current store (defaults to false)
8666 doQuery : function(q, forceAll){
8668 if(q === undefined || q === null){
8677 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8682 forceAll = qe.forceAll;
8683 if(forceAll === true || (q.length >= this.minChars)){
8685 this.hasQuery = true;
8687 if(this.lastQuery != q || this.alwaysQuery){
8689 if(this.mode == 'local'){
8690 this.selectedIndex = -1;
8692 this.store.clearFilter();
8694 this.store.filter(this.displayField, q);
8698 this.store.baseParams[this.queryParam] = q;
8700 var options = {params : this.getParams(q)};
8704 options.params.start = this.page * this.pageSize;
8707 this.store.load(options);
8711 this.selectedIndex = -1;
8716 this.loadNext = false;
8720 getParams : function(q){
8722 //p[this.queryParam] = q;
8726 p.limit = this.pageSize;
8732 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8734 collapse : function(){
8735 if(!this.isExpanded()){
8740 Roo.get(document).un('mousedown', this.collapseIf, this);
8741 Roo.get(document).un('mousewheel', this.collapseIf, this);
8742 if (!this.editable) {
8743 Roo.get(document).un('keydown', this.listKeyPress, this);
8745 this.fireEvent('collapse', this);
8749 collapseIf : function(e){
8750 var in_combo = e.within(this.el);
8751 var in_list = e.within(this.list);
8753 if (in_combo || in_list) {
8754 //e.stopPropagation();
8763 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8765 expand : function(){
8767 if(this.isExpanded() || !this.hasFocus){
8771 this.list.alignTo(this.inputEl(), this.listAlign);
8773 Roo.get(document).on('mousedown', this.collapseIf, this);
8774 Roo.get(document).on('mousewheel', this.collapseIf, this);
8775 if (!this.editable) {
8776 Roo.get(document).on('keydown', this.listKeyPress, this);
8779 this.fireEvent('expand', this);
8783 // Implements the default empty TriggerField.onTriggerClick function
8784 onTriggerClick : function()
8786 Roo.log('trigger click');
8793 this.loadNext = false;
8795 if(this.isExpanded()){
8797 if (!this.blockFocus) {
8798 this.inputEl().focus();
8802 this.hasFocus = true;
8803 if(this.triggerAction == 'all') {
8804 this.doQuery(this.allQuery, true);
8806 this.doQuery(this.getRawValue());
8808 if (!this.blockFocus) {
8809 this.inputEl().focus();
8813 listKeyPress : function(e)
8815 //Roo.log('listkeypress');
8816 // scroll to first matching element based on key pres..
8817 if (e.isSpecialKey()) {
8820 var k = String.fromCharCode(e.getKey()).toUpperCase();
8823 var csel = this.view.getSelectedNodes();
8824 var cselitem = false;
8826 var ix = this.view.indexOf(csel[0]);
8827 cselitem = this.store.getAt(ix);
8828 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8834 this.store.each(function(v) {
8836 // start at existing selection.
8837 if (cselitem.id == v.id) {
8843 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8844 match = this.store.indexOf(v);
8850 if (match === false) {
8851 return true; // no more action?
8854 this.view.select(match);
8855 var sn = Roo.get(this.view.getSelectedNodes()[0])
8856 //sn.scrollIntoView(sn.dom.parentNode, false);
8859 onViewScroll : function(e, t){
8861 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8865 this.hasQuery = true;
8867 this.loading = this.list.select('.loading', true).first();
8869 if(this.loading === null){
8870 this.list.createChild({
8872 cls: 'loading select2-more-results select2-active',
8873 html: 'Loading more results...'
8876 this.loading = this.list.select('.loading', true).first();
8878 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8880 this.loading.hide();
8883 this.loading.show();
8888 this.loadNext = true;
8890 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8895 addItem : function(o)
8897 var dv = ''; // display value
8899 if (this.displayField) {
8900 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8902 // this is an error condition!!!
8903 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8910 var choice = this.choices.createChild({
8912 cls: 'select2-search-choice',
8921 cls: 'select2-search-choice-close',
8926 }, this.searchField);
8928 var close = choice.select('a.select2-search-choice-close', true).first()
8930 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8937 this.inputEl().dom.value = '';
8941 onRemoveItem : function(e, _self, o)
8943 Roo.log('remove item');
8944 var index = this.item.indexOf(o.data) * 1;
8947 Roo.log('not this item?!');
8951 this.item.splice(index, 1);
8956 this.fireEvent('remove', this);
8960 syncValue : function()
8962 if(!this.item.length){
8969 Roo.each(this.item, function(i){
8970 if(_this.valueField){
8971 value.push(i[_this.valueField]);
8978 this.value = value.join(',');
8980 if(this.hiddenField){
8981 this.hiddenField.dom.value = this.value;
8985 clearItem : function()
8993 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9003 * @cfg {Boolean} grow
9007 * @cfg {Number} growMin
9011 * @cfg {Number} growMax
9021 * Ext JS Library 1.1.1
9022 * Copyright(c) 2006-2007, Ext JS, LLC.
9024 * Originally Released Under LGPL - original licence link has changed is not relivant.
9027 * <script type="text/javascript">
9032 * @extends Roo.util.Observable
9033 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9034 * This class also supports single and multi selection modes. <br>
9035 * Create a data model bound view:
9037 var store = new Roo.data.Store(...);
9039 var view = new Roo.View({
9041 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9044 selectedClass: "ydataview-selected",
9048 // listen for node click?
9049 view.on("click", function(vw, index, node, e){
9050 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9054 dataModel.load("foobar.xml");
9056 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9058 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9059 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9061 * Note: old style constructor is still suported (container, template, config)
9065 * @param {Object} config The config object
9068 Roo.View = function(config, depreciated_tpl, depreciated_config){
9070 if (typeof(depreciated_tpl) == 'undefined') {
9071 // new way.. - universal constructor.
9072 Roo.apply(this, config);
9073 this.el = Roo.get(this.el);
9076 this.el = Roo.get(config);
9077 this.tpl = depreciated_tpl;
9078 Roo.apply(this, depreciated_config);
9080 this.wrapEl = this.el.wrap().wrap();
9081 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9084 if(typeof(this.tpl) == "string"){
9085 this.tpl = new Roo.Template(this.tpl);
9087 // support xtype ctors..
9088 this.tpl = new Roo.factory(this.tpl, Roo);
9100 * @event beforeclick
9101 * Fires before a click is processed. Returns false to cancel the default action.
9102 * @param {Roo.View} this
9103 * @param {Number} index The index of the target node
9104 * @param {HTMLElement} node The target node
9105 * @param {Roo.EventObject} e The raw event object
9107 "beforeclick" : true,
9110 * Fires when a template node is clicked.
9111 * @param {Roo.View} this
9112 * @param {Number} index The index of the target node
9113 * @param {HTMLElement} node The target node
9114 * @param {Roo.EventObject} e The raw event object
9119 * Fires when a template node is double clicked.
9120 * @param {Roo.View} this
9121 * @param {Number} index The index of the target node
9122 * @param {HTMLElement} node The target node
9123 * @param {Roo.EventObject} e The raw event object
9127 * @event contextmenu
9128 * Fires when a template node is right clicked.
9129 * @param {Roo.View} this
9130 * @param {Number} index The index of the target node
9131 * @param {HTMLElement} node The target node
9132 * @param {Roo.EventObject} e The raw event object
9134 "contextmenu" : true,
9136 * @event selectionchange
9137 * Fires when the selected nodes change.
9138 * @param {Roo.View} this
9139 * @param {Array} selections Array of the selected nodes
9141 "selectionchange" : true,
9144 * @event beforeselect
9145 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9146 * @param {Roo.View} this
9147 * @param {HTMLElement} node The node to be selected
9148 * @param {Array} selections Array of currently selected nodes
9150 "beforeselect" : true,
9152 * @event preparedata
9153 * Fires on every row to render, to allow you to change the data.
9154 * @param {Roo.View} this
9155 * @param {Object} data to be rendered (change this)
9157 "preparedata" : true
9165 "click": this.onClick,
9166 "dblclick": this.onDblClick,
9167 "contextmenu": this.onContextMenu,
9171 this.selections = [];
9173 this.cmp = new Roo.CompositeElementLite([]);
9175 this.store = Roo.factory(this.store, Roo.data);
9176 this.setStore(this.store, true);
9179 if ( this.footer && this.footer.xtype) {
9181 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9183 this.footer.dataSource = this.store
9184 this.footer.container = fctr;
9185 this.footer = Roo.factory(this.footer, Roo);
9186 fctr.insertFirst(this.el);
9188 // this is a bit insane - as the paging toolbar seems to detach the el..
9189 // dom.parentNode.parentNode.parentNode
9190 // they get detached?
9194 Roo.View.superclass.constructor.call(this);
9199 Roo.extend(Roo.View, Roo.util.Observable, {
9202 * @cfg {Roo.data.Store} store Data store to load data from.
9207 * @cfg {String|Roo.Element} el The container element.
9212 * @cfg {String|Roo.Template} tpl The template used by this View
9216 * @cfg {String} dataName the named area of the template to use as the data area
9217 * Works with domtemplates roo-name="name"
9221 * @cfg {String} selectedClass The css class to add to selected nodes
9223 selectedClass : "x-view-selected",
9225 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9230 * @cfg {String} text to display on mask (default Loading)
9234 * @cfg {Boolean} multiSelect Allow multiple selection
9236 multiSelect : false,
9238 * @cfg {Boolean} singleSelect Allow single selection
9240 singleSelect: false,
9243 * @cfg {Boolean} toggleSelect - selecting
9245 toggleSelect : false,
9248 * Returns the element this view is bound to.
9249 * @return {Roo.Element}
9258 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9260 refresh : function(){
9264 // if we are using something like 'domtemplate', then
9265 // the what gets used is:
9266 // t.applySubtemplate(NAME, data, wrapping data..)
9267 // the outer template then get' applied with
9268 // the store 'extra data'
9269 // and the body get's added to the
9270 // roo-name="data" node?
9271 // <span class='roo-tpl-{name}'></span> ?????
9275 this.clearSelections();
9278 var records = this.store.getRange();
9279 if(records.length < 1) {
9281 // is this valid?? = should it render a template??
9283 this.el.update(this.emptyText);
9287 if (this.dataName) {
9288 this.el.update(t.apply(this.store.meta)); //????
9289 el = this.el.child('.roo-tpl-' + this.dataName);
9292 for(var i = 0, len = records.length; i < len; i++){
9293 var data = this.prepareData(records[i].data, i, records[i]);
9294 this.fireEvent("preparedata", this, data, i, records[i]);
9295 html[html.length] = Roo.util.Format.trim(
9297 t.applySubtemplate(this.dataName, data, this.store.meta) :
9304 el.update(html.join(""));
9305 this.nodes = el.dom.childNodes;
9306 this.updateIndexes(0);
9311 * Function to override to reformat the data that is sent to
9312 * the template for each node.
9313 * DEPRICATED - use the preparedata event handler.
9314 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9315 * a JSON object for an UpdateManager bound view).
9317 prepareData : function(data, index, record)
9319 this.fireEvent("preparedata", this, data, index, record);
9323 onUpdate : function(ds, record){
9324 Roo.log('on update');
9325 this.clearSelections();
9326 var index = this.store.indexOf(record);
9327 var n = this.nodes[index];
9328 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9329 n.parentNode.removeChild(n);
9330 this.updateIndexes(index, index);
9336 onAdd : function(ds, records, index)
9338 Roo.log(['on Add', ds, records, index] );
9339 this.clearSelections();
9340 if(this.nodes.length == 0){
9344 var n = this.nodes[index];
9345 for(var i = 0, len = records.length; i < len; i++){
9346 var d = this.prepareData(records[i].data, i, records[i]);
9348 this.tpl.insertBefore(n, d);
9351 this.tpl.append(this.el, d);
9354 this.updateIndexes(index);
9357 onRemove : function(ds, record, index){
9358 Roo.log('onRemove');
9359 this.clearSelections();
9360 var el = this.dataName ?
9361 this.el.child('.roo-tpl-' + this.dataName) :
9364 el.dom.removeChild(this.nodes[index]);
9365 this.updateIndexes(index);
9369 * Refresh an individual node.
9370 * @param {Number} index
9372 refreshNode : function(index){
9373 this.onUpdate(this.store, this.store.getAt(index));
9376 updateIndexes : function(startIndex, endIndex){
9377 var ns = this.nodes;
9378 startIndex = startIndex || 0;
9379 endIndex = endIndex || ns.length - 1;
9380 for(var i = startIndex; i <= endIndex; i++){
9381 ns[i].nodeIndex = i;
9386 * Changes the data store this view uses and refresh the view.
9387 * @param {Store} store
9389 setStore : function(store, initial){
9390 if(!initial && this.store){
9391 this.store.un("datachanged", this.refresh);
9392 this.store.un("add", this.onAdd);
9393 this.store.un("remove", this.onRemove);
9394 this.store.un("update", this.onUpdate);
9395 this.store.un("clear", this.refresh);
9396 this.store.un("beforeload", this.onBeforeLoad);
9397 this.store.un("load", this.onLoad);
9398 this.store.un("loadexception", this.onLoad);
9402 store.on("datachanged", this.refresh, this);
9403 store.on("add", this.onAdd, this);
9404 store.on("remove", this.onRemove, this);
9405 store.on("update", this.onUpdate, this);
9406 store.on("clear", this.refresh, this);
9407 store.on("beforeload", this.onBeforeLoad, this);
9408 store.on("load", this.onLoad, this);
9409 store.on("loadexception", this.onLoad, this);
9417 * onbeforeLoad - masks the loading area.
9420 onBeforeLoad : function(store,opts)
9422 Roo.log('onBeforeLoad');
9426 this.el.mask(this.mask ? this.mask : "Loading" );
9428 onLoad : function ()
9435 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9436 * @param {HTMLElement} node
9437 * @return {HTMLElement} The template node
9439 findItemFromChild : function(node){
9440 var el = this.dataName ?
9441 this.el.child('.roo-tpl-' + this.dataName,true) :
9444 if(!node || node.parentNode == el){
9447 var p = node.parentNode;
9448 while(p && p != el){
9449 if(p.parentNode == el){
9458 onClick : function(e){
9459 var item = this.findItemFromChild(e.getTarget());
9461 var index = this.indexOf(item);
9462 if(this.onItemClick(item, index, e) !== false){
9463 this.fireEvent("click", this, index, item, e);
9466 this.clearSelections();
9471 onContextMenu : function(e){
9472 var item = this.findItemFromChild(e.getTarget());
9474 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9479 onDblClick : function(e){
9480 var item = this.findItemFromChild(e.getTarget());
9482 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9486 onItemClick : function(item, index, e)
9488 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9491 if (this.toggleSelect) {
9492 var m = this.isSelected(item) ? 'unselect' : 'select';
9495 _t[m](item, true, false);
9498 if(this.multiSelect || this.singleSelect){
9499 if(this.multiSelect && e.shiftKey && this.lastSelection){
9500 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9502 this.select(item, this.multiSelect && e.ctrlKey);
9503 this.lastSelection = item;
9511 * Get the number of selected nodes.
9514 getSelectionCount : function(){
9515 return this.selections.length;
9519 * Get the currently selected nodes.
9520 * @return {Array} An array of HTMLElements
9522 getSelectedNodes : function(){
9523 return this.selections;
9527 * Get the indexes of the selected nodes.
9530 getSelectedIndexes : function(){
9531 var indexes = [], s = this.selections;
9532 for(var i = 0, len = s.length; i < len; i++){
9533 indexes.push(s[i].nodeIndex);
9539 * Clear all selections
9540 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9542 clearSelections : function(suppressEvent){
9543 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9544 this.cmp.elements = this.selections;
9545 this.cmp.removeClass(this.selectedClass);
9546 this.selections = [];
9548 this.fireEvent("selectionchange", this, this.selections);
9554 * Returns true if the passed node is selected
9555 * @param {HTMLElement/Number} node The node or node index
9558 isSelected : function(node){
9559 var s = this.selections;
9563 node = this.getNode(node);
9564 return s.indexOf(node) !== -1;
9569 * @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
9570 * @param {Boolean} keepExisting (optional) true to keep existing selections
9571 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9573 select : function(nodeInfo, keepExisting, suppressEvent){
9574 if(nodeInfo instanceof Array){
9576 this.clearSelections(true);
9578 for(var i = 0, len = nodeInfo.length; i < len; i++){
9579 this.select(nodeInfo[i], true, true);
9583 var node = this.getNode(nodeInfo);
9584 if(!node || this.isSelected(node)){
9585 return; // already selected.
9588 this.clearSelections(true);
9590 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9591 Roo.fly(node).addClass(this.selectedClass);
9592 this.selections.push(node);
9594 this.fireEvent("selectionchange", this, this.selections);
9602 * @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
9603 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9604 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9606 unselect : function(nodeInfo, keepExisting, suppressEvent)
9608 if(nodeInfo instanceof Array){
9609 Roo.each(this.selections, function(s) {
9610 this.unselect(s, nodeInfo);
9614 var node = this.getNode(nodeInfo);
9615 if(!node || !this.isSelected(node)){
9616 Roo.log("not selected");
9617 return; // not selected.
9621 Roo.each(this.selections, function(s) {
9623 Roo.fly(node).removeClass(this.selectedClass);
9630 this.selections= ns;
9631 this.fireEvent("selectionchange", this, this.selections);
9635 * Gets a template node.
9636 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9637 * @return {HTMLElement} The node or null if it wasn't found
9639 getNode : function(nodeInfo){
9640 if(typeof nodeInfo == "string"){
9641 return document.getElementById(nodeInfo);
9642 }else if(typeof nodeInfo == "number"){
9643 return this.nodes[nodeInfo];
9649 * Gets a range template nodes.
9650 * @param {Number} startIndex
9651 * @param {Number} endIndex
9652 * @return {Array} An array of nodes
9654 getNodes : function(start, end){
9655 var ns = this.nodes;
9657 end = typeof end == "undefined" ? ns.length - 1 : end;
9660 for(var i = start; i <= end; i++){
9664 for(var i = start; i >= end; i--){
9672 * Finds the index of the passed node
9673 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9674 * @return {Number} The index of the node or -1
9676 indexOf : function(node){
9677 node = this.getNode(node);
9678 if(typeof node.nodeIndex == "number"){
9679 return node.nodeIndex;
9681 var ns = this.nodes;
9682 for(var i = 0, len = ns.length; i < len; i++){
9693 * based on jquery fullcalendar
9697 Roo.bootstrap = Roo.bootstrap || {};
9699 * @class Roo.bootstrap.Calendar
9700 * @extends Roo.bootstrap.Component
9701 * Bootstrap Calendar class
9702 * @cfg {Boolean} loadMask (true|false) default false
9705 * Create a new Container
9706 * @param {Object} config The config object
9711 Roo.bootstrap.Calendar = function(config){
9712 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9716 * Fires when a date is selected
9717 * @param {DatePicker} this
9718 * @param {Date} date The selected date
9722 * @event monthchange
9723 * Fires when the displayed month changes
9724 * @param {DatePicker} this
9725 * @param {Date} date The selected month
9727 'monthchange': true,
9730 * Fires when mouse over an event
9731 * @param {Calendar} this
9732 * @param {event} Event
9737 * Fires when the mouse leaves an
9738 * @param {Calendar} this
9744 * Fires when the mouse click an
9745 * @param {Calendar} this
9754 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9757 * @cfg {Number} startDay
9758 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9764 getAutoCreate : function(){
9767 var fc_button = function(name, corner, style, content ) {
9768 return Roo.apply({},{
9770 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9772 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9775 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9783 style : 'width:100%',
9790 cls : 'fc-header-left',
9792 fc_button('prev', 'left', 'arrow', '‹' ),
9793 fc_button('next', 'right', 'arrow', '›' ),
9794 { tag: 'span', cls: 'fc-header-space' },
9795 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9803 cls : 'fc-header-center',
9807 cls: 'fc-header-title',
9810 html : 'month / year'
9818 cls : 'fc-header-right',
9820 /* fc_button('month', 'left', '', 'month' ),
9821 fc_button('week', '', '', 'week' ),
9822 fc_button('day', 'right', '', 'day' )
9834 var cal_heads = function() {
9836 // fixme - handle this.
9838 for (var i =0; i < Date.dayNames.length; i++) {
9839 var d = Date.dayNames[i];
9842 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9843 html : d.substring(0,3)
9847 ret[0].cls += ' fc-first';
9848 ret[6].cls += ' fc-last';
9851 var cal_cell = function(n) {
9854 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9859 cls: 'fc-day-number',
9863 cls: 'fc-day-content',
9867 style: 'position: relative;' // height: 17px;
9879 var cal_rows = function() {
9882 for (var r = 0; r < 6; r++) {
9889 for (var i =0; i < Date.dayNames.length; i++) {
9890 var d = Date.dayNames[i];
9891 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9894 row.cn[0].cls+=' fc-first';
9895 row.cn[0].cn[0].style = 'min-height:90px';
9896 row.cn[6].cls+=' fc-last';
9900 ret[0].cls += ' fc-first';
9901 ret[4].cls += ' fc-prev-last';
9902 ret[5].cls += ' fc-last';
9909 cls: 'fc-border-separate',
9910 style : 'width:100%',
9918 cls : 'fc-first fc-last',
9937 style : "position: relative;",
9940 cls : 'fc-view fc-view-month fc-grid',
9941 style : 'position: relative',
9942 unselectable : 'on',
9945 cls : 'fc-event-container',
9946 style : 'position:absolute;z-index:8;top:0;left:0;'
9964 initEvents : function()
9967 throw "can not find store for calendar";
9973 style: "text-align:center",
9977 style: "background-color:white;width:50%;margin:250 auto",
9981 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9992 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9994 var size = this.el.select('.fc-content', true).first().getSize();
9995 this.maskEl.setSize(size.width, size.height);
9996 this.maskEl.enableDisplayMode("block");
10001 this.store = Roo.factory(this.store, Roo.data);
10002 this.store.on('load', this.onLoad, this);
10003 this.store.on('beforeload', this.onBeforeLoad, this);
10007 this.cells = this.el.select('.fc-day',true);
10008 //Roo.log(this.cells);
10009 this.textNodes = this.el.query('.fc-day-number');
10010 this.cells.addClassOnOver('fc-state-hover');
10012 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10013 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10014 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10015 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10017 this.on('monthchange', this.onMonthChange, this);
10019 this.update(new Date().clearTime());
10022 resize : function() {
10023 var sz = this.el.getSize();
10025 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10026 this.el.select('.fc-day-content div',true).setHeight(34);
10031 showPrevMonth : function(e){
10032 this.update(this.activeDate.add("mo", -1));
10034 showToday : function(e){
10035 this.update(new Date().clearTime());
10038 showNextMonth : function(e){
10039 this.update(this.activeDate.add("mo", 1));
10043 showPrevYear : function(){
10044 this.update(this.activeDate.add("y", -1));
10048 showNextYear : function(){
10049 this.update(this.activeDate.add("y", 1));
10054 update : function(date)
10056 var vd = this.activeDate;
10057 this.activeDate = date;
10058 // if(vd && this.el){
10059 // var t = date.getTime();
10060 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10061 // Roo.log('using add remove');
10063 // this.fireEvent('monthchange', this, date);
10065 // this.cells.removeClass("fc-state-highlight");
10066 // this.cells.each(function(c){
10067 // if(c.dateValue == t){
10068 // c.addClass("fc-state-highlight");
10069 // setTimeout(function(){
10070 // try{c.dom.firstChild.focus();}catch(e){}
10080 var days = date.getDaysInMonth();
10082 var firstOfMonth = date.getFirstDateOfMonth();
10083 var startingPos = firstOfMonth.getDay()-this.startDay;
10085 if(startingPos < this.startDay){
10089 var pm = date.add(Date.MONTH, -1);
10090 var prevStart = pm.getDaysInMonth()-startingPos;
10092 this.cells = this.el.select('.fc-day',true);
10093 this.textNodes = this.el.query('.fc-day-number');
10094 this.cells.addClassOnOver('fc-state-hover');
10096 var cells = this.cells.elements;
10097 var textEls = this.textNodes;
10099 Roo.each(cells, function(cell){
10100 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10103 days += startingPos;
10105 // convert everything to numbers so it's fast
10106 var day = 86400000;
10107 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10110 //Roo.log(prevStart);
10112 var today = new Date().clearTime().getTime();
10113 var sel = date.clearTime().getTime();
10114 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10115 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10116 var ddMatch = this.disabledDatesRE;
10117 var ddText = this.disabledDatesText;
10118 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10119 var ddaysText = this.disabledDaysText;
10120 var format = this.format;
10122 var setCellClass = function(cal, cell){
10124 //Roo.log('set Cell Class');
10126 var t = d.getTime();
10130 cell.dateValue = t;
10132 cell.className += " fc-today";
10133 cell.className += " fc-state-highlight";
10134 cell.title = cal.todayText;
10137 // disable highlight in other month..
10138 //cell.className += " fc-state-highlight";
10143 cell.className = " fc-state-disabled";
10144 cell.title = cal.minText;
10148 cell.className = " fc-state-disabled";
10149 cell.title = cal.maxText;
10153 if(ddays.indexOf(d.getDay()) != -1){
10154 cell.title = ddaysText;
10155 cell.className = " fc-state-disabled";
10158 if(ddMatch && format){
10159 var fvalue = d.dateFormat(format);
10160 if(ddMatch.test(fvalue)){
10161 cell.title = ddText.replace("%0", fvalue);
10162 cell.className = " fc-state-disabled";
10166 if (!cell.initialClassName) {
10167 cell.initialClassName = cell.dom.className;
10170 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10175 for(; i < startingPos; i++) {
10176 textEls[i].innerHTML = (++prevStart);
10177 d.setDate(d.getDate()+1);
10179 cells[i].className = "fc-past fc-other-month";
10180 setCellClass(this, cells[i]);
10185 for(; i < days; i++){
10186 intDay = i - startingPos + 1;
10187 textEls[i].innerHTML = (intDay);
10188 d.setDate(d.getDate()+1);
10190 cells[i].className = ''; // "x-date-active";
10191 setCellClass(this, cells[i]);
10195 for(; i < 42; i++) {
10196 textEls[i].innerHTML = (++extraDays);
10197 d.setDate(d.getDate()+1);
10199 cells[i].className = "fc-future fc-other-month";
10200 setCellClass(this, cells[i]);
10203 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10205 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10207 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10208 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10210 if(totalRows != 6){
10211 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10212 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10215 this.fireEvent('monthchange', this, date);
10219 if(!this.internalRender){
10220 var main = this.el.dom.firstChild;
10221 var w = main.offsetWidth;
10222 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10223 Roo.fly(main).setWidth(w);
10224 this.internalRender = true;
10225 // opera does not respect the auto grow header center column
10226 // then, after it gets a width opera refuses to recalculate
10227 // without a second pass
10228 if(Roo.isOpera && !this.secondPass){
10229 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10230 this.secondPass = true;
10231 this.update.defer(10, this, [date]);
10238 findCell : function(dt) {
10239 dt = dt.clearTime().getTime();
10241 this.cells.each(function(c){
10242 //Roo.log("check " +c.dateValue + '?=' + dt);
10243 if(c.dateValue == dt){
10253 findCells : function(ev) {
10254 var s = ev.start.clone().clearTime().getTime();
10256 var e= ev.end.clone().clearTime().getTime();
10259 this.cells.each(function(c){
10260 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10262 if(c.dateValue > e){
10265 if(c.dateValue < s){
10274 findBestRow: function(cells)
10278 for (var i =0 ; i < cells.length;i++) {
10279 ret = Math.max(cells[i].rows || 0,ret);
10286 addItem : function(ev)
10288 // look for vertical location slot in
10289 var cells = this.findCells(ev);
10291 ev.row = this.findBestRow(cells);
10293 // work out the location.
10297 for(var i =0; i < cells.length; i++) {
10305 if (crow.start.getY() == cells[i].getY()) {
10307 crow.end = cells[i];
10323 for (var i = 0; i < cells.length;i++) {
10324 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10328 this.calevents.push(ev);
10331 clearEvents: function() {
10333 if(!this.calevents){
10337 Roo.each(this.cells.elements, function(c){
10341 Roo.each(this.calevents, function(e) {
10342 Roo.each(e.els, function(el) {
10343 el.un('mouseenter' ,this.onEventEnter, this);
10344 el.un('mouseleave' ,this.onEventLeave, this);
10351 renderEvents: function()
10353 // first make sure there is enough space..
10355 this.cells.each(function(c) {
10357 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10360 for (var e = 0; e < this.calevents.length; e++) {
10361 var ev = this.calevents[e];
10362 var cells = ev.cells;
10363 var rows = ev.rows;
10365 for(var i =0; i < rows.length; i++) {
10368 // how many rows should it span..
10371 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10372 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10374 unselectable : "on",
10377 cls: 'fc-event-inner',
10381 // cls: 'fc-event-time',
10382 // html : cells.length > 1 ? '' : ev.time
10386 cls: 'fc-event-title',
10387 html : String.format('{0}', ev.title)
10394 cls: 'ui-resizable-handle ui-resizable-e',
10395 html : '  '
10401 cfg.cls += ' fc-event-start';
10403 if ((i+1) == rows.length) {
10404 cfg.cls += ' fc-event-end';
10407 var ctr = this.el.select('.fc-event-container',true).first();
10408 var cg = ctr.createChild(cfg);
10410 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10411 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10412 cg.on('click', this.onEventClick, this, ev);
10416 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10417 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10419 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10420 cg.setWidth(ebox.right - sbox.x -2);
10428 onEventEnter: function (e, el,event,d) {
10429 this.fireEvent('evententer', this, el, event);
10432 onEventLeave: function (e, el,event,d) {
10433 this.fireEvent('eventleave', this, el, event);
10436 onEventClick: function (e, el,event,d) {
10437 this.fireEvent('eventclick', this, el, event);
10440 onMonthChange: function () {
10444 onLoad: function ()
10446 this.calevents = [];
10449 if(this.store.getCount() > 0){
10450 this.store.data.each(function(d){
10453 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10454 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10455 time : d.data.start_time,
10456 title : d.data.title,
10457 description : d.data.description,
10458 venue : d.data.venue
10463 this.renderEvents();
10466 this.maskEl.hide();
10470 onBeforeLoad: function()
10472 this.clearEvents();
10475 this.maskEl.show();
10489 * @class Roo.bootstrap.Popover
10490 * @extends Roo.bootstrap.Component
10491 * Bootstrap Popover class
10492 * @cfg {String} html contents of the popover (or false to use children..)
10493 * @cfg {String} title of popover (or false to hide)
10494 * @cfg {String} placement how it is placed
10495 * @cfg {String} trigger click || hover (or false to trigger manually)
10496 * @cfg {String} over what (parent or false to trigger manually.)
10499 * Create a new Popover
10500 * @param {Object} config The config object
10503 Roo.bootstrap.Popover = function(config){
10504 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10507 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10509 title: 'Fill in a title',
10512 placement : 'right',
10513 trigger : 'hover', // hover
10517 can_build_overlaid : false,
10519 getChildContainer : function()
10521 return this.el.select('.popover-content',true).first();
10524 getAutoCreate : function(){
10525 Roo.log('make popover?');
10527 cls : 'popover roo-dynamic',
10528 style: 'display:block',
10534 cls : 'popover-inner',
10538 cls: 'popover-title',
10542 cls : 'popover-content',
10553 setTitle: function(str)
10555 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10557 setContent: function(str)
10559 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10561 // as it get's added to the bottom of the page.
10562 onRender : function(ct, position)
10564 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10566 var cfg = Roo.apply({}, this.getAutoCreate());
10570 cfg.cls += ' ' + this.cls;
10573 cfg.style = this.style;
10575 Roo.log("adding to ")
10576 this.el = Roo.get(document.body).createChild(cfg, position);
10582 initEvents : function()
10584 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10585 this.el.enableDisplayMode('block');
10587 if (this.over === false) {
10590 if (this.triggers === false) {
10593 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10594 var triggers = this.trigger ? this.trigger.split(' ') : [];
10595 Roo.each(triggers, function(trigger) {
10597 if (trigger == 'click') {
10598 on_el.on('click', this.toggle, this);
10599 } else if (trigger != 'manual') {
10600 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10601 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10603 on_el.on(eventIn ,this.enter, this);
10604 on_el.on(eventOut, this.leave, this);
10615 toggle : function () {
10616 this.hoverState == 'in' ? this.leave() : this.enter();
10619 enter : function () {
10622 clearTimeout(this.timeout);
10624 this.hoverState = 'in'
10626 if (!this.delay || !this.delay.show) {
10631 this.timeout = setTimeout(function () {
10632 if (_t.hoverState == 'in') {
10635 }, this.delay.show)
10637 leave : function() {
10638 clearTimeout(this.timeout);
10640 this.hoverState = 'out'
10642 if (!this.delay || !this.delay.hide) {
10647 this.timeout = setTimeout(function () {
10648 if (_t.hoverState == 'out') {
10651 }, this.delay.hide)
10654 show : function (on_el)
10657 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10660 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10661 if (this.html !== false) {
10662 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10664 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10665 if (!this.title.length) {
10666 this.el.select('.popover-title',true).hide();
10669 var placement = typeof this.placement == 'function' ?
10670 this.placement.call(this, this.el, on_el) :
10673 var autoToken = /\s?auto?\s?/i;
10674 var autoPlace = autoToken.test(placement);
10676 placement = placement.replace(autoToken, '') || 'top';
10680 //this.el.setXY([0,0]);
10682 this.el.dom.style.display='block';
10683 this.el.addClass(placement);
10685 //this.el.appendTo(on_el);
10687 var p = this.getPosition();
10688 var box = this.el.getBox();
10693 var align = Roo.bootstrap.Popover.alignment[placement]
10694 this.el.alignTo(on_el, align[0],align[1]);
10695 //var arrow = this.el.select('.arrow',true).first();
10696 //arrow.set(align[2],
10698 this.el.addClass('in');
10699 this.hoverState = null;
10701 if (this.el.hasClass('fade')) {
10708 this.el.setXY([0,0]);
10709 this.el.removeClass('in');
10716 Roo.bootstrap.Popover.alignment = {
10717 'left' : ['r-l', [-10,0], 'right'],
10718 'right' : ['l-r', [10,0], 'left'],
10719 'bottom' : ['t-b', [0,10], 'top'],
10720 'top' : [ 'b-t', [0,-10], 'bottom']
10731 * @class Roo.bootstrap.Progress
10732 * @extends Roo.bootstrap.Component
10733 * Bootstrap Progress class
10734 * @cfg {Boolean} striped striped of the progress bar
10735 * @cfg {Boolean} active animated of the progress bar
10739 * Create a new Progress
10740 * @param {Object} config The config object
10743 Roo.bootstrap.Progress = function(config){
10744 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10747 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10752 getAutoCreate : function(){
10760 cfg.cls += ' progress-striped';
10764 cfg.cls += ' active';
10783 * @class Roo.bootstrap.ProgressBar
10784 * @extends Roo.bootstrap.Component
10785 * Bootstrap ProgressBar class
10786 * @cfg {Number} aria_valuenow aria-value now
10787 * @cfg {Number} aria_valuemin aria-value min
10788 * @cfg {Number} aria_valuemax aria-value max
10789 * @cfg {String} label label for the progress bar
10790 * @cfg {String} panel (success | info | warning | danger )
10791 * @cfg {String} role role of the progress bar
10792 * @cfg {String} sr_only text
10796 * Create a new ProgressBar
10797 * @param {Object} config The config object
10800 Roo.bootstrap.ProgressBar = function(config){
10801 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10804 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10808 aria_valuemax : 100,
10814 getAutoCreate : function()
10819 cls: 'progress-bar',
10820 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10832 cfg.role = this.role;
10835 if(this.aria_valuenow){
10836 cfg['aria-valuenow'] = this.aria_valuenow;
10839 if(this.aria_valuemin){
10840 cfg['aria-valuemin'] = this.aria_valuemin;
10843 if(this.aria_valuemax){
10844 cfg['aria-valuemax'] = this.aria_valuemax;
10847 if(this.label && !this.sr_only){
10848 cfg.html = this.label;
10852 cfg.cls += ' progress-bar-' + this.panel;
10858 update : function(aria_valuenow)
10860 this.aria_valuenow = aria_valuenow;
10862 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10877 * @class Roo.bootstrap.TabPanel
10878 * @extends Roo.bootstrap.Component
10879 * Bootstrap TabPanel class
10880 * @cfg {Boolean} active panel active
10881 * @cfg {String} html panel content
10882 * @cfg {String} tabId tab relate id
10886 * Create a new TabPanel
10887 * @param {Object} config The config object
10890 Roo.bootstrap.TabPanel = function(config){
10891 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10894 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10900 getAutoCreate : function(){
10904 html: this.html || ''
10908 cfg.cls += ' active';
10912 cfg.tabId = this.tabId;
10930 * @class Roo.bootstrap.DateField
10931 * @extends Roo.bootstrap.Input
10932 * Bootstrap DateField class
10933 * @cfg {Number} weekStart default 0
10934 * @cfg {Number} weekStart default 0
10935 * @cfg {Number} viewMode default empty, (months|years)
10936 * @cfg {Number} minViewMode default empty, (months|years)
10937 * @cfg {Number} startDate default -Infinity
10938 * @cfg {Number} endDate default Infinity
10939 * @cfg {Boolean} todayHighlight default false
10940 * @cfg {Boolean} todayBtn default false
10941 * @cfg {Boolean} calendarWeeks default false
10942 * @cfg {Object} daysOfWeekDisabled default empty
10944 * @cfg {Boolean} keyboardNavigation default true
10945 * @cfg {String} language default en
10948 * Create a new DateField
10949 * @param {Object} config The config object
10952 Roo.bootstrap.DateField = function(config){
10953 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10957 * Fires when this field show.
10958 * @param {Roo.bootstrap.DateField} this
10959 * @param {Mixed} date The date value
10964 * Fires when this field hide.
10965 * @param {Roo.bootstrap.DateField} this
10966 * @param {Mixed} date The date value
10971 * Fires when select a date.
10972 * @param {Roo.bootstrap.DateField} this
10973 * @param {Mixed} date The date value
10979 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
10982 * @cfg {String} format
10983 * The default date format string which can be overriden for localization support. The format must be
10984 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10988 * @cfg {String} altFormats
10989 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10990 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10992 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11000 todayHighlight : false,
11006 keyboardNavigation: true,
11008 calendarWeeks: false,
11010 startDate: -Infinity,
11014 daysOfWeekDisabled: [],
11018 UTCDate: function()
11020 return new Date(Date.UTC.apply(Date, arguments));
11023 UTCToday: function()
11025 var today = new Date();
11026 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11029 getDate: function() {
11030 var d = this.getUTCDate();
11031 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11034 getUTCDate: function() {
11038 setDate: function(d) {
11039 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11042 setUTCDate: function(d) {
11044 this.setValue(this.formatDate(this.date));
11047 onRender: function(ct, position)
11050 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11052 this.language = this.language || 'en';
11053 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11054 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11056 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11057 this.format = this.format || 'm/d/y';
11058 this.isInline = false;
11059 this.isInput = true;
11060 this.component = this.el.select('.add-on', true).first() || false;
11061 this.component = (this.component && this.component.length === 0) ? false : this.component;
11062 this.hasInput = this.component && this.inputEL().length;
11064 if (typeof(this.minViewMode === 'string')) {
11065 switch (this.minViewMode) {
11067 this.minViewMode = 1;
11070 this.minViewMode = 2;
11073 this.minViewMode = 0;
11078 if (typeof(this.viewMode === 'string')) {
11079 switch (this.viewMode) {
11092 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11094 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11096 this.picker().on('mousedown', this.onMousedown, this);
11097 this.picker().on('click', this.onClick, this);
11099 this.picker().addClass('datepicker-dropdown');
11101 this.startViewMode = this.viewMode;
11104 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11105 if(!this.calendarWeeks){
11110 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11111 v.attr('colspan', function(i, val){
11112 return parseInt(val) + 1;
11117 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11119 this.setStartDate(this.startDate);
11120 this.setEndDate(this.endDate);
11122 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11129 if(this.isInline) {
11134 picker : function()
11136 return this.el.select('.datepicker', true).first();
11139 fillDow: function()
11141 var dowCnt = this.weekStart;
11150 if(this.calendarWeeks){
11158 while (dowCnt < this.weekStart + 7) {
11162 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11166 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11169 fillMonths: function()
11172 var months = this.picker().select('>.datepicker-months td', true).first();
11174 months.dom.innerHTML = '';
11180 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11183 months.createChild(month);
11188 update: function(){
11190 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11192 if (this.date < this.startDate) {
11193 this.viewDate = new Date(this.startDate);
11194 } else if (this.date > this.endDate) {
11195 this.viewDate = new Date(this.endDate);
11197 this.viewDate = new Date(this.date);
11204 var d = new Date(this.viewDate),
11205 year = d.getUTCFullYear(),
11206 month = d.getUTCMonth(),
11207 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11208 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11209 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11210 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11211 currentDate = this.date && this.date.valueOf(),
11212 today = this.UTCToday();
11214 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11216 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11218 // this.picker.select('>tfoot th.today').
11219 // .text(dates[this.language].today)
11220 // .toggle(this.todayBtn !== false);
11222 this.updateNavArrows();
11225 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11227 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11229 prevMonth.setUTCDate(day);
11231 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11233 var nextMonth = new Date(prevMonth);
11235 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11237 nextMonth = nextMonth.valueOf();
11239 var fillMonths = false;
11241 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11243 while(prevMonth.valueOf() < nextMonth) {
11246 if (prevMonth.getUTCDay() === this.weekStart) {
11248 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11256 if(this.calendarWeeks){
11257 // ISO 8601: First week contains first thursday.
11258 // ISO also states week starts on Monday, but we can be more abstract here.
11260 // Start of current week: based on weekstart/current date
11261 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11262 // Thursday of this week
11263 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11264 // First Thursday of year, year from thursday
11265 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11266 // Calendar week: ms between thursdays, div ms per day, div 7 days
11267 calWeek = (th - yth) / 864e5 / 7 + 1;
11269 fillMonths.cn.push({
11277 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11279 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11282 if (this.todayHighlight &&
11283 prevMonth.getUTCFullYear() == today.getFullYear() &&
11284 prevMonth.getUTCMonth() == today.getMonth() &&
11285 prevMonth.getUTCDate() == today.getDate()) {
11286 clsName += ' today';
11289 if (currentDate && prevMonth.valueOf() === currentDate) {
11290 clsName += ' active';
11293 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11294 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11295 clsName += ' disabled';
11298 fillMonths.cn.push({
11300 cls: 'day ' + clsName,
11301 html: prevMonth.getDate()
11304 prevMonth.setDate(prevMonth.getDate()+1);
11307 var currentYear = this.date && this.date.getUTCFullYear();
11308 var currentMonth = this.date && this.date.getUTCMonth();
11310 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11312 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11313 v.removeClass('active');
11315 if(currentYear === year && k === currentMonth){
11316 v.addClass('active');
11319 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11320 v.addClass('disabled');
11326 year = parseInt(year/10, 10) * 10;
11328 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11330 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11333 for (var i = -1; i < 11; i++) {
11334 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11336 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11344 showMode: function(dir) {
11346 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11348 Roo.each(this.picker().select('>div',true).elements, function(v){
11349 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11352 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11357 if(this.isInline) return;
11359 this.picker().removeClass(['bottom', 'top']);
11361 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11363 * place to the top of element!
11367 this.picker().addClass('top');
11368 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11373 this.picker().addClass('bottom');
11375 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11378 parseDate : function(value){
11379 if(!value || value instanceof Date){
11382 var v = Date.parseDate(value, this.format);
11383 if (!v && this.useIso) {
11384 v = Date.parseDate(value, 'Y-m-d');
11386 if(!v && this.altFormats){
11387 if(!this.altFormatsArray){
11388 this.altFormatsArray = this.altFormats.split("|");
11390 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11391 v = Date.parseDate(value, this.altFormatsArray[i]);
11397 formatDate : function(date, fmt){
11398 return (!date || !(date instanceof Date)) ?
11399 date : date.dateFormat(fmt || this.format);
11402 onFocus : function()
11404 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11408 onBlur : function()
11410 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11416 this.picker().show();
11420 this.fireEvent('show', this, this.date);
11425 if(this.isInline) return;
11426 this.picker().hide();
11427 this.viewMode = this.startViewMode;
11430 this.fireEvent('hide', this, this.date);
11434 onMousedown: function(e){
11435 e.stopPropagation();
11436 e.preventDefault();
11439 keyup: function(e){
11440 Roo.bootstrap.DateField.superclass.keyup.call(this);
11445 setValue: function(v){
11446 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11448 this.fireEvent('select', this, this.date);
11452 fireKey: function(e){
11453 if (!this.picker().isVisible()){
11454 if (e.keyCode == 27) // allow escape to hide and re-show picker
11458 var dateChanged = false,
11460 newDate, newViewDate;
11464 e.preventDefault();
11468 if (!this.keyboardNavigation) break;
11469 dir = e.keyCode == 37 ? -1 : 1;
11472 newDate = this.moveYear(this.date, dir);
11473 newViewDate = this.moveYear(this.viewDate, dir);
11474 } else if (e.shiftKey){
11475 newDate = this.moveMonth(this.date, dir);
11476 newViewDate = this.moveMonth(this.viewDate, dir);
11478 newDate = new Date(this.date);
11479 newDate.setUTCDate(this.date.getUTCDate() + dir);
11480 newViewDate = new Date(this.viewDate);
11481 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11483 if (this.dateWithinRange(newDate)){
11484 this.date = newDate;
11485 this.viewDate = newViewDate;
11486 this.setValue(this.formatDate(this.date));
11488 e.preventDefault();
11489 dateChanged = true;
11494 if (!this.keyboardNavigation) break;
11495 dir = e.keyCode == 38 ? -1 : 1;
11497 newDate = this.moveYear(this.date, dir);
11498 newViewDate = this.moveYear(this.viewDate, dir);
11499 } else if (e.shiftKey){
11500 newDate = this.moveMonth(this.date, dir);
11501 newViewDate = this.moveMonth(this.viewDate, dir);
11503 newDate = new Date(this.date);
11504 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11505 newViewDate = new Date(this.viewDate);
11506 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11508 if (this.dateWithinRange(newDate)){
11509 this.date = newDate;
11510 this.viewDate = newViewDate;
11511 this.setValue(this.formatDate(this.date));
11513 e.preventDefault();
11514 dateChanged = true;
11518 this.setValue(this.formatDate(this.date));
11520 e.preventDefault();
11523 this.setValue(this.formatDate(this.date));
11530 onClick: function(e) {
11531 e.stopPropagation();
11532 e.preventDefault();
11534 var target = e.getTarget();
11536 if(target.nodeName.toLowerCase() === 'i'){
11537 target = Roo.get(target).dom.parentNode;
11540 var nodeName = target.nodeName;
11541 var className = target.className;
11542 var html = target.innerHTML;
11544 switch(nodeName.toLowerCase()) {
11546 switch(className) {
11552 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11553 switch(this.viewMode){
11555 this.viewDate = this.moveMonth(this.viewDate, dir);
11559 this.viewDate = this.moveYear(this.viewDate, dir);
11565 var date = new Date();
11566 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11568 this.setValue(this.formatDate(this.date));
11574 if (className.indexOf('disabled') === -1) {
11575 this.viewDate.setUTCDate(1);
11576 if (className.indexOf('month') !== -1) {
11577 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11579 var year = parseInt(html, 10) || 0;
11580 this.viewDate.setUTCFullYear(year);
11589 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11590 var day = parseInt(html, 10) || 1;
11591 var year = this.viewDate.getUTCFullYear(),
11592 month = this.viewDate.getUTCMonth();
11594 if (className.indexOf('old') !== -1) {
11601 } else if (className.indexOf('new') !== -1) {
11609 this.date = this.UTCDate(year, month, day,0,0,0,0);
11610 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11612 this.setValue(this.formatDate(this.date));
11619 setStartDate: function(startDate){
11620 this.startDate = startDate || -Infinity;
11621 if (this.startDate !== -Infinity) {
11622 this.startDate = this.parseDate(this.startDate);
11625 this.updateNavArrows();
11628 setEndDate: function(endDate){
11629 this.endDate = endDate || Infinity;
11630 if (this.endDate !== Infinity) {
11631 this.endDate = this.parseDate(this.endDate);
11634 this.updateNavArrows();
11637 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11638 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11639 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11640 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11642 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11643 return parseInt(d, 10);
11646 this.updateNavArrows();
11649 updateNavArrows: function() {
11650 var d = new Date(this.viewDate),
11651 year = d.getUTCFullYear(),
11652 month = d.getUTCMonth();
11654 Roo.each(this.picker().select('.prev', true).elements, function(v){
11656 switch (this.viewMode) {
11659 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11665 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11672 Roo.each(this.picker().select('.next', true).elements, function(v){
11674 switch (this.viewMode) {
11677 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11683 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11691 moveMonth: function(date, dir){
11692 if (!dir) return date;
11693 var new_date = new Date(date.valueOf()),
11694 day = new_date.getUTCDate(),
11695 month = new_date.getUTCMonth(),
11696 mag = Math.abs(dir),
11698 dir = dir > 0 ? 1 : -1;
11701 // If going back one month, make sure month is not current month
11702 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11704 return new_date.getUTCMonth() == month;
11706 // If going forward one month, make sure month is as expected
11707 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11709 return new_date.getUTCMonth() != new_month;
11711 new_month = month + dir;
11712 new_date.setUTCMonth(new_month);
11713 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11714 if (new_month < 0 || new_month > 11)
11715 new_month = (new_month + 12) % 12;
11717 // For magnitudes >1, move one month at a time...
11718 for (var i=0; i<mag; i++)
11719 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11720 new_date = this.moveMonth(new_date, dir);
11721 // ...then reset the day, keeping it in the new month
11722 new_month = new_date.getUTCMonth();
11723 new_date.setUTCDate(day);
11725 return new_month != new_date.getUTCMonth();
11728 // Common date-resetting loop -- if date is beyond end of month, make it
11731 new_date.setUTCDate(--day);
11732 new_date.setUTCMonth(new_month);
11737 moveYear: function(date, dir){
11738 return this.moveMonth(date, dir*12);
11741 dateWithinRange: function(date){
11742 return date >= this.startDate && date <= this.endDate;
11746 remove: function() {
11747 this.picker().remove();
11752 Roo.apply(Roo.bootstrap.DateField, {
11763 html: '<i class="icon-arrow-left"/>'
11773 html: '<i class="icon-arrow-right"/>'
11815 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11816 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11817 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11818 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11819 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11832 navFnc: 'FullYear',
11837 navFnc: 'FullYear',
11842 Roo.apply(Roo.bootstrap.DateField, {
11846 cls: 'datepicker dropdown-menu',
11850 cls: 'datepicker-days',
11854 cls: 'table-condensed',
11856 Roo.bootstrap.DateField.head,
11860 Roo.bootstrap.DateField.footer
11867 cls: 'datepicker-months',
11871 cls: 'table-condensed',
11873 Roo.bootstrap.DateField.head,
11874 Roo.bootstrap.DateField.content,
11875 Roo.bootstrap.DateField.footer
11882 cls: 'datepicker-years',
11886 cls: 'table-condensed',
11888 Roo.bootstrap.DateField.head,
11889 Roo.bootstrap.DateField.content,
11890 Roo.bootstrap.DateField.footer
11909 * @class Roo.bootstrap.TimeField
11910 * @extends Roo.bootstrap.Input
11911 * Bootstrap DateField class
11915 * Create a new TimeField
11916 * @param {Object} config The config object
11919 Roo.bootstrap.TimeField = function(config){
11920 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11924 * Fires when this field show.
11925 * @param {Roo.bootstrap.DateField} this
11926 * @param {Mixed} date The date value
11931 * Fires when this field hide.
11932 * @param {Roo.bootstrap.DateField} this
11933 * @param {Mixed} date The date value
11938 * Fires when select a date.
11939 * @param {Roo.bootstrap.DateField} this
11940 * @param {Mixed} date The date value
11946 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
11949 * @cfg {String} format
11950 * The default time format string which can be overriden for localization support. The format must be
11951 * valid according to {@link Date#parseDate} (defaults to 'H:i').
11955 onRender: function(ct, position)
11958 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11960 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11962 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11964 this.pop = this.picker().select('>.datepicker-time',true).first();
11965 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
11967 this.picker().on('mousedown', this.onMousedown, this);
11968 this.picker().on('click', this.onClick, this);
11970 this.picker().addClass('datepicker-dropdown');
11975 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11976 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11977 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11978 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11979 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11980 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11984 fireKey: function(e){
11985 if (!this.picker().isVisible()){
11986 if (e.keyCode == 27) // allow escape to hide and re-show picker
11991 e.preventDefault();
11999 this.onTogglePeriod();
12002 this.onIncrementMinutes();
12005 this.onDecrementMinutes();
12014 onClick: function(e) {
12015 e.stopPropagation();
12016 e.preventDefault();
12019 picker : function()
12021 return this.el.select('.datepicker', true).first();
12024 fillTime: function()
12026 var time = this.pop.select('tbody', true).first();
12028 time.dom.innerHTML = '';
12043 cls: 'hours-up glyphicon glyphicon-chevron-up'
12063 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12084 cls: 'timepicker-hour',
12099 cls: 'timepicker-minute',
12114 cls: 'btn btn-primary period',
12136 cls: 'hours-down glyphicon glyphicon-chevron-down'
12156 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12174 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12181 var hours = this.time.getHours();
12182 var minutes = this.time.getMinutes();
12195 hours = hours - 12;
12199 hours = '0' + hours;
12203 minutes = '0' + minutes;
12206 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12207 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12208 this.pop.select('button', true).first().dom.innerHTML = period;
12214 this.picker().removeClass(['bottom', 'top']);
12216 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12218 * place to the top of element!
12222 this.picker().addClass('top');
12223 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12228 this.picker().addClass('bottom');
12230 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12233 onFocus : function()
12235 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12239 onBlur : function()
12241 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12247 this.picker().show();
12252 this.fireEvent('show', this, this.date);
12257 this.picker().hide();
12260 this.fireEvent('hide', this, this.date);
12263 setTime : function()
12266 this.setValue(this.time.format(this.format));
12268 this.fireEvent('select', this, this.date);
12273 onMousedown: function(e){
12274 e.stopPropagation();
12275 e.preventDefault();
12278 onIncrementHours: function()
12280 Roo.log('onIncrementHours');
12281 this.time = this.time.add(Date.HOUR, 1);
12286 onDecrementHours: function()
12288 Roo.log('onDecrementHours');
12289 this.time = this.time.add(Date.HOUR, -1);
12293 onIncrementMinutes: function()
12295 Roo.log('onIncrementMinutes');
12296 this.time = this.time.add(Date.MINUTE, 1);
12300 onDecrementMinutes: function()
12302 Roo.log('onDecrementMinutes');
12303 this.time = this.time.add(Date.MINUTE, -1);
12307 onTogglePeriod: function()
12309 Roo.log('onTogglePeriod');
12310 this.time = this.time.add(Date.HOUR, 12);
12317 Roo.apply(Roo.bootstrap.TimeField, {
12347 cls: 'btn btn-info ok',
12359 Roo.apply(Roo.bootstrap.TimeField, {
12363 cls: 'datepicker dropdown-menu',
12367 cls: 'datepicker-time',
12371 cls: 'table-condensed',
12373 Roo.bootstrap.TimeField.content,
12374 Roo.bootstrap.TimeField.footer
12393 * @class Roo.bootstrap.CheckBox
12394 * @extends Roo.bootstrap.Input
12395 * Bootstrap CheckBox class
12397 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12398 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12399 * @cfg {String} boxLabel The text that appears beside the checkbox
12400 * @cfg {Boolean} checked initnal the element
12403 * Create a new CheckBox
12404 * @param {Object} config The config object
12407 Roo.bootstrap.CheckBox = function(config){
12408 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12413 * Fires when the element is checked or unchecked.
12414 * @param {Roo.bootstrap.CheckBox} this This input
12415 * @param {Boolean} checked The new checked value
12421 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12423 inputType: 'checkbox',
12429 getAutoCreate : function()
12431 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12437 cfg.cls = 'form-group' //input-group
12442 type : this.inputType,
12443 value : (!this.checked) ? this.valueOff : this.inputValue,
12445 placeholder : this.placeholder || ''
12449 if (this.disabled) {
12450 input.disabled=true;
12454 input.checked = this.checked;
12458 input.name = this.name;
12462 input.cls += ' input-' + this.size;
12466 ['xs','sm','md','lg'].map(function(size){
12467 if (settings[size]) {
12468 cfg.cls += ' col-' + size + '-' + settings[size];
12472 var inputblock = input;
12474 if (this.before || this.after) {
12477 cls : 'input-group',
12481 inputblock.cn.push({
12483 cls : 'input-group-addon',
12487 inputblock.cn.push(input);
12489 inputblock.cn.push({
12491 cls : 'input-group-addon',
12498 if (align ==='left' && this.fieldLabel.length) {
12499 Roo.log("left and has label");
12505 cls : 'control-label col-md-' + this.labelWidth,
12506 html : this.fieldLabel
12510 cls : "col-md-" + (12 - this.labelWidth),
12517 } else if ( this.fieldLabel.length) {
12522 tag: this.boxLabel ? 'span' : 'label',
12524 cls: 'control-label box-input-label',
12525 //cls : 'input-group-addon',
12526 html : this.fieldLabel
12536 Roo.log(" no label && no align");
12551 html: this.boxLabel
12560 * return the real input element.
12562 inputEl: function ()
12564 return this.el.select('input.form-box',true).first();
12567 initEvents : function()
12569 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12571 this.inputEl().on('click', this.onClick, this);
12575 onClick : function()
12577 this.setChecked(!this.checked);
12580 setChecked : function(state,suppressEvent)
12582 this.checked = state;
12584 this.inputEl().dom.checked = state;
12586 if(suppressEvent !== true){
12587 this.fireEvent('check', this, state);
12590 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12594 setValue : function(v,suppressEvent)
12596 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12610 * @class Roo.bootstrap.Radio
12611 * @extends Roo.bootstrap.CheckBox
12612 * Bootstrap Radio class
12615 * Create a new Radio
12616 * @param {Object} config The config object
12619 Roo.bootstrap.Radio = function(config){
12620 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12624 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12626 inputType: 'radio',
12630 getAutoCreate : function()
12632 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12638 cfg.cls = 'form-group' //input-group
12643 type : this.inputType,
12644 value : (!this.checked) ? this.valueOff : this.inputValue,
12646 placeholder : this.placeholder || ''
12650 if (this.disabled) {
12651 input.disabled=true;
12655 input.checked = this.checked;
12659 input.name = this.name;
12663 input.cls += ' input-' + this.size;
12667 ['xs','sm','md','lg'].map(function(size){
12668 if (settings[size]) {
12669 cfg.cls += ' col-' + size + '-' + settings[size];
12673 var inputblock = input;
12675 if (this.before || this.after) {
12678 cls : 'input-group',
12682 inputblock.cn.push({
12684 cls : 'input-group-addon',
12688 inputblock.cn.push(input);
12690 inputblock.cn.push({
12692 cls : 'input-group-addon',
12699 if (align ==='left' && this.fieldLabel.length) {
12700 Roo.log("left and has label");
12706 cls : 'control-label col-md-' + this.labelWidth,
12707 html : this.fieldLabel
12711 cls : "col-md-" + (12 - this.labelWidth),
12718 } else if ( this.fieldLabel.length) {
12725 cls: 'control-label box-input-label',
12726 //cls : 'input-group-addon',
12727 html : this.fieldLabel
12737 Roo.log(" no label && no align");
12752 html: this.boxLabel
12760 onClick : function()
12762 this.setChecked(true);
12765 setChecked : function(state,suppressEvent)
12768 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12769 v.dom.checked = false;
12773 this.checked = state;
12774 this.inputEl().dom.checked = state;
12776 if(suppressEvent !== true){
12777 this.fireEvent('check', this, state);
12780 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12784 getGroupValue : function()
12787 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12788 if(v.dom.checked == true){
12789 value = v.dom.value;
12797 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12798 * @return {Mixed} value The field value
12800 getValue : function(){
12801 return this.getGroupValue();
12807 //<script type="text/javascript">
12810 * Based Ext JS Library 1.1.1
12811 * Copyright(c) 2006-2007, Ext JS, LLC.
12817 * @class Roo.HtmlEditorCore
12818 * @extends Roo.Component
12819 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12821 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12824 Roo.HtmlEditorCore = function(config){
12827 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12830 * @event initialize
12831 * Fires when the editor is fully initialized (including the iframe)
12832 * @param {Roo.HtmlEditorCore} this
12837 * Fires when the editor is first receives the focus. Any insertion must wait
12838 * until after this event.
12839 * @param {Roo.HtmlEditorCore} this
12843 * @event beforesync
12844 * Fires before the textarea is updated with content from the editor iframe. Return false
12845 * to cancel the sync.
12846 * @param {Roo.HtmlEditorCore} this
12847 * @param {String} html
12851 * @event beforepush
12852 * Fires before the iframe editor is updated with content from the textarea. Return false
12853 * to cancel the push.
12854 * @param {Roo.HtmlEditorCore} this
12855 * @param {String} html
12860 * Fires when the textarea is updated with content from the editor iframe.
12861 * @param {Roo.HtmlEditorCore} this
12862 * @param {String} html
12867 * Fires when the iframe editor is updated with content from the textarea.
12868 * @param {Roo.HtmlEditorCore} this
12869 * @param {String} html
12874 * @event editorevent
12875 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12876 * @param {Roo.HtmlEditorCore} this
12884 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12888 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12894 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12899 * @cfg {Number} height (in pixels)
12903 * @cfg {Number} width (in pixels)
12908 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12911 stylesheets: false,
12916 // private properties
12917 validationEvent : false,
12919 initialized : false,
12921 sourceEditMode : false,
12922 onFocus : Roo.emptyFn,
12924 hideMode:'offsets',
12930 * Protected method that will not generally be called directly. It
12931 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12932 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12934 getDocMarkup : function(){
12937 Roo.log(this.stylesheets);
12939 // inherit styels from page...??
12940 if (this.stylesheets === false) {
12942 Roo.get(document.head).select('style').each(function(node) {
12943 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12946 Roo.get(document.head).select('link').each(function(node) {
12947 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12950 } else if (!this.stylesheets.length) {
12952 st = '<style type="text/css">' +
12953 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12956 Roo.each(this.stylesheets, function(s) {
12957 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12962 st += '<style type="text/css">' +
12963 'IMG { cursor: pointer } ' +
12967 return '<html><head>' + st +
12968 //<style type="text/css">' +
12969 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12971 ' </head><body class="roo-htmleditor-body"></body></html>';
12975 onRender : function(ct, position)
12978 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12979 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12982 this.el.dom.style.border = '0 none';
12983 this.el.dom.setAttribute('tabIndex', -1);
12984 this.el.addClass('x-hidden hide');
12988 if(Roo.isIE){ // fix IE 1px bogus margin
12989 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
12993 this.frameId = Roo.id();
12997 var iframe = this.owner.wrap.createChild({
12999 cls: 'form-control', // bootstrap..
13001 name: this.frameId,
13002 frameBorder : 'no',
13003 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13008 this.iframe = iframe.dom;
13010 this.assignDocWin();
13012 this.doc.designMode = 'on';
13015 this.doc.write(this.getDocMarkup());
13019 var task = { // must defer to wait for browser to be ready
13021 //console.log("run task?" + this.doc.readyState);
13022 this.assignDocWin();
13023 if(this.doc.body || this.doc.readyState == 'complete'){
13025 this.doc.designMode="on";
13029 Roo.TaskMgr.stop(task);
13030 this.initEditor.defer(10, this);
13037 Roo.TaskMgr.start(task);
13044 onResize : function(w, h)
13046 Roo.log('resize: ' +w + ',' + h );
13047 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13051 if(typeof w == 'number'){
13053 this.iframe.style.width = w + 'px';
13055 if(typeof h == 'number'){
13057 this.iframe.style.height = h + 'px';
13059 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13066 * Toggles the editor between standard and source edit mode.
13067 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13069 toggleSourceEdit : function(sourceEditMode){
13071 this.sourceEditMode = sourceEditMode === true;
13073 if(this.sourceEditMode){
13075 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13078 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13079 //this.iframe.className = '';
13082 //this.setSize(this.owner.wrap.getSize());
13083 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13090 * Protected method that will not generally be called directly. If you need/want
13091 * custom HTML cleanup, this is the method you should override.
13092 * @param {String} html The HTML to be cleaned
13093 * return {String} The cleaned HTML
13095 cleanHtml : function(html){
13096 html = String(html);
13097 if(html.length > 5){
13098 if(Roo.isSafari){ // strip safari nonsense
13099 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13102 if(html == ' '){
13109 * HTML Editor -> Textarea
13110 * Protected method that will not generally be called directly. Syncs the contents
13111 * of the editor iframe with the textarea.
13113 syncValue : function(){
13114 if(this.initialized){
13115 var bd = (this.doc.body || this.doc.documentElement);
13116 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13117 var html = bd.innerHTML;
13119 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13120 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13122 html = '<div style="'+m[0]+'">' + html + '</div>';
13125 html = this.cleanHtml(html);
13126 // fix up the special chars.. normaly like back quotes in word...
13127 // however we do not want to do this with chinese..
13128 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13129 var cc = b.charCodeAt();
13131 (cc >= 0x4E00 && cc < 0xA000 ) ||
13132 (cc >= 0x3400 && cc < 0x4E00 ) ||
13133 (cc >= 0xf900 && cc < 0xfb00 )
13139 if(this.owner.fireEvent('beforesync', this, html) !== false){
13140 this.el.dom.value = html;
13141 this.owner.fireEvent('sync', this, html);
13147 * Protected method that will not generally be called directly. Pushes the value of the textarea
13148 * into the iframe editor.
13150 pushValue : function(){
13151 if(this.initialized){
13152 var v = this.el.dom.value.trim();
13154 // if(v.length < 1){
13158 if(this.owner.fireEvent('beforepush', this, v) !== false){
13159 var d = (this.doc.body || this.doc.documentElement);
13161 this.cleanUpPaste();
13162 this.el.dom.value = d.innerHTML;
13163 this.owner.fireEvent('push', this, v);
13169 deferFocus : function(){
13170 this.focus.defer(10, this);
13174 focus : function(){
13175 if(this.win && !this.sourceEditMode){
13182 assignDocWin: function()
13184 var iframe = this.iframe;
13187 this.doc = iframe.contentWindow.document;
13188 this.win = iframe.contentWindow;
13190 if (!Roo.get(this.frameId)) {
13193 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13194 this.win = Roo.get(this.frameId).dom.contentWindow;
13199 initEditor : function(){
13200 //console.log("INIT EDITOR");
13201 this.assignDocWin();
13205 this.doc.designMode="on";
13207 this.doc.write(this.getDocMarkup());
13210 var dbody = (this.doc.body || this.doc.documentElement);
13211 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13212 // this copies styles from the containing element into thsi one..
13213 // not sure why we need all of this..
13214 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13215 ss['background-attachment'] = 'fixed'; // w3c
13216 dbody.bgProperties = 'fixed'; // ie
13217 Roo.DomHelper.applyStyles(dbody, ss);
13218 Roo.EventManager.on(this.doc, {
13219 //'mousedown': this.onEditorEvent,
13220 'mouseup': this.onEditorEvent,
13221 'dblclick': this.onEditorEvent,
13222 'click': this.onEditorEvent,
13223 'keyup': this.onEditorEvent,
13228 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13230 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13231 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13233 this.initialized = true;
13235 this.owner.fireEvent('initialize', this);
13240 onDestroy : function(){
13246 //for (var i =0; i < this.toolbars.length;i++) {
13247 // // fixme - ask toolbars for heights?
13248 // this.toolbars[i].onDestroy();
13251 //this.wrap.dom.innerHTML = '';
13252 //this.wrap.remove();
13257 onFirstFocus : function(){
13259 this.assignDocWin();
13262 this.activated = true;
13265 if(Roo.isGecko){ // prevent silly gecko errors
13267 var s = this.win.getSelection();
13268 if(!s.focusNode || s.focusNode.nodeType != 3){
13269 var r = s.getRangeAt(0);
13270 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13275 this.execCmd('useCSS', true);
13276 this.execCmd('styleWithCSS', false);
13279 this.owner.fireEvent('activate', this);
13283 adjustFont: function(btn){
13284 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13285 //if(Roo.isSafari){ // safari
13288 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13289 if(Roo.isSafari){ // safari
13290 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13291 v = (v < 10) ? 10 : v;
13292 v = (v > 48) ? 48 : v;
13293 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13298 v = Math.max(1, v+adjust);
13300 this.execCmd('FontSize', v );
13303 onEditorEvent : function(e){
13304 this.owner.fireEvent('editorevent', this, e);
13305 // this.updateToolbar();
13306 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13309 insertTag : function(tg)
13311 // could be a bit smarter... -> wrap the current selected tRoo..
13312 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13314 range = this.createRange(this.getSelection());
13315 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13316 wrappingNode.appendChild(range.extractContents());
13317 range.insertNode(wrappingNode);
13324 this.execCmd("formatblock", tg);
13328 insertText : function(txt)
13332 var range = this.createRange();
13333 range.deleteContents();
13334 //alert(Sender.getAttribute('label'));
13336 range.insertNode(this.doc.createTextNode(txt));
13342 * Executes a Midas editor command on the editor document and performs necessary focus and
13343 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13344 * @param {String} cmd The Midas command
13345 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13347 relayCmd : function(cmd, value){
13349 this.execCmd(cmd, value);
13350 this.owner.fireEvent('editorevent', this);
13351 //this.updateToolbar();
13352 this.owner.deferFocus();
13356 * Executes a Midas editor command directly on the editor document.
13357 * For visual commands, you should use {@link #relayCmd} instead.
13358 * <b>This should only be called after the editor is initialized.</b>
13359 * @param {String} cmd The Midas command
13360 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13362 execCmd : function(cmd, value){
13363 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13370 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13372 * @param {String} text | dom node..
13374 insertAtCursor : function(text)
13379 if(!this.activated){
13385 var r = this.doc.selection.createRange();
13396 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13400 // from jquery ui (MIT licenced)
13402 var win = this.win;
13404 if (win.getSelection && win.getSelection().getRangeAt) {
13405 range = win.getSelection().getRangeAt(0);
13406 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13407 range.insertNode(node);
13408 } else if (win.document.selection && win.document.selection.createRange) {
13409 // no firefox support
13410 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13411 win.document.selection.createRange().pasteHTML(txt);
13413 // no firefox support
13414 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13415 this.execCmd('InsertHTML', txt);
13424 mozKeyPress : function(e){
13426 var c = e.getCharCode(), cmd;
13429 c = String.fromCharCode(c).toLowerCase();
13443 this.cleanUpPaste.defer(100, this);
13451 e.preventDefault();
13459 fixKeys : function(){ // load time branching for fastest keydown performance
13461 return function(e){
13462 var k = e.getKey(), r;
13465 r = this.doc.selection.createRange();
13468 r.pasteHTML('    ');
13475 r = this.doc.selection.createRange();
13477 var target = r.parentElement();
13478 if(!target || target.tagName.toLowerCase() != 'li'){
13480 r.pasteHTML('<br />');
13486 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13487 this.cleanUpPaste.defer(100, this);
13493 }else if(Roo.isOpera){
13494 return function(e){
13495 var k = e.getKey();
13499 this.execCmd('InsertHTML','    ');
13502 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13503 this.cleanUpPaste.defer(100, this);
13508 }else if(Roo.isSafari){
13509 return function(e){
13510 var k = e.getKey();
13514 this.execCmd('InsertText','\t');
13518 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13519 this.cleanUpPaste.defer(100, this);
13527 getAllAncestors: function()
13529 var p = this.getSelectedNode();
13532 a.push(p); // push blank onto stack..
13533 p = this.getParentElement();
13537 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13541 a.push(this.doc.body);
13545 lastSelNode : false,
13548 getSelection : function()
13550 this.assignDocWin();
13551 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13554 getSelectedNode: function()
13556 // this may only work on Gecko!!!
13558 // should we cache this!!!!
13563 var range = this.createRange(this.getSelection()).cloneRange();
13566 var parent = range.parentElement();
13568 var testRange = range.duplicate();
13569 testRange.moveToElementText(parent);
13570 if (testRange.inRange(range)) {
13573 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13576 parent = parent.parentElement;
13581 // is ancestor a text element.
13582 var ac = range.commonAncestorContainer;
13583 if (ac.nodeType == 3) {
13584 ac = ac.parentNode;
13587 var ar = ac.childNodes;
13590 var other_nodes = [];
13591 var has_other_nodes = false;
13592 for (var i=0;i<ar.length;i++) {
13593 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13596 // fullly contained node.
13598 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13603 // probably selected..
13604 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13605 other_nodes.push(ar[i]);
13609 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13614 has_other_nodes = true;
13616 if (!nodes.length && other_nodes.length) {
13617 nodes= other_nodes;
13619 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13625 createRange: function(sel)
13627 // this has strange effects when using with
13628 // top toolbar - not sure if it's a great idea.
13629 //this.editor.contentWindow.focus();
13630 if (typeof sel != "undefined") {
13632 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13634 return this.doc.createRange();
13637 return this.doc.createRange();
13640 getParentElement: function()
13643 this.assignDocWin();
13644 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13646 var range = this.createRange(sel);
13649 var p = range.commonAncestorContainer;
13650 while (p.nodeType == 3) { // text node
13661 * Range intersection.. the hard stuff...
13665 * [ -- selected range --- ]
13669 * if end is before start or hits it. fail.
13670 * if start is after end or hits it fail.
13672 * if either hits (but other is outside. - then it's not
13678 // @see http://www.thismuchiknow.co.uk/?p=64.
13679 rangeIntersectsNode : function(range, node)
13681 var nodeRange = node.ownerDocument.createRange();
13683 nodeRange.selectNode(node);
13685 nodeRange.selectNodeContents(node);
13688 var rangeStartRange = range.cloneRange();
13689 rangeStartRange.collapse(true);
13691 var rangeEndRange = range.cloneRange();
13692 rangeEndRange.collapse(false);
13694 var nodeStartRange = nodeRange.cloneRange();
13695 nodeStartRange.collapse(true);
13697 var nodeEndRange = nodeRange.cloneRange();
13698 nodeEndRange.collapse(false);
13700 return rangeStartRange.compareBoundaryPoints(
13701 Range.START_TO_START, nodeEndRange) == -1 &&
13702 rangeEndRange.compareBoundaryPoints(
13703 Range.START_TO_START, nodeStartRange) == 1;
13707 rangeCompareNode : function(range, node)
13709 var nodeRange = node.ownerDocument.createRange();
13711 nodeRange.selectNode(node);
13713 nodeRange.selectNodeContents(node);
13717 range.collapse(true);
13719 nodeRange.collapse(true);
13721 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13722 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13724 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13726 var nodeIsBefore = ss == 1;
13727 var nodeIsAfter = ee == -1;
13729 if (nodeIsBefore && nodeIsAfter)
13731 if (!nodeIsBefore && nodeIsAfter)
13732 return 1; //right trailed.
13734 if (nodeIsBefore && !nodeIsAfter)
13735 return 2; // left trailed.
13740 // private? - in a new class?
13741 cleanUpPaste : function()
13743 // cleans up the whole document..
13744 Roo.log('cleanuppaste');
13745 this.cleanUpChildren(this.doc.body);
13746 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13747 if (clean != this.doc.body.innerHTML) {
13748 this.doc.body.innerHTML = clean;
13753 cleanWordChars : function(input) {// change the chars to hex code
13754 var he = Roo.HtmlEditorCore;
13756 var output = input;
13757 Roo.each(he.swapCodes, function(sw) {
13758 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13760 output = output.replace(swapper, sw[1]);
13767 cleanUpChildren : function (n)
13769 if (!n.childNodes.length) {
13772 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13773 this.cleanUpChild(n.childNodes[i]);
13780 cleanUpChild : function (node)
13783 //console.log(node);
13784 if (node.nodeName == "#text") {
13785 // clean up silly Windows -- stuff?
13788 if (node.nodeName == "#comment") {
13789 node.parentNode.removeChild(node);
13790 // clean up silly Windows -- stuff?
13794 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
13796 node.parentNode.removeChild(node);
13801 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13803 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13804 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13806 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13807 // remove_keep_children = true;
13810 if (remove_keep_children) {
13811 this.cleanUpChildren(node);
13812 // inserts everything just before this node...
13813 while (node.childNodes.length) {
13814 var cn = node.childNodes[0];
13815 node.removeChild(cn);
13816 node.parentNode.insertBefore(cn, node);
13818 node.parentNode.removeChild(node);
13822 if (!node.attributes || !node.attributes.length) {
13823 this.cleanUpChildren(node);
13827 function cleanAttr(n,v)
13830 if (v.match(/^\./) || v.match(/^\//)) {
13833 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13836 if (v.match(/^#/)) {
13839 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13840 node.removeAttribute(n);
13844 function cleanStyle(n,v)
13846 if (v.match(/expression/)) { //XSS?? should we even bother..
13847 node.removeAttribute(n);
13850 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13851 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13854 var parts = v.split(/;/);
13857 Roo.each(parts, function(p) {
13858 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13862 var l = p.split(':').shift().replace(/\s+/g,'');
13863 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13866 if ( cblack.indexOf(l) > -1) {
13867 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13868 //node.removeAttribute(n);
13872 // only allow 'c whitelisted system attributes'
13873 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13874 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13875 //node.removeAttribute(n);
13885 if (clean.length) {
13886 node.setAttribute(n, clean.join(';'));
13888 node.removeAttribute(n);
13894 for (var i = node.attributes.length-1; i > -1 ; i--) {
13895 var a = node.attributes[i];
13898 if (a.name.toLowerCase().substr(0,2)=='on') {
13899 node.removeAttribute(a.name);
13902 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13903 node.removeAttribute(a.name);
13906 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13907 cleanAttr(a.name,a.value); // fixme..
13910 if (a.name == 'style') {
13911 cleanStyle(a.name,a.value);
13914 /// clean up MS crap..
13915 // tecnically this should be a list of valid class'es..
13918 if (a.name == 'class') {
13919 if (a.value.match(/^Mso/)) {
13920 node.className = '';
13923 if (a.value.match(/body/)) {
13924 node.className = '';
13935 this.cleanUpChildren(node);
13941 // hide stuff that is not compatible
13955 * @event specialkey
13959 * @cfg {String} fieldClass @hide
13962 * @cfg {String} focusClass @hide
13965 * @cfg {String} autoCreate @hide
13968 * @cfg {String} inputType @hide
13971 * @cfg {String} invalidClass @hide
13974 * @cfg {String} invalidText @hide
13977 * @cfg {String} msgFx @hide
13980 * @cfg {String} validateOnBlur @hide
13984 Roo.HtmlEditorCore.white = [
13985 'area', 'br', 'img', 'input', 'hr', 'wbr',
13987 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
13988 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
13989 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
13990 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
13991 'table', 'ul', 'xmp',
13993 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
13996 'dir', 'menu', 'ol', 'ul', 'dl',
14002 Roo.HtmlEditorCore.black = [
14003 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14005 'base', 'basefont', 'bgsound', 'blink', 'body',
14006 'frame', 'frameset', 'head', 'html', 'ilayer',
14007 'iframe', 'layer', 'link', 'meta', 'object',
14008 'script', 'style' ,'title', 'xml' // clean later..
14010 Roo.HtmlEditorCore.clean = [
14011 'script', 'style', 'title', 'xml'
14013 Roo.HtmlEditorCore.remove = [
14018 Roo.HtmlEditorCore.ablack = [
14022 Roo.HtmlEditorCore.aclean = [
14023 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14027 Roo.HtmlEditorCore.pwhite= [
14028 'http', 'https', 'mailto'
14031 // white listed style attributes.
14032 Roo.HtmlEditorCore.cwhite= [
14033 // 'text-align', /// default is to allow most things..
14039 // black listed style attributes.
14040 Roo.HtmlEditorCore.cblack= [
14041 // 'font-size' -- this can be set by the project
14045 Roo.HtmlEditorCore.swapCodes =[
14064 * @class Roo.bootstrap.HtmlEditor
14065 * @extends Roo.bootstrap.TextArea
14066 * Bootstrap HtmlEditor class
14069 * Create a new HtmlEditor
14070 * @param {Object} config The config object
14073 Roo.bootstrap.HtmlEditor = function(config){
14074 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14075 if (!this.toolbars) {
14076 this.toolbars = [];
14078 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14081 * @event initialize
14082 * Fires when the editor is fully initialized (including the iframe)
14083 * @param {HtmlEditor} this
14088 * Fires when the editor is first receives the focus. Any insertion must wait
14089 * until after this event.
14090 * @param {HtmlEditor} this
14094 * @event beforesync
14095 * Fires before the textarea is updated with content from the editor iframe. Return false
14096 * to cancel the sync.
14097 * @param {HtmlEditor} this
14098 * @param {String} html
14102 * @event beforepush
14103 * Fires before the iframe editor is updated with content from the textarea. Return false
14104 * to cancel the push.
14105 * @param {HtmlEditor} this
14106 * @param {String} html
14111 * Fires when the textarea is updated with content from the editor iframe.
14112 * @param {HtmlEditor} this
14113 * @param {String} html
14118 * Fires when the iframe editor is updated with content from the textarea.
14119 * @param {HtmlEditor} this
14120 * @param {String} html
14124 * @event editmodechange
14125 * Fires when the editor switches edit modes
14126 * @param {HtmlEditor} this
14127 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14129 editmodechange: true,
14131 * @event editorevent
14132 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14133 * @param {HtmlEditor} this
14137 * @event firstfocus
14138 * Fires when on first focus - needed by toolbars..
14139 * @param {HtmlEditor} this
14144 * Auto save the htmlEditor value as a file into Events
14145 * @param {HtmlEditor} this
14149 * @event savedpreview
14150 * preview the saved version of htmlEditor
14151 * @param {HtmlEditor} this
14158 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14162 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14167 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14172 * @cfg {Number} height (in pixels)
14176 * @cfg {Number} width (in pixels)
14181 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14184 stylesheets: false,
14189 // private properties
14190 validationEvent : false,
14192 initialized : false,
14195 onFocus : Roo.emptyFn,
14197 hideMode:'offsets',
14200 tbContainer : false,
14202 toolbarContainer :function() {
14203 return this.wrap.select('.x-html-editor-tb',true).first();
14207 * Protected method that will not generally be called directly. It
14208 * is called when the editor creates its toolbar. Override this method if you need to
14209 * add custom toolbar buttons.
14210 * @param {HtmlEditor} editor
14212 createToolbar : function(){
14214 Roo.log("create toolbars");
14216 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14217 this.toolbars[0].render(this.toolbarContainer());
14221 // if (!editor.toolbars || !editor.toolbars.length) {
14222 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14225 // for (var i =0 ; i < editor.toolbars.length;i++) {
14226 // editor.toolbars[i] = Roo.factory(
14227 // typeof(editor.toolbars[i]) == 'string' ?
14228 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14229 // Roo.bootstrap.HtmlEditor);
14230 // editor.toolbars[i].init(editor);
14236 onRender : function(ct, position)
14238 // Roo.log("Call onRender: " + this.xtype);
14240 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14242 this.wrap = this.inputEl().wrap({
14243 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14246 this.editorcore.onRender(ct, position);
14248 if (this.resizable) {
14249 this.resizeEl = new Roo.Resizable(this.wrap, {
14253 minHeight : this.height,
14254 height: this.height,
14255 handles : this.resizable,
14258 resize : function(r, w, h) {
14259 _t.onResize(w,h); // -something
14265 this.createToolbar(this);
14268 if(!this.width && this.resizable){
14269 this.setSize(this.wrap.getSize());
14271 if (this.resizeEl) {
14272 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14273 // should trigger onReize..
14279 onResize : function(w, h)
14281 Roo.log('resize: ' +w + ',' + h );
14282 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14286 if(this.inputEl() ){
14287 if(typeof w == 'number'){
14288 var aw = w - this.wrap.getFrameWidth('lr');
14289 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14292 if(typeof h == 'number'){
14293 var tbh = -11; // fixme it needs to tool bar size!
14294 for (var i =0; i < this.toolbars.length;i++) {
14295 // fixme - ask toolbars for heights?
14296 tbh += this.toolbars[i].el.getHeight();
14297 //if (this.toolbars[i].footer) {
14298 // tbh += this.toolbars[i].footer.el.getHeight();
14306 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14307 ah -= 5; // knock a few pixes off for look..
14308 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14312 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14313 this.editorcore.onResize(ew,eh);
14318 * Toggles the editor between standard and source edit mode.
14319 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14321 toggleSourceEdit : function(sourceEditMode)
14323 this.editorcore.toggleSourceEdit(sourceEditMode);
14325 if(this.editorcore.sourceEditMode){
14326 Roo.log('editor - showing textarea');
14329 // Roo.log(this.syncValue());
14331 this.inputEl().removeClass('hide');
14332 this.inputEl().dom.removeAttribute('tabIndex');
14333 this.inputEl().focus();
14335 Roo.log('editor - hiding textarea');
14337 // Roo.log(this.pushValue());
14340 this.inputEl().addClass('hide');
14341 this.inputEl().dom.setAttribute('tabIndex', -1);
14342 //this.deferFocus();
14345 if(this.resizable){
14346 this.setSize(this.wrap.getSize());
14349 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14352 // private (for BoxComponent)
14353 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14355 // private (for BoxComponent)
14356 getResizeEl : function(){
14360 // private (for BoxComponent)
14361 getPositionEl : function(){
14366 initEvents : function(){
14367 this.originalValue = this.getValue();
14371 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14374 // markInvalid : Roo.emptyFn,
14376 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14379 // clearInvalid : Roo.emptyFn,
14381 setValue : function(v){
14382 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14383 this.editorcore.pushValue();
14388 deferFocus : function(){
14389 this.focus.defer(10, this);
14393 focus : function(){
14394 this.editorcore.focus();
14400 onDestroy : function(){
14406 for (var i =0; i < this.toolbars.length;i++) {
14407 // fixme - ask toolbars for heights?
14408 this.toolbars[i].onDestroy();
14411 this.wrap.dom.innerHTML = '';
14412 this.wrap.remove();
14417 onFirstFocus : function(){
14418 //Roo.log("onFirstFocus");
14419 this.editorcore.onFirstFocus();
14420 for (var i =0; i < this.toolbars.length;i++) {
14421 this.toolbars[i].onFirstFocus();
14427 syncValue : function()
14429 this.editorcore.syncValue();
14432 pushValue : function()
14434 this.editorcore.pushValue();
14438 // hide stuff that is not compatible
14452 * @event specialkey
14456 * @cfg {String} fieldClass @hide
14459 * @cfg {String} focusClass @hide
14462 * @cfg {String} autoCreate @hide
14465 * @cfg {String} inputType @hide
14468 * @cfg {String} invalidClass @hide
14471 * @cfg {String} invalidText @hide
14474 * @cfg {String} msgFx @hide
14477 * @cfg {String} validateOnBlur @hide
14488 * @class Roo.bootstrap.HtmlEditorToolbar1
14493 new Roo.bootstrap.HtmlEditor({
14496 new Roo.bootstrap.HtmlEditorToolbar1({
14497 disable : { fonts: 1 , format: 1, ..., ... , ...],
14503 * @cfg {Object} disable List of elements to disable..
14504 * @cfg {Array} btns List of additional buttons.
14508 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14511 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14514 Roo.apply(this, config);
14516 // default disabled, based on 'good practice'..
14517 this.disable = this.disable || {};
14518 Roo.applyIf(this.disable, {
14521 specialElements : true
14523 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14525 this.editor = config.editor;
14526 this.editorcore = config.editor.editorcore;
14528 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14530 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14531 // dont call parent... till later.
14533 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14539 editorcore : false,
14544 "h1","h2","h3","h4","h5","h6",
14546 "abbr", "acronym", "address", "cite", "samp", "var",
14550 onRender : function(ct, position)
14552 // Roo.log("Call onRender: " + this.xtype);
14554 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14556 this.el.dom.style.marginBottom = '0';
14558 var editorcore = this.editorcore;
14559 var editor= this.editor;
14562 var btn = function(id,cmd , toggle, handler){
14564 var event = toggle ? 'toggle' : 'click';
14569 xns: Roo.bootstrap,
14572 enableToggle:toggle !== false,
14574 pressed : toggle ? false : null,
14577 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14578 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14587 xns: Roo.bootstrap,
14588 glyphicon : 'font',
14592 xns: Roo.bootstrap,
14596 Roo.each(this.formats, function(f) {
14597 style.menu.items.push({
14599 xns: Roo.bootstrap,
14600 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14605 editorcore.insertTag(this.tagname);
14612 children.push(style);
14615 btn('bold',false,true);
14616 btn('italic',false,true);
14617 btn('align-left', 'justifyleft',true);
14618 btn('align-center', 'justifycenter',true);
14619 btn('align-right' , 'justifyright',true);
14620 btn('link', false, false, function(btn) {
14621 //Roo.log("create link?");
14622 var url = prompt(this.createLinkText, this.defaultLinkValue);
14623 if(url && url != 'http:/'+'/'){
14624 this.editorcore.relayCmd('createlink', url);
14627 btn('list','insertunorderedlist',true);
14628 btn('pencil', false,true, function(btn){
14631 this.toggleSourceEdit(btn.pressed);
14637 xns: Roo.bootstrap,
14642 xns: Roo.bootstrap,
14647 cog.menu.items.push({
14649 xns: Roo.bootstrap,
14650 html : Clean styles,
14655 editorcore.insertTag(this.tagname);
14664 this.xtype = 'Navbar';
14666 for(var i=0;i< children.length;i++) {
14668 this.buttons.add(this.addxtypeChild(children[i]));
14672 editor.on('editorevent', this.updateToolbar, this);
14674 onBtnClick : function(id)
14676 this.editorcore.relayCmd(id);
14677 this.editorcore.focus();
14681 * Protected method that will not generally be called directly. It triggers
14682 * a toolbar update by reading the markup state of the current selection in the editor.
14684 updateToolbar: function(){
14686 if(!this.editorcore.activated){
14687 this.editor.onFirstFocus(); // is this neeed?
14691 var btns = this.buttons;
14692 var doc = this.editorcore.doc;
14693 btns.get('bold').setActive(doc.queryCommandState('bold'));
14694 btns.get('italic').setActive(doc.queryCommandState('italic'));
14695 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14697 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14698 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14699 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14701 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14702 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14705 var ans = this.editorcore.getAllAncestors();
14706 if (this.formatCombo) {
14709 var store = this.formatCombo.store;
14710 this.formatCombo.setValue("");
14711 for (var i =0; i < ans.length;i++) {
14712 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14714 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14722 // hides menus... - so this cant be on a menu...
14723 Roo.bootstrap.MenuMgr.hideAll();
14725 Roo.bootstrap.MenuMgr.hideAll();
14726 //this.editorsyncValue();
14728 onFirstFocus: function() {
14729 this.buttons.each(function(item){
14733 toggleSourceEdit : function(sourceEditMode){
14736 if(sourceEditMode){
14737 Roo.log("disabling buttons");
14738 this.buttons.each( function(item){
14739 if(item.cmd != 'pencil'){
14745 Roo.log("enabling buttons");
14746 if(this.editorcore.initialized){
14747 this.buttons.each( function(item){
14753 Roo.log("calling toggole on editor");
14754 // tell the editor that it's been pressed..
14755 this.editor.toggleSourceEdit(sourceEditMode);
14765 * @class Roo.bootstrap.Table.AbstractSelectionModel
14766 * @extends Roo.util.Observable
14767 * Abstract base class for grid SelectionModels. It provides the interface that should be
14768 * implemented by descendant classes. This class should not be directly instantiated.
14771 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14772 this.locked = false;
14773 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14777 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14778 /** @ignore Called by the grid automatically. Do not call directly. */
14779 init : function(grid){
14785 * Locks the selections.
14788 this.locked = true;
14792 * Unlocks the selections.
14794 unlock : function(){
14795 this.locked = false;
14799 * Returns true if the selections are locked.
14800 * @return {Boolean}
14802 isLocked : function(){
14803 return this.locked;
14807 * @class Roo.bootstrap.Table.ColumnModel
14808 * @extends Roo.util.Observable
14809 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14810 * the columns in the table.
14813 * @param {Object} config An Array of column config objects. See this class's
14814 * config objects for details.
14816 Roo.bootstrap.Table.ColumnModel = function(config){
14818 * The config passed into the constructor
14820 this.config = config;
14823 // if no id, create one
14824 // if the column does not have a dataIndex mapping,
14825 // map it to the order it is in the config
14826 for(var i = 0, len = config.length; i < len; i++){
14828 if(typeof c.dataIndex == "undefined"){
14831 if(typeof c.renderer == "string"){
14832 c.renderer = Roo.util.Format[c.renderer];
14834 if(typeof c.id == "undefined"){
14837 // if(c.editor && c.editor.xtype){
14838 // c.editor = Roo.factory(c.editor, Roo.grid);
14840 // if(c.editor && c.editor.isFormField){
14841 // c.editor = new Roo.grid.GridEditor(c.editor);
14844 this.lookup[c.id] = c;
14848 * The width of columns which have no width specified (defaults to 100)
14851 this.defaultWidth = 100;
14854 * Default sortable of columns which have no sortable specified (defaults to false)
14857 this.defaultSortable = false;
14861 * @event widthchange
14862 * Fires when the width of a column changes.
14863 * @param {ColumnModel} this
14864 * @param {Number} columnIndex The column index
14865 * @param {Number} newWidth The new width
14867 "widthchange": true,
14869 * @event headerchange
14870 * Fires when the text of a header changes.
14871 * @param {ColumnModel} this
14872 * @param {Number} columnIndex The column index
14873 * @param {Number} newText The new header text
14875 "headerchange": true,
14877 * @event hiddenchange
14878 * Fires when a column is hidden or "unhidden".
14879 * @param {ColumnModel} this
14880 * @param {Number} columnIndex The column index
14881 * @param {Boolean} hidden true if hidden, false otherwise
14883 "hiddenchange": true,
14885 * @event columnmoved
14886 * Fires when a column is moved.
14887 * @param {ColumnModel} this
14888 * @param {Number} oldIndex
14889 * @param {Number} newIndex
14891 "columnmoved" : true,
14893 * @event columlockchange
14894 * Fires when a column's locked state is changed
14895 * @param {ColumnModel} this
14896 * @param {Number} colIndex
14897 * @param {Boolean} locked true if locked
14899 "columnlockchange" : true
14901 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14903 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14905 * @cfg {String} header The header text to display in the Grid view.
14908 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14909 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14910 * specified, the column's index is used as an index into the Record's data Array.
14913 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14914 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14917 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14918 * Defaults to the value of the {@link #defaultSortable} property.
14919 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14922 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14925 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14928 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14931 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14934 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14935 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14936 * default renderer uses the raw data value.
14939 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14943 * Returns the id of the column at the specified index.
14944 * @param {Number} index The column index
14945 * @return {String} the id
14947 getColumnId : function(index){
14948 return this.config[index].id;
14952 * Returns the column for a specified id.
14953 * @param {String} id The column id
14954 * @return {Object} the column
14956 getColumnById : function(id){
14957 return this.lookup[id];
14962 * Returns the column for a specified dataIndex.
14963 * @param {String} dataIndex The column dataIndex
14964 * @return {Object|Boolean} the column or false if not found
14966 getColumnByDataIndex: function(dataIndex){
14967 var index = this.findColumnIndex(dataIndex);
14968 return index > -1 ? this.config[index] : false;
14972 * Returns the index for a specified column id.
14973 * @param {String} id The column id
14974 * @return {Number} the index, or -1 if not found
14976 getIndexById : function(id){
14977 for(var i = 0, len = this.config.length; i < len; i++){
14978 if(this.config[i].id == id){
14986 * Returns the index for a specified column dataIndex.
14987 * @param {String} dataIndex The column dataIndex
14988 * @return {Number} the index, or -1 if not found
14991 findColumnIndex : function(dataIndex){
14992 for(var i = 0, len = this.config.length; i < len; i++){
14993 if(this.config[i].dataIndex == dataIndex){
15001 moveColumn : function(oldIndex, newIndex){
15002 var c = this.config[oldIndex];
15003 this.config.splice(oldIndex, 1);
15004 this.config.splice(newIndex, 0, c);
15005 this.dataMap = null;
15006 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15009 isLocked : function(colIndex){
15010 return this.config[colIndex].locked === true;
15013 setLocked : function(colIndex, value, suppressEvent){
15014 if(this.isLocked(colIndex) == value){
15017 this.config[colIndex].locked = value;
15018 if(!suppressEvent){
15019 this.fireEvent("columnlockchange", this, colIndex, value);
15023 getTotalLockedWidth : function(){
15024 var totalWidth = 0;
15025 for(var i = 0; i < this.config.length; i++){
15026 if(this.isLocked(i) && !this.isHidden(i)){
15027 this.totalWidth += this.getColumnWidth(i);
15033 getLockedCount : function(){
15034 for(var i = 0, len = this.config.length; i < len; i++){
15035 if(!this.isLocked(i)){
15042 * Returns the number of columns.
15045 getColumnCount : function(visibleOnly){
15046 if(visibleOnly === true){
15048 for(var i = 0, len = this.config.length; i < len; i++){
15049 if(!this.isHidden(i)){
15055 return this.config.length;
15059 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15060 * @param {Function} fn
15061 * @param {Object} scope (optional)
15062 * @return {Array} result
15064 getColumnsBy : function(fn, scope){
15066 for(var i = 0, len = this.config.length; i < len; i++){
15067 var c = this.config[i];
15068 if(fn.call(scope||this, c, i) === true){
15076 * Returns true if the specified column is sortable.
15077 * @param {Number} col The column index
15078 * @return {Boolean}
15080 isSortable : function(col){
15081 if(typeof this.config[col].sortable == "undefined"){
15082 return this.defaultSortable;
15084 return this.config[col].sortable;
15088 * Returns the rendering (formatting) function defined for the column.
15089 * @param {Number} col The column index.
15090 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15092 getRenderer : function(col){
15093 if(!this.config[col].renderer){
15094 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15096 return this.config[col].renderer;
15100 * Sets the rendering (formatting) function for a column.
15101 * @param {Number} col The column index
15102 * @param {Function} fn The function to use to process the cell's raw data
15103 * to return HTML markup for the grid view. The render function is called with
15104 * the following parameters:<ul>
15105 * <li>Data value.</li>
15106 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15107 * <li>css A CSS style string to apply to the table cell.</li>
15108 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15109 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15110 * <li>Row index</li>
15111 * <li>Column index</li>
15112 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15114 setRenderer : function(col, fn){
15115 this.config[col].renderer = fn;
15119 * Returns the width for the specified column.
15120 * @param {Number} col The column index
15123 getColumnWidth : function(col){
15124 return this.config[col].width * 1 || this.defaultWidth;
15128 * Sets the width for a column.
15129 * @param {Number} col The column index
15130 * @param {Number} width The new width
15132 setColumnWidth : function(col, width, suppressEvent){
15133 this.config[col].width = width;
15134 this.totalWidth = null;
15135 if(!suppressEvent){
15136 this.fireEvent("widthchange", this, col, width);
15141 * Returns the total width of all columns.
15142 * @param {Boolean} includeHidden True to include hidden column widths
15145 getTotalWidth : function(includeHidden){
15146 if(!this.totalWidth){
15147 this.totalWidth = 0;
15148 for(var i = 0, len = this.config.length; i < len; i++){
15149 if(includeHidden || !this.isHidden(i)){
15150 this.totalWidth += this.getColumnWidth(i);
15154 return this.totalWidth;
15158 * Returns the header for the specified column.
15159 * @param {Number} col The column index
15162 getColumnHeader : function(col){
15163 return this.config[col].header;
15167 * Sets the header for a column.
15168 * @param {Number} col The column index
15169 * @param {String} header The new header
15171 setColumnHeader : function(col, header){
15172 this.config[col].header = header;
15173 this.fireEvent("headerchange", this, col, header);
15177 * Returns the tooltip for the specified column.
15178 * @param {Number} col The column index
15181 getColumnTooltip : function(col){
15182 return this.config[col].tooltip;
15185 * Sets the tooltip for a column.
15186 * @param {Number} col The column index
15187 * @param {String} tooltip The new tooltip
15189 setColumnTooltip : function(col, tooltip){
15190 this.config[col].tooltip = tooltip;
15194 * Returns the dataIndex for the specified column.
15195 * @param {Number} col The column index
15198 getDataIndex : function(col){
15199 return this.config[col].dataIndex;
15203 * Sets the dataIndex for a column.
15204 * @param {Number} col The column index
15205 * @param {Number} dataIndex The new dataIndex
15207 setDataIndex : function(col, dataIndex){
15208 this.config[col].dataIndex = dataIndex;
15214 * Returns true if the cell is editable.
15215 * @param {Number} colIndex The column index
15216 * @param {Number} rowIndex The row index
15217 * @return {Boolean}
15219 isCellEditable : function(colIndex, rowIndex){
15220 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15224 * Returns the editor defined for the cell/column.
15225 * return false or null to disable editing.
15226 * @param {Number} colIndex The column index
15227 * @param {Number} rowIndex The row index
15230 getCellEditor : function(colIndex, rowIndex){
15231 return this.config[colIndex].editor;
15235 * Sets if a column is editable.
15236 * @param {Number} col The column index
15237 * @param {Boolean} editable True if the column is editable
15239 setEditable : function(col, editable){
15240 this.config[col].editable = editable;
15245 * Returns true if the column is hidden.
15246 * @param {Number} colIndex The column index
15247 * @return {Boolean}
15249 isHidden : function(colIndex){
15250 return this.config[colIndex].hidden;
15255 * Returns true if the column width cannot be changed
15257 isFixed : function(colIndex){
15258 return this.config[colIndex].fixed;
15262 * Returns true if the column can be resized
15263 * @return {Boolean}
15265 isResizable : function(colIndex){
15266 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15269 * Sets if a column is hidden.
15270 * @param {Number} colIndex The column index
15271 * @param {Boolean} hidden True if the column is hidden
15273 setHidden : function(colIndex, hidden){
15274 this.config[colIndex].hidden = hidden;
15275 this.totalWidth = null;
15276 this.fireEvent("hiddenchange", this, colIndex, hidden);
15280 * Sets the editor for a column.
15281 * @param {Number} col The column index
15282 * @param {Object} editor The editor object
15284 setEditor : function(col, editor){
15285 this.config[col].editor = editor;
15289 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15290 if(typeof value == "string" && value.length < 1){
15296 // Alias for backwards compatibility
15297 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15300 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15301 * @class Roo.bootstrap.Table.RowSelectionModel
15302 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15303 * It supports multiple selections and keyboard selection/navigation.
15305 * @param {Object} config
15308 Roo.bootstrap.Table.RowSelectionModel = function(config){
15309 Roo.apply(this, config);
15310 this.selections = new Roo.util.MixedCollection(false, function(o){
15315 this.lastActive = false;
15319 * @event selectionchange
15320 * Fires when the selection changes
15321 * @param {SelectionModel} this
15323 "selectionchange" : true,
15325 * @event afterselectionchange
15326 * Fires after the selection changes (eg. by key press or clicking)
15327 * @param {SelectionModel} this
15329 "afterselectionchange" : true,
15331 * @event beforerowselect
15332 * Fires when a row is selected being selected, return false to cancel.
15333 * @param {SelectionModel} this
15334 * @param {Number} rowIndex The selected index
15335 * @param {Boolean} keepExisting False if other selections will be cleared
15337 "beforerowselect" : true,
15340 * Fires when a row is selected.
15341 * @param {SelectionModel} this
15342 * @param {Number} rowIndex The selected index
15343 * @param {Roo.data.Record} r The record
15345 "rowselect" : true,
15347 * @event rowdeselect
15348 * Fires when a row is deselected.
15349 * @param {SelectionModel} this
15350 * @param {Number} rowIndex The selected index
15352 "rowdeselect" : true
15354 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15355 this.locked = false;
15358 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15360 * @cfg {Boolean} singleSelect
15361 * True to allow selection of only one row at a time (defaults to false)
15363 singleSelect : false,
15366 initEvents : function(){
15368 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15369 this.grid.on("mousedown", this.handleMouseDown, this);
15370 }else{ // allow click to work like normal
15371 this.grid.on("rowclick", this.handleDragableRowClick, this);
15374 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15375 "up" : function(e){
15377 this.selectPrevious(e.shiftKey);
15378 }else if(this.last !== false && this.lastActive !== false){
15379 var last = this.last;
15380 this.selectRange(this.last, this.lastActive-1);
15381 this.grid.getView().focusRow(this.lastActive);
15382 if(last !== false){
15386 this.selectFirstRow();
15388 this.fireEvent("afterselectionchange", this);
15390 "down" : function(e){
15392 this.selectNext(e.shiftKey);
15393 }else if(this.last !== false && this.lastActive !== false){
15394 var last = this.last;
15395 this.selectRange(this.last, this.lastActive+1);
15396 this.grid.getView().focusRow(this.lastActive);
15397 if(last !== false){
15401 this.selectFirstRow();
15403 this.fireEvent("afterselectionchange", this);
15408 var view = this.grid.view;
15409 view.on("refresh", this.onRefresh, this);
15410 view.on("rowupdated", this.onRowUpdated, this);
15411 view.on("rowremoved", this.onRemove, this);
15415 onRefresh : function(){
15416 var ds = this.grid.dataSource, i, v = this.grid.view;
15417 var s = this.selections;
15418 s.each(function(r){
15419 if((i = ds.indexOfId(r.id)) != -1){
15428 onRemove : function(v, index, r){
15429 this.selections.remove(r);
15433 onRowUpdated : function(v, index, r){
15434 if(this.isSelected(r)){
15435 v.onRowSelect(index);
15441 * @param {Array} records The records to select
15442 * @param {Boolean} keepExisting (optional) True to keep existing selections
15444 selectRecords : function(records, keepExisting){
15446 this.clearSelections();
15448 var ds = this.grid.dataSource;
15449 for(var i = 0, len = records.length; i < len; i++){
15450 this.selectRow(ds.indexOf(records[i]), true);
15455 * Gets the number of selected rows.
15458 getCount : function(){
15459 return this.selections.length;
15463 * Selects the first row in the grid.
15465 selectFirstRow : function(){
15470 * Select the last row.
15471 * @param {Boolean} keepExisting (optional) True to keep existing selections
15473 selectLastRow : function(keepExisting){
15474 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15478 * Selects the row immediately following the last selected row.
15479 * @param {Boolean} keepExisting (optional) True to keep existing selections
15481 selectNext : function(keepExisting){
15482 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15483 this.selectRow(this.last+1, keepExisting);
15484 this.grid.getView().focusRow(this.last);
15489 * Selects the row that precedes the last selected row.
15490 * @param {Boolean} keepExisting (optional) True to keep existing selections
15492 selectPrevious : function(keepExisting){
15494 this.selectRow(this.last-1, keepExisting);
15495 this.grid.getView().focusRow(this.last);
15500 * Returns the selected records
15501 * @return {Array} Array of selected records
15503 getSelections : function(){
15504 return [].concat(this.selections.items);
15508 * Returns the first selected record.
15511 getSelected : function(){
15512 return this.selections.itemAt(0);
15517 * Clears all selections.
15519 clearSelections : function(fast){
15520 if(this.locked) return;
15522 var ds = this.grid.dataSource;
15523 var s = this.selections;
15524 s.each(function(r){
15525 this.deselectRow(ds.indexOfId(r.id));
15529 this.selections.clear();
15536 * Selects all rows.
15538 selectAll : function(){
15539 if(this.locked) return;
15540 this.selections.clear();
15541 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15542 this.selectRow(i, true);
15547 * Returns True if there is a selection.
15548 * @return {Boolean}
15550 hasSelection : function(){
15551 return this.selections.length > 0;
15555 * Returns True if the specified row is selected.
15556 * @param {Number/Record} record The record or index of the record to check
15557 * @return {Boolean}
15559 isSelected : function(index){
15560 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15561 return (r && this.selections.key(r.id) ? true : false);
15565 * Returns True if the specified record id is selected.
15566 * @param {String} id The id of record to check
15567 * @return {Boolean}
15569 isIdSelected : function(id){
15570 return (this.selections.key(id) ? true : false);
15574 handleMouseDown : function(e, t){
15575 var view = this.grid.getView(), rowIndex;
15576 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15579 if(e.shiftKey && this.last !== false){
15580 var last = this.last;
15581 this.selectRange(last, rowIndex, e.ctrlKey);
15582 this.last = last; // reset the last
15583 view.focusRow(rowIndex);
15585 var isSelected = this.isSelected(rowIndex);
15586 if(e.button !== 0 && isSelected){
15587 view.focusRow(rowIndex);
15588 }else if(e.ctrlKey && isSelected){
15589 this.deselectRow(rowIndex);
15590 }else if(!isSelected){
15591 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15592 view.focusRow(rowIndex);
15595 this.fireEvent("afterselectionchange", this);
15598 handleDragableRowClick : function(grid, rowIndex, e)
15600 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15601 this.selectRow(rowIndex, false);
15602 grid.view.focusRow(rowIndex);
15603 this.fireEvent("afterselectionchange", this);
15608 * Selects multiple rows.
15609 * @param {Array} rows Array of the indexes of the row to select
15610 * @param {Boolean} keepExisting (optional) True to keep existing selections
15612 selectRows : function(rows, keepExisting){
15614 this.clearSelections();
15616 for(var i = 0, len = rows.length; i < len; i++){
15617 this.selectRow(rows[i], true);
15622 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15623 * @param {Number} startRow The index of the first row in the range
15624 * @param {Number} endRow The index of the last row in the range
15625 * @param {Boolean} keepExisting (optional) True to retain existing selections
15627 selectRange : function(startRow, endRow, keepExisting){
15628 if(this.locked) return;
15630 this.clearSelections();
15632 if(startRow <= endRow){
15633 for(var i = startRow; i <= endRow; i++){
15634 this.selectRow(i, true);
15637 for(var i = startRow; i >= endRow; i--){
15638 this.selectRow(i, true);
15644 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15645 * @param {Number} startRow The index of the first row in the range
15646 * @param {Number} endRow The index of the last row in the range
15648 deselectRange : function(startRow, endRow, preventViewNotify){
15649 if(this.locked) return;
15650 for(var i = startRow; i <= endRow; i++){
15651 this.deselectRow(i, preventViewNotify);
15657 * @param {Number} row The index of the row to select
15658 * @param {Boolean} keepExisting (optional) True to keep existing selections
15660 selectRow : function(index, keepExisting, preventViewNotify){
15661 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15662 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15663 if(!keepExisting || this.singleSelect){
15664 this.clearSelections();
15666 var r = this.grid.dataSource.getAt(index);
15667 this.selections.add(r);
15668 this.last = this.lastActive = index;
15669 if(!preventViewNotify){
15670 this.grid.getView().onRowSelect(index);
15672 this.fireEvent("rowselect", this, index, r);
15673 this.fireEvent("selectionchange", this);
15679 * @param {Number} row The index of the row to deselect
15681 deselectRow : function(index, preventViewNotify){
15682 if(this.locked) return;
15683 if(this.last == index){
15686 if(this.lastActive == index){
15687 this.lastActive = false;
15689 var r = this.grid.dataSource.getAt(index);
15690 this.selections.remove(r);
15691 if(!preventViewNotify){
15692 this.grid.getView().onRowDeselect(index);
15694 this.fireEvent("rowdeselect", this, index);
15695 this.fireEvent("selectionchange", this);
15699 restoreLast : function(){
15701 this.last = this._last;
15706 acceptsNav : function(row, col, cm){
15707 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15711 onEditorKey : function(field, e){
15712 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15717 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15719 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15721 }else if(k == e.ENTER && !e.ctrlKey){
15725 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15727 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15729 }else if(k == e.ESC){
15733 g.startEditing(newCell[0], newCell[1]);
15744 * @class Roo.bootstrap.MessageBar
15745 * @extends Roo.bootstrap.Component
15746 * Bootstrap MessageBar class
15747 * @cfg {String} html contents of the MessageBar
15748 * @cfg {String} weight (info | success | warning | danger) default info
15749 * @cfg {String} beforeClass insert the bar before the given class
15750 * @cfg {Boolean} closable (true | false) default false
15751 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15754 * Create a new Element
15755 * @param {Object} config The config object
15758 Roo.bootstrap.MessageBar = function(config){
15759 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15762 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15768 beforeClass: 'bootstrap-sticky-wrap',
15770 getAutoCreate : function(){
15774 cls: 'alert alert-dismissable alert-' + this.weight,
15779 html: this.html || ''
15785 cfg.cls += ' alert-messages-fixed';
15799 onRender : function(ct, position)
15801 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15804 var cfg = Roo.apply({}, this.getAutoCreate());
15808 cfg.cls += ' ' + this.cls;
15811 cfg.style = this.style;
15813 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15815 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15818 this.el.select('>button.close').on('click', this.hide, this);
15824 if (!this.rendered) {
15830 this.fireEvent('show', this);
15836 if (!this.rendered) {
15842 this.fireEvent('hide', this);
15845 update : function()
15847 // var e = this.el.dom.firstChild;
15849 // if(this.closable){
15850 // e = e.nextSibling;
15853 // e.data = this.html || '';
15855 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';