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!!!!');
2955 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
2956 e.on('click', _this.sort, _this);
2958 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2959 // this.maskEl.enableDisplayMode("block");
2960 // this.maskEl.show();
2962 this.store.on('load', this.onLoad, this);
2963 this.store.on('beforeload', this.onBeforeLoad, this);
2971 sort : function(a,b,c)
2976 //this.store.sortInfo = {field:'created_dt',direction:'DESC'};
2978 //this.store.load();
2981 renderHeader : function()
2990 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2992 var config = cm.config[i];
2996 html: cm.getColumnHeader(i)
2999 if(typeof(config.dataIndex) != 'undefined'){
3000 c.sort = config.dataIndex;
3003 if(typeof(config.sortable) != 'undefined' && config.sortable){
3013 renderBody : function()
3023 renderFooter : function()
3035 Roo.log('ds onload');
3039 var tbody = this.el.select('tbody', true).first();
3043 if(this.store.getCount() > 0){
3044 this.store.data.each(function(d){
3050 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3051 var renderer = cm.getRenderer(i);
3052 var config = cm.config[i];
3056 if(typeof(renderer) !== 'undefined'){
3057 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3060 if(typeof(value) === 'object'){
3070 html: (typeof(value) === 'object') ? '' : value
3073 if(typeof(config.width) != 'undefined'){
3074 td.width = config.width;
3081 tbody.createChild(row);
3089 Roo.each(renders, function(r){
3090 _this.renderColumn(r);
3094 // if(this.loadMask){
3095 // this.maskEl.hide();
3099 onBeforeLoad : function()
3101 Roo.log('ds onBeforeLoad');
3105 // if(this.loadMask){
3106 // this.maskEl.show();
3112 this.el.select('tbody', true).first().dom.innerHTML = '';
3115 getSelectionModel : function(){
3117 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3119 return this.selModel;
3122 renderColumn : function(r)
3125 r.cfg.render(Roo.get(r.id));
3128 Roo.each(r.cfg.cn, function(c){
3133 _this.renderColumn(child);
3150 * @class Roo.bootstrap.TableCell
3151 * @extends Roo.bootstrap.Component
3152 * Bootstrap TableCell class
3153 * @cfg {String} html cell contain text
3154 * @cfg {String} cls cell class
3155 * @cfg {String} tag cell tag (td|th) default td
3156 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3157 * @cfg {String} align Aligns the content in a cell
3158 * @cfg {String} axis Categorizes cells
3159 * @cfg {String} bgcolor Specifies the background color of a cell
3160 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3161 * @cfg {Number} colspan Specifies the number of columns a cell should span
3162 * @cfg {String} headers Specifies one or more header cells a cell is related to
3163 * @cfg {Number} height Sets the height of a cell
3164 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3165 * @cfg {Number} rowspan Sets the number of rows a cell should span
3166 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3167 * @cfg {String} valign Vertical aligns the content in a cell
3168 * @cfg {Number} width Specifies the width of a cell
3171 * Create a new TableCell
3172 * @param {Object} config The config object
3175 Roo.bootstrap.TableCell = function(config){
3176 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3179 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3199 getAutoCreate : function(){
3200 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3220 cfg.align=this.align
3226 cfg.bgcolor=this.bgcolor
3229 cfg.charoff=this.charoff
3232 cfg.colspan=this.colspan
3235 cfg.headers=this.headers
3238 cfg.height=this.height
3241 cfg.nowrap=this.nowrap
3244 cfg.rowspan=this.rowspan
3247 cfg.scope=this.scope
3250 cfg.valign=this.valign
3253 cfg.width=this.width
3272 * @class Roo.bootstrap.TableRow
3273 * @extends Roo.bootstrap.Component
3274 * Bootstrap TableRow class
3275 * @cfg {String} cls row class
3276 * @cfg {String} align Aligns the content in a table row
3277 * @cfg {String} bgcolor Specifies a background color for a table row
3278 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3279 * @cfg {String} valign Vertical aligns the content in a table row
3282 * Create a new TableRow
3283 * @param {Object} config The config object
3286 Roo.bootstrap.TableRow = function(config){
3287 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3290 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3298 getAutoCreate : function(){
3299 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3309 cfg.align = this.align;
3312 cfg.bgcolor = this.bgcolor;
3315 cfg.charoff = this.charoff;
3318 cfg.valign = this.valign;
3336 * @class Roo.bootstrap.TableBody
3337 * @extends Roo.bootstrap.Component
3338 * Bootstrap TableBody class
3339 * @cfg {String} cls element class
3340 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3341 * @cfg {String} align Aligns the content inside the element
3342 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3343 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3346 * Create a new TableBody
3347 * @param {Object} config The config object
3350 Roo.bootstrap.TableBody = function(config){
3351 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3354 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3362 getAutoCreate : function(){
3363 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3377 cfg.align = this.align;
3380 cfg.charoff = this.charoff;
3383 cfg.valign = this.valign;
3390 // initEvents : function()
3397 // this.store = Roo.factory(this.store, Roo.data);
3398 // this.store.on('load', this.onLoad, this);
3400 // this.store.load();
3404 // onLoad: function ()
3406 // this.fireEvent('load', this);
3416 * Ext JS Library 1.1.1
3417 * Copyright(c) 2006-2007, Ext JS, LLC.
3419 * Originally Released Under LGPL - original licence link has changed is not relivant.
3422 * <script type="text/javascript">
3425 // as we use this in bootstrap.
3426 Roo.namespace('Roo.form');
3428 * @class Roo.form.Action
3429 * Internal Class used to handle form actions
3431 * @param {Roo.form.BasicForm} el The form element or its id
3432 * @param {Object} config Configuration options
3437 // define the action interface
3438 Roo.form.Action = function(form, options){
3440 this.options = options || {};
3443 * Client Validation Failed
3446 Roo.form.Action.CLIENT_INVALID = 'client';
3448 * Server Validation Failed
3451 Roo.form.Action.SERVER_INVALID = 'server';
3453 * Connect to Server Failed
3456 Roo.form.Action.CONNECT_FAILURE = 'connect';
3458 * Reading Data from Server Failed
3461 Roo.form.Action.LOAD_FAILURE = 'load';
3463 Roo.form.Action.prototype = {
3465 failureType : undefined,
3466 response : undefined,
3470 run : function(options){
3475 success : function(response){
3480 handleResponse : function(response){
3484 // default connection failure
3485 failure : function(response){
3487 this.response = response;
3488 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3489 this.form.afterAction(this, false);
3492 processResponse : function(response){
3493 this.response = response;
3494 if(!response.responseText){
3497 this.result = this.handleResponse(response);
3501 // utility functions used internally
3502 getUrl : function(appendParams){
3503 var url = this.options.url || this.form.url || this.form.el.dom.action;
3505 var p = this.getParams();
3507 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3513 getMethod : function(){
3514 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3517 getParams : function(){
3518 var bp = this.form.baseParams;
3519 var p = this.options.params;
3521 if(typeof p == "object"){
3522 p = Roo.urlEncode(Roo.applyIf(p, bp));
3523 }else if(typeof p == 'string' && bp){
3524 p += '&' + Roo.urlEncode(bp);
3527 p = Roo.urlEncode(bp);
3532 createCallback : function(){
3534 success: this.success,
3535 failure: this.failure,
3537 timeout: (this.form.timeout*1000),
3538 upload: this.form.fileUpload ? this.success : undefined
3543 Roo.form.Action.Submit = function(form, options){
3544 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3547 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3550 haveProgress : false,
3551 uploadComplete : false,
3553 // uploadProgress indicator.
3554 uploadProgress : function()
3556 if (!this.form.progressUrl) {
3560 if (!this.haveProgress) {
3561 Roo.MessageBox.progress("Uploading", "Uploading");
3563 if (this.uploadComplete) {
3564 Roo.MessageBox.hide();
3568 this.haveProgress = true;
3570 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3572 var c = new Roo.data.Connection();
3574 url : this.form.progressUrl,
3579 success : function(req){
3580 //console.log(data);
3584 rdata = Roo.decode(req.responseText)
3586 Roo.log("Invalid data from server..");
3590 if (!rdata || !rdata.success) {
3592 Roo.MessageBox.alert(Roo.encode(rdata));
3595 var data = rdata.data;
3597 if (this.uploadComplete) {
3598 Roo.MessageBox.hide();
3603 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3604 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3607 this.uploadProgress.defer(2000,this);
3610 failure: function(data) {
3611 Roo.log('progress url failed ');
3622 // run get Values on the form, so it syncs any secondary forms.
3623 this.form.getValues();
3625 var o = this.options;
3626 var method = this.getMethod();
3627 var isPost = method == 'POST';
3628 if(o.clientValidation === false || this.form.isValid()){
3630 if (this.form.progressUrl) {
3631 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3632 (new Date() * 1) + '' + Math.random());
3637 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3638 form:this.form.el.dom,
3639 url:this.getUrl(!isPost),
3641 params:isPost ? this.getParams() : null,
3642 isUpload: this.form.fileUpload
3645 this.uploadProgress();
3647 }else if (o.clientValidation !== false){ // client validation failed
3648 this.failureType = Roo.form.Action.CLIENT_INVALID;
3649 this.form.afterAction(this, false);
3653 success : function(response)
3655 this.uploadComplete= true;
3656 if (this.haveProgress) {
3657 Roo.MessageBox.hide();
3661 var result = this.processResponse(response);
3662 if(result === true || result.success){
3663 this.form.afterAction(this, true);
3667 this.form.markInvalid(result.errors);
3668 this.failureType = Roo.form.Action.SERVER_INVALID;
3670 this.form.afterAction(this, false);
3672 failure : function(response)
3674 this.uploadComplete= true;
3675 if (this.haveProgress) {
3676 Roo.MessageBox.hide();
3679 this.response = response;
3680 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3681 this.form.afterAction(this, false);
3684 handleResponse : function(response){
3685 if(this.form.errorReader){
3686 var rs = this.form.errorReader.read(response);
3689 for(var i = 0, len = rs.records.length; i < len; i++) {
3690 var r = rs.records[i];
3694 if(errors.length < 1){
3698 success : rs.success,
3704 ret = Roo.decode(response.responseText);
3708 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3718 Roo.form.Action.Load = function(form, options){
3719 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3720 this.reader = this.form.reader;
3723 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3728 Roo.Ajax.request(Roo.apply(
3729 this.createCallback(), {
3730 method:this.getMethod(),
3731 url:this.getUrl(false),
3732 params:this.getParams()
3736 success : function(response){
3738 var result = this.processResponse(response);
3739 if(result === true || !result.success || !result.data){
3740 this.failureType = Roo.form.Action.LOAD_FAILURE;
3741 this.form.afterAction(this, false);
3744 this.form.clearInvalid();
3745 this.form.setValues(result.data);
3746 this.form.afterAction(this, true);
3749 handleResponse : function(response){
3750 if(this.form.reader){
3751 var rs = this.form.reader.read(response);
3752 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3754 success : rs.success,
3758 return Roo.decode(response.responseText);
3762 Roo.form.Action.ACTION_TYPES = {
3763 'load' : Roo.form.Action.Load,
3764 'submit' : Roo.form.Action.Submit
3773 * @class Roo.bootstrap.Form
3774 * @extends Roo.bootstrap.Component
3775 * Bootstrap Form class
3776 * @cfg {String} method GET | POST (default POST)
3777 * @cfg {String} labelAlign top | left (default top)
3778 * @cfg {String} align left | right - for navbars
3783 * @param {Object} config The config object
3787 Roo.bootstrap.Form = function(config){
3788 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3791 * @event clientvalidation
3792 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3793 * @param {Form} this
3794 * @param {Boolean} valid true if the form has passed client-side validation
3796 clientvalidation: true,
3798 * @event beforeaction
3799 * Fires before any action is performed. Return false to cancel the action.
3800 * @param {Form} this
3801 * @param {Action} action The action to be performed
3805 * @event actionfailed
3806 * Fires when an action fails.
3807 * @param {Form} this
3808 * @param {Action} action The action that failed
3810 actionfailed : true,
3812 * @event actioncomplete
3813 * Fires when an action is completed.
3814 * @param {Form} this
3815 * @param {Action} action The action that completed
3817 actioncomplete : true
3822 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3825 * @cfg {String} method
3826 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3831 * The URL to use for form actions if one isn't supplied in the action options.
3834 * @cfg {Boolean} fileUpload
3835 * Set to true if this form is a file upload.
3839 * @cfg {Object} baseParams
3840 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3844 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3848 * @cfg {Sting} align (left|right) for navbar forms
3853 activeAction : null,
3856 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3857 * element by passing it or its id or mask the form itself by passing in true.
3860 waitMsgTarget : false,
3865 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3866 * element by passing it or its id or mask the form itself by passing in true.
3870 getAutoCreate : function(){
3874 method : this.method || 'POST',
3875 id : this.id || Roo.id(),
3878 if (this.parent().xtype.match(/^Nav/)) {
3879 cfg.cls = 'navbar-form navbar-' + this.align;
3883 if (this.labelAlign == 'left' ) {
3884 cfg.cls += ' form-horizontal';
3890 initEvents : function()
3892 this.el.on('submit', this.onSubmit, this);
3897 onSubmit : function(e){
3902 * Returns true if client-side validation on the form is successful.
3905 isValid : function(){
3906 var items = this.getItems();
3908 items.each(function(f){
3917 * Returns true if any fields in this form have changed since their original load.
3920 isDirty : function(){
3922 var items = this.getItems();
3923 items.each(function(f){
3933 * Performs a predefined action (submit or load) or custom actions you define on this form.
3934 * @param {String} actionName The name of the action type
3935 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3936 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3937 * accept other config options):
3939 Property Type Description
3940 ---------------- --------------- ----------------------------------------------------------------------------------
3941 url String The url for the action (defaults to the form's url)
3942 method String The form method to use (defaults to the form's method, or POST if not defined)
3943 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3944 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3945 validate the form on the client (defaults to false)
3947 * @return {BasicForm} this
3949 doAction : function(action, options){
3950 if(typeof action == 'string'){
3951 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3953 if(this.fireEvent('beforeaction', this, action) !== false){
3954 this.beforeAction(action);
3955 action.run.defer(100, action);
3961 beforeAction : function(action){
3962 var o = action.options;
3964 // not really supported yet.. ??
3966 //if(this.waitMsgTarget === true){
3967 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3968 //}else if(this.waitMsgTarget){
3969 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3970 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3972 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3978 afterAction : function(action, success){
3979 this.activeAction = null;
3980 var o = action.options;
3982 //if(this.waitMsgTarget === true){
3984 //}else if(this.waitMsgTarget){
3985 // this.waitMsgTarget.unmask();
3987 // Roo.MessageBox.updateProgress(1);
3988 // Roo.MessageBox.hide();
3995 Roo.callback(o.success, o.scope, [this, action]);
3996 this.fireEvent('actioncomplete', this, action);
4000 // failure condition..
4001 // we have a scenario where updates need confirming.
4002 // eg. if a locking scenario exists..
4003 // we look for { errors : { needs_confirm : true }} in the response.
4005 (typeof(action.result) != 'undefined') &&
4006 (typeof(action.result.errors) != 'undefined') &&
4007 (typeof(action.result.errors.needs_confirm) != 'undefined')
4010 Roo.log("not supported yet");
4013 Roo.MessageBox.confirm(
4014 "Change requires confirmation",
4015 action.result.errorMsg,
4020 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4030 Roo.callback(o.failure, o.scope, [this, action]);
4031 // show an error message if no failed handler is set..
4032 if (!this.hasListener('actionfailed')) {
4033 Roo.log("need to add dialog support");
4035 Roo.MessageBox.alert("Error",
4036 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4037 action.result.errorMsg :
4038 "Saving Failed, please check your entries or try again"
4043 this.fireEvent('actionfailed', this, action);
4048 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4049 * @param {String} id The value to search for
4052 findField : function(id){
4053 var items = this.getItems();
4054 var field = items.get(id);
4056 items.each(function(f){
4057 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4064 return field || null;
4067 * Mark fields in this form invalid in bulk.
4068 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4069 * @return {BasicForm} this
4071 markInvalid : function(errors){
4072 if(errors instanceof Array){
4073 for(var i = 0, len = errors.length; i < len; i++){
4074 var fieldError = errors[i];
4075 var f = this.findField(fieldError.id);
4077 f.markInvalid(fieldError.msg);
4083 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4084 field.markInvalid(errors[id]);
4088 //Roo.each(this.childForms || [], function (f) {
4089 // f.markInvalid(errors);
4096 * Set values for fields in this form in bulk.
4097 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4098 * @return {BasicForm} this
4100 setValues : function(values){
4101 if(values instanceof Array){ // array of objects
4102 for(var i = 0, len = values.length; i < len; i++){
4104 var f = this.findField(v.id);
4106 f.setValue(v.value);
4107 if(this.trackResetOnLoad){
4108 f.originalValue = f.getValue();
4112 }else{ // object hash
4115 if(typeof values[id] != 'function' && (field = this.findField(id))){
4117 if (field.setFromData &&
4119 field.displayField &&
4120 // combos' with local stores can
4121 // be queried via setValue()
4122 // to set their value..
4123 (field.store && !field.store.isLocal)
4127 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4128 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4129 field.setFromData(sd);
4132 field.setValue(values[id]);
4136 if(this.trackResetOnLoad){
4137 field.originalValue = field.getValue();
4143 //Roo.each(this.childForms || [], function (f) {
4144 // f.setValues(values);
4151 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4152 * they are returned as an array.
4153 * @param {Boolean} asString
4156 getValues : function(asString){
4157 //if (this.childForms) {
4158 // copy values from the child forms
4159 // Roo.each(this.childForms, function (f) {
4160 // this.setValues(f.getValues());
4166 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4167 if(asString === true){
4170 return Roo.urlDecode(fs);
4174 * Returns the fields in this form as an object with key/value pairs.
4175 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4178 getFieldValues : function(with_hidden)
4180 var items = this.getItems();
4182 items.each(function(f){
4186 var v = f.getValue();
4187 if (f.inputType =='radio') {
4188 if (typeof(ret[f.getName()]) == 'undefined') {
4189 ret[f.getName()] = ''; // empty..
4192 if (!f.el.dom.checked) {
4200 // not sure if this supported any more..
4201 if ((typeof(v) == 'object') && f.getRawValue) {
4202 v = f.getRawValue() ; // dates..
4204 // combo boxes where name != hiddenName...
4205 if (f.name != f.getName()) {
4206 ret[f.name] = f.getRawValue();
4208 ret[f.getName()] = v;
4215 * Clears all invalid messages in this form.
4216 * @return {BasicForm} this
4218 clearInvalid : function(){
4219 var items = this.getItems();
4221 items.each(function(f){
4232 * @return {BasicForm} this
4235 var items = this.getItems();
4236 items.each(function(f){
4240 Roo.each(this.childForms || [], function (f) {
4247 getItems : function()
4249 var r=new Roo.util.MixedCollection(false, function(o){
4250 return o.id || (o.id = Roo.id());
4252 var iter = function(el) {
4259 Roo.each(el.items,function(e) {
4278 * Ext JS Library 1.1.1
4279 * Copyright(c) 2006-2007, Ext JS, LLC.
4281 * Originally Released Under LGPL - original licence link has changed is not relivant.
4284 * <script type="text/javascript">
4287 * @class Roo.form.VTypes
4288 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4291 Roo.form.VTypes = function(){
4292 // closure these in so they are only created once.
4293 var alpha = /^[a-zA-Z_]+$/;
4294 var alphanum = /^[a-zA-Z0-9_]+$/;
4295 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4296 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4298 // All these messages and functions are configurable
4301 * The function used to validate email addresses
4302 * @param {String} value The email address
4304 'email' : function(v){
4305 return email.test(v);
4308 * The error text to display when the email validation function returns false
4311 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4313 * The keystroke filter mask to be applied on email input
4316 'emailMask' : /[a-z0-9_\.\-@]/i,
4319 * The function used to validate URLs
4320 * @param {String} value The URL
4322 'url' : function(v){
4326 * The error text to display when the url validation function returns false
4329 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4332 * The function used to validate alpha values
4333 * @param {String} value The value
4335 'alpha' : function(v){
4336 return alpha.test(v);
4339 * The error text to display when the alpha validation function returns false
4342 'alphaText' : 'This field should only contain letters and _',
4344 * The keystroke filter mask to be applied on alpha input
4347 'alphaMask' : /[a-z_]/i,
4350 * The function used to validate alphanumeric values
4351 * @param {String} value The value
4353 'alphanum' : function(v){
4354 return alphanum.test(v);
4357 * The error text to display when the alphanumeric validation function returns false
4360 'alphanumText' : 'This field should only contain letters, numbers and _',
4362 * The keystroke filter mask to be applied on alphanumeric input
4365 'alphanumMask' : /[a-z0-9_]/i
4375 * @class Roo.bootstrap.Input
4376 * @extends Roo.bootstrap.Component
4377 * Bootstrap Input class
4378 * @cfg {Boolean} disabled is it disabled
4379 * @cfg {String} fieldLabel - the label associated
4380 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4381 * @cfg {String} name name of the input
4382 * @cfg {string} fieldLabel - the label associated
4383 * @cfg {string} inputType - input / file submit ...
4384 * @cfg {string} placeholder - placeholder to put in text.
4385 * @cfg {string} before - input group add on before
4386 * @cfg {string} after - input group add on after
4387 * @cfg {string} size - (lg|sm) or leave empty..
4388 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4389 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4390 * @cfg {Number} md colspan out of 12 for computer-sized screens
4391 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4392 * @cfg {string} value default value of the input
4393 * @cfg {Number} labelWidth set the width of label (0-12)
4394 * @cfg {String} labelAlign (top|left)
4395 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4399 * Create a new Input
4400 * @param {Object} config The config object
4403 Roo.bootstrap.Input = function(config){
4404 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4409 * Fires when this field receives input focus.
4410 * @param {Roo.form.Field} this
4415 * Fires when this field loses input focus.
4416 * @param {Roo.form.Field} this
4421 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4422 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4423 * @param {Roo.form.Field} this
4424 * @param {Roo.EventObject} e The event object
4429 * Fires just before the field blurs if the field value has changed.
4430 * @param {Roo.form.Field} this
4431 * @param {Mixed} newValue The new value
4432 * @param {Mixed} oldValue The original value
4437 * Fires after the field has been marked as invalid.
4438 * @param {Roo.form.Field} this
4439 * @param {String} msg The validation message
4444 * Fires after the field has been validated with no errors.
4445 * @param {Roo.form.Field} this
4450 * Fires after the key up
4451 * @param {Roo.form.Field} this
4452 * @param {Roo.EventObject} e The event Object
4458 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4460 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4461 automatic validation (defaults to "keyup").
4463 validationEvent : "keyup",
4465 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4467 validateOnBlur : true,
4469 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4471 validationDelay : 250,
4473 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4475 focusClass : "x-form-focus", // not needed???
4479 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4481 invalidClass : "has-error",
4484 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4486 selectOnFocus : false,
4489 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4493 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4498 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4500 disableKeyFilter : false,
4503 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4507 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4511 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4513 blankText : "This field is required",
4516 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4520 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4522 maxLength : Number.MAX_VALUE,
4524 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4526 minLengthText : "The minimum length for this field is {0}",
4528 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4530 maxLengthText : "The maximum length for this field is {0}",
4534 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4535 * If available, this function will be called only after the basic validators all return true, and will be passed the
4536 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4540 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4541 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4542 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4546 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4569 parentLabelAlign : function()
4572 while (parent.parent()) {
4573 parent = parent.parent();
4574 if (typeof(parent.labelAlign) !='undefined') {
4575 return parent.labelAlign;
4582 getAutoCreate : function(){
4584 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4590 if(this.inputType != 'hidden'){
4591 cfg.cls = 'form-group' //input-group
4597 type : this.inputType,
4599 cls : 'form-control',
4600 placeholder : this.placeholder || ''
4604 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4605 input.maxLength = this.maxLength;
4608 if (this.disabled) {
4609 input.disabled=true;
4612 if (this.readOnly) {
4613 input.readonly=true;
4617 input.name = this.name;
4620 input.cls += ' input-' + this.size;
4623 ['xs','sm','md','lg'].map(function(size){
4624 if (settings[size]) {
4625 cfg.cls += ' col-' + size + '-' + settings[size];
4629 var inputblock = input;
4631 if (this.before || this.after) {
4634 cls : 'input-group',
4638 inputblock.cn.push({
4640 cls : 'input-group-addon',
4644 inputblock.cn.push(input);
4646 inputblock.cn.push({
4648 cls : 'input-group-addon',
4655 if (align ==='left' && this.fieldLabel.length) {
4656 Roo.log("left and has label");
4662 cls : 'control-label col-sm-' + this.labelWidth,
4663 html : this.fieldLabel
4667 cls : "col-sm-" + (12 - this.labelWidth),
4674 } else if ( this.fieldLabel.length) {
4680 //cls : 'input-group-addon',
4681 html : this.fieldLabel
4691 Roo.log(" no label && no align");
4700 Roo.log('input-parentType: ' + this.parentType);
4702 if (this.parentType === 'Navbar' && this.parent().bar) {
4703 cfg.cls += ' navbar-form';
4711 * return the real input element.
4713 inputEl: function ()
4715 return this.el.select('input.form-control',true).first();
4717 setDisabled : function(v)
4719 var i = this.inputEl().dom;
4721 i.removeAttribute('disabled');
4725 i.setAttribute('disabled','true');
4727 initEvents : function()
4730 this.inputEl().on("keydown" , this.fireKey, this);
4731 this.inputEl().on("focus", this.onFocus, this);
4732 this.inputEl().on("blur", this.onBlur, this);
4734 this.inputEl().relayEvent('keyup', this);
4736 // reference to original value for reset
4737 this.originalValue = this.getValue();
4738 //Roo.form.TextField.superclass.initEvents.call(this);
4739 if(this.validationEvent == 'keyup'){
4740 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4741 this.inputEl().on('keyup', this.filterValidation, this);
4743 else if(this.validationEvent !== false){
4744 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4747 if(this.selectOnFocus){
4748 this.on("focus", this.preFocus, this);
4751 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4752 this.inputEl().on("keypress", this.filterKeys, this);
4755 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4756 this.el.on("click", this.autoSize, this);
4759 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4760 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4764 filterValidation : function(e){
4765 if(!e.isNavKeyPress()){
4766 this.validationTask.delay(this.validationDelay);
4770 * Validates the field value
4771 * @return {Boolean} True if the value is valid, else false
4773 validate : function(){
4774 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4775 if(this.disabled || this.validateValue(this.getRawValue())){
4776 this.clearInvalid();
4784 * Validates a value according to the field's validation rules and marks the field as invalid
4785 * if the validation fails
4786 * @param {Mixed} value The value to validate
4787 * @return {Boolean} True if the value is valid, else false
4789 validateValue : function(value){
4790 if(value.length < 1) { // if it's blank
4791 if(this.allowBlank){
4792 this.clearInvalid();
4795 this.markInvalid(this.blankText);
4799 if(value.length < this.minLength){
4800 this.markInvalid(String.format(this.minLengthText, this.minLength));
4803 if(value.length > this.maxLength){
4804 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4808 var vt = Roo.form.VTypes;
4809 if(!vt[this.vtype](value, this)){
4810 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4814 if(typeof this.validator == "function"){
4815 var msg = this.validator(value);
4817 this.markInvalid(msg);
4821 if(this.regex && !this.regex.test(value)){
4822 this.markInvalid(this.regexText);
4831 fireKey : function(e){
4832 //Roo.log('field ' + e.getKey());
4833 if(e.isNavKeyPress()){
4834 this.fireEvent("specialkey", this, e);
4837 focus : function (selectText){
4839 this.inputEl().focus();
4840 if(selectText === true){
4841 this.inputEl().dom.select();
4847 onFocus : function(){
4848 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4849 // this.el.addClass(this.focusClass);
4852 this.hasFocus = true;
4853 this.startValue = this.getValue();
4854 this.fireEvent("focus", this);
4858 beforeBlur : Roo.emptyFn,
4862 onBlur : function(){
4864 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4865 //this.el.removeClass(this.focusClass);
4867 this.hasFocus = false;
4868 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4871 var v = this.getValue();
4872 if(String(v) !== String(this.startValue)){
4873 this.fireEvent('change', this, v, this.startValue);
4875 this.fireEvent("blur", this);
4879 * Resets the current field value to the originally loaded value and clears any validation messages
4882 this.setValue(this.originalValue);
4883 this.clearInvalid();
4886 * Returns the name of the field
4887 * @return {Mixed} name The name field
4889 getName: function(){
4893 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4894 * @return {Mixed} value The field value
4896 getValue : function(){
4897 return this.inputEl().getValue();
4900 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4901 * @return {Mixed} value The field value
4903 getRawValue : function(){
4904 var v = this.inputEl().getValue();
4910 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4911 * @param {Mixed} value The value to set
4913 setRawValue : function(v){
4914 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4917 selectText : function(start, end){
4918 var v = this.getRawValue();
4920 start = start === undefined ? 0 : start;
4921 end = end === undefined ? v.length : end;
4922 var d = this.inputEl().dom;
4923 if(d.setSelectionRange){
4924 d.setSelectionRange(start, end);
4925 }else if(d.createTextRange){
4926 var range = d.createTextRange();
4927 range.moveStart("character", start);
4928 range.moveEnd("character", v.length-end);
4935 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4936 * @param {Mixed} value The value to set
4938 setValue : function(v){
4941 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4947 processValue : function(value){
4948 if(this.stripCharsRe){
4949 var newValue = value.replace(this.stripCharsRe, '');
4950 if(newValue !== value){
4951 this.setRawValue(newValue);
4958 preFocus : function(){
4960 if(this.selectOnFocus){
4961 this.inputEl().dom.select();
4964 filterKeys : function(e){
4966 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4969 var c = e.getCharCode(), cc = String.fromCharCode(c);
4970 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4973 if(!this.maskRe.test(cc)){
4978 * Clear any invalid styles/messages for this field
4980 clearInvalid : function(){
4982 if(!this.el || this.preventMark){ // not rendered
4985 this.el.removeClass(this.invalidClass);
4987 switch(this.msgTarget){
4989 this.el.dom.qtip = '';
4992 this.el.dom.title = '';
4996 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5001 this.errorIcon.dom.qtip = '';
5002 this.errorIcon.hide();
5003 this.un('resize', this.alignErrorIcon, this);
5007 var t = Roo.getDom(this.msgTarget);
5009 t.style.display = 'none';
5013 this.fireEvent('valid', this);
5016 * Mark this field as invalid
5017 * @param {String} msg The validation message
5019 markInvalid : function(msg){
5020 if(!this.el || this.preventMark){ // not rendered
5023 this.el.addClass(this.invalidClass);
5025 msg = msg || this.invalidText;
5026 switch(this.msgTarget){
5028 this.el.dom.qtip = msg;
5029 this.el.dom.qclass = 'x-form-invalid-tip';
5030 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5031 Roo.QuickTips.enable();
5035 this.el.dom.title = msg;
5039 var elp = this.el.findParent('.x-form-element', 5, true);
5040 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5041 this.errorEl.setWidth(elp.getWidth(true)-20);
5043 this.errorEl.update(msg);
5044 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5047 if(!this.errorIcon){
5048 var elp = this.el.findParent('.x-form-element', 5, true);
5049 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5051 this.alignErrorIcon();
5052 this.errorIcon.dom.qtip = msg;
5053 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5054 this.errorIcon.show();
5055 this.on('resize', this.alignErrorIcon, this);
5058 var t = Roo.getDom(this.msgTarget);
5060 t.style.display = this.msgDisplay;
5064 this.fireEvent('invalid', this, msg);
5067 SafariOnKeyDown : function(event)
5069 // this is a workaround for a password hang bug on chrome/ webkit.
5071 var isSelectAll = false;
5073 if(this.inputEl().dom.selectionEnd > 0){
5074 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5076 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5077 event.preventDefault();
5082 if(isSelectAll){ // backspace and delete key
5084 event.preventDefault();
5085 // this is very hacky as keydown always get's upper case.
5087 var cc = String.fromCharCode(event.getCharCode());
5088 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5092 adjustWidth : function(tag, w){
5093 tag = tag.toLowerCase();
5094 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5095 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5099 if(tag == 'textarea'){
5102 }else if(Roo.isOpera){
5106 if(tag == 'textarea'){
5125 * @class Roo.bootstrap.TextArea
5126 * @extends Roo.bootstrap.Input
5127 * Bootstrap TextArea class
5128 * @cfg {Number} cols Specifies the visible width of a text area
5129 * @cfg {Number} rows Specifies the visible number of lines in a text area
5130 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5131 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5132 * @cfg {string} html text
5135 * Create a new TextArea
5136 * @param {Object} config The config object
5139 Roo.bootstrap.TextArea = function(config){
5140 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5144 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5154 getAutoCreate : function(){
5156 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5167 value : this.value || '',
5168 html: this.html || '',
5169 cls : 'form-control',
5170 placeholder : this.placeholder || ''
5174 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5175 input.maxLength = this.maxLength;
5179 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5183 input.cols = this.cols;
5186 if (this.readOnly) {
5187 input.readonly = true;
5191 input.name = this.name;
5195 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5199 ['xs','sm','md','lg'].map(function(size){
5200 if (settings[size]) {
5201 cfg.cls += ' col-' + size + '-' + settings[size];
5205 var inputblock = input;
5207 if (this.before || this.after) {
5210 cls : 'input-group',
5214 inputblock.cn.push({
5216 cls : 'input-group-addon',
5220 inputblock.cn.push(input);
5222 inputblock.cn.push({
5224 cls : 'input-group-addon',
5231 if (align ==='left' && this.fieldLabel.length) {
5232 Roo.log("left and has label");
5238 cls : 'control-label col-sm-' + this.labelWidth,
5239 html : this.fieldLabel
5243 cls : "col-sm-" + (12 - this.labelWidth),
5250 } else if ( this.fieldLabel.length) {
5256 //cls : 'input-group-addon',
5257 html : this.fieldLabel
5267 Roo.log(" no label && no align");
5277 if (this.disabled) {
5278 input.disabled=true;
5285 * return the real textarea element.
5287 inputEl: function ()
5289 return this.el.select('textarea.form-control',true).first();
5297 * trigger field - base class for combo..
5302 * @class Roo.bootstrap.TriggerField
5303 * @extends Roo.bootstrap.Input
5304 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5305 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5306 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5307 * for which you can provide a custom implementation. For example:
5309 var trigger = new Roo.bootstrap.TriggerField();
5310 trigger.onTriggerClick = myTriggerFn;
5311 trigger.applyTo('my-field');
5314 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5315 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5316 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5317 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5319 * Create a new TriggerField.
5320 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5321 * to the base TextField)
5323 Roo.bootstrap.TriggerField = function(config){
5324 this.mimicing = false;
5325 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5328 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5330 * @cfg {String} triggerClass A CSS class to apply to the trigger
5333 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5337 /** @cfg {Boolean} grow @hide */
5338 /** @cfg {Number} growMin @hide */
5339 /** @cfg {Number} growMax @hide */
5345 autoSize: Roo.emptyFn,
5352 actionMode : 'wrap',
5356 getAutoCreate : function(){
5358 var parent = this.parent();
5360 var align = this.parentLabelAlign();
5365 cls: 'form-group' //input-group
5372 type : this.inputType,
5373 cls : 'form-control',
5374 autocomplete: 'off',
5375 placeholder : this.placeholder || ''
5379 input.name = this.name;
5382 input.cls += ' input-' + this.size;
5385 if (this.disabled) {
5386 input.disabled=true;
5389 var inputblock = input;
5391 if (this.before || this.after) {
5394 cls : 'input-group',
5398 inputblock.cn.push({
5400 cls : 'input-group-addon',
5404 inputblock.cn.push(input);
5406 inputblock.cn.push({
5408 cls : 'input-group-addon',
5421 cls: 'form-hidden-field'
5429 Roo.log('multiple');
5437 cls: 'form-hidden-field'
5441 cls: 'select2-choices',
5445 cls: 'select2-search-field',
5458 cls: 'select2-container input-group',
5463 cls: 'typeahead typeahead-long dropdown-menu',
5464 style: 'display:none'
5472 cls : 'input-group-addon btn dropdown-toggle',
5480 cls: 'combobox-clear',
5494 combobox.cls += ' select2-container-multi';
5497 if (align ==='left' && this.fieldLabel.length) {
5499 Roo.log("left and has label");
5505 cls : 'control-label col-sm-' + this.labelWidth,
5506 html : this.fieldLabel
5510 cls : "col-sm-" + (12 - this.labelWidth),
5517 } else if ( this.fieldLabel.length) {
5523 //cls : 'input-group-addon',
5524 html : this.fieldLabel
5534 Roo.log(" no label && no align");
5541 ['xs','sm','md','lg'].map(function(size){
5542 if (settings[size]) {
5543 cfg.cls += ' col-' + size + '-' + settings[size];
5554 onResize : function(w, h){
5555 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5556 // if(typeof w == 'number'){
5557 // var x = w - this.trigger.getWidth();
5558 // this.inputEl().setWidth(this.adjustWidth('input', x));
5559 // this.trigger.setStyle('left', x+'px');
5564 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5567 getResizeEl : function(){
5568 return this.inputEl();
5572 getPositionEl : function(){
5573 return this.inputEl();
5577 alignErrorIcon : function(){
5578 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5582 initEvents : function(){
5584 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5585 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5587 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5588 if(this.hideTrigger){
5589 this.trigger.setDisplayed(false);
5591 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5595 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5598 //this.trigger.addClassOnOver('x-form-trigger-over');
5599 //this.trigger.addClassOnClick('x-form-trigger-click');
5602 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5607 initTrigger : function(){
5612 onDestroy : function(){
5614 this.trigger.removeAllListeners();
5615 // this.trigger.remove();
5618 // this.wrap.remove();
5620 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5624 onFocus : function(){
5625 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5628 this.wrap.addClass('x-trigger-wrap-focus');
5629 this.mimicing = true;
5630 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5631 if(this.monitorTab){
5632 this.el.on("keydown", this.checkTab, this);
5639 checkTab : function(e){
5640 if(e.getKey() == e.TAB){
5646 onBlur : function(){
5651 mimicBlur : function(e, t){
5653 if(!this.wrap.contains(t) && this.validateBlur()){
5660 triggerBlur : function(){
5661 this.mimicing = false;
5662 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5663 if(this.monitorTab){
5664 this.el.un("keydown", this.checkTab, this);
5666 //this.wrap.removeClass('x-trigger-wrap-focus');
5667 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5671 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5672 validateBlur : function(e, t){
5677 onDisable : function(){
5678 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5680 // this.wrap.addClass('x-item-disabled');
5685 onEnable : function(){
5686 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5688 // this.el.removeClass('x-item-disabled');
5693 onShow : function(){
5694 var ae = this.getActionEl();
5697 ae.dom.style.display = '';
5698 ae.dom.style.visibility = 'visible';
5704 onHide : function(){
5705 var ae = this.getActionEl();
5706 ae.dom.style.display = 'none';
5710 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5711 * by an implementing function.
5713 * @param {EventObject} e
5715 onTriggerClick : Roo.emptyFn
5719 * Ext JS Library 1.1.1
5720 * Copyright(c) 2006-2007, Ext JS, LLC.
5722 * Originally Released Under LGPL - original licence link has changed is not relivant.
5725 * <script type="text/javascript">
5730 * @class Roo.data.SortTypes
5732 * Defines the default sorting (casting?) comparison functions used when sorting data.
5734 Roo.data.SortTypes = {
5736 * Default sort that does nothing
5737 * @param {Mixed} s The value being converted
5738 * @return {Mixed} The comparison value
5745 * The regular expression used to strip tags
5749 stripTagsRE : /<\/?[^>]+>/gi,
5752 * Strips all HTML tags to sort on text only
5753 * @param {Mixed} s The value being converted
5754 * @return {String} The comparison value
5756 asText : function(s){
5757 return String(s).replace(this.stripTagsRE, "");
5761 * Strips all HTML tags to sort on text only - Case insensitive
5762 * @param {Mixed} s The value being converted
5763 * @return {String} The comparison value
5765 asUCText : function(s){
5766 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5770 * Case insensitive string
5771 * @param {Mixed} s The value being converted
5772 * @return {String} The comparison value
5774 asUCString : function(s) {
5775 return String(s).toUpperCase();
5780 * @param {Mixed} s The value being converted
5781 * @return {Number} The comparison value
5783 asDate : function(s) {
5787 if(s instanceof Date){
5790 return Date.parse(String(s));
5795 * @param {Mixed} s The value being converted
5796 * @return {Float} The comparison value
5798 asFloat : function(s) {
5799 var val = parseFloat(String(s).replace(/,/g, ""));
5800 if(isNaN(val)) val = 0;
5806 * @param {Mixed} s The value being converted
5807 * @return {Number} The comparison value
5809 asInt : function(s) {
5810 var val = parseInt(String(s).replace(/,/g, ""));
5811 if(isNaN(val)) val = 0;
5816 * Ext JS Library 1.1.1
5817 * Copyright(c) 2006-2007, Ext JS, LLC.
5819 * Originally Released Under LGPL - original licence link has changed is not relivant.
5822 * <script type="text/javascript">
5826 * @class Roo.data.Record
5827 * Instances of this class encapsulate both record <em>definition</em> information, and record
5828 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5829 * to access Records cached in an {@link Roo.data.Store} object.<br>
5831 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5832 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5835 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5837 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5838 * {@link #create}. The parameters are the same.
5839 * @param {Array} data An associative Array of data values keyed by the field name.
5840 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5841 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5842 * not specified an integer id is generated.
5844 Roo.data.Record = function(data, id){
5845 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5850 * Generate a constructor for a specific record layout.
5851 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5852 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5853 * Each field definition object may contain the following properties: <ul>
5854 * <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,
5855 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5856 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5857 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5858 * is being used, then this is a string containing the javascript expression to reference the data relative to
5859 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5860 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5861 * this may be omitted.</p></li>
5862 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5863 * <ul><li>auto (Default, implies no conversion)</li>
5868 * <li>date</li></ul></p></li>
5869 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5870 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5871 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5872 * by the Reader into an object that will be stored in the Record. It is passed the
5873 * following parameters:<ul>
5874 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5876 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5878 * <br>usage:<br><pre><code>
5879 var TopicRecord = Roo.data.Record.create(
5880 {name: 'title', mapping: 'topic_title'},
5881 {name: 'author', mapping: 'username'},
5882 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5883 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5884 {name: 'lastPoster', mapping: 'user2'},
5885 {name: 'excerpt', mapping: 'post_text'}
5888 var myNewRecord = new TopicRecord({
5889 title: 'Do my job please',
5892 lastPost: new Date(),
5893 lastPoster: 'Animal',
5894 excerpt: 'No way dude!'
5896 myStore.add(myNewRecord);
5901 Roo.data.Record.create = function(o){
5903 f.superclass.constructor.apply(this, arguments);
5905 Roo.extend(f, Roo.data.Record);
5906 var p = f.prototype;
5907 p.fields = new Roo.util.MixedCollection(false, function(field){
5910 for(var i = 0, len = o.length; i < len; i++){
5911 p.fields.add(new Roo.data.Field(o[i]));
5913 f.getField = function(name){
5914 return p.fields.get(name);
5919 Roo.data.Record.AUTO_ID = 1000;
5920 Roo.data.Record.EDIT = 'edit';
5921 Roo.data.Record.REJECT = 'reject';
5922 Roo.data.Record.COMMIT = 'commit';
5924 Roo.data.Record.prototype = {
5926 * Readonly flag - true if this record has been modified.
5935 join : function(store){
5940 * Set the named field to the specified value.
5941 * @param {String} name The name of the field to set.
5942 * @param {Object} value The value to set the field to.
5944 set : function(name, value){
5945 if(this.data[name] == value){
5952 if(typeof this.modified[name] == 'undefined'){
5953 this.modified[name] = this.data[name];
5955 this.data[name] = value;
5956 if(!this.editing && this.store){
5957 this.store.afterEdit(this);
5962 * Get the value of the named field.
5963 * @param {String} name The name of the field to get the value of.
5964 * @return {Object} The value of the field.
5966 get : function(name){
5967 return this.data[name];
5971 beginEdit : function(){
5972 this.editing = true;
5977 cancelEdit : function(){
5978 this.editing = false;
5979 delete this.modified;
5983 endEdit : function(){
5984 this.editing = false;
5985 if(this.dirty && this.store){
5986 this.store.afterEdit(this);
5991 * Usually called by the {@link Roo.data.Store} which owns the Record.
5992 * Rejects all changes made to the Record since either creation, or the last commit operation.
5993 * Modified fields are reverted to their original values.
5995 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5996 * of reject operations.
5998 reject : function(){
5999 var m = this.modified;
6001 if(typeof m[n] != "function"){
6002 this.data[n] = m[n];
6006 delete this.modified;
6007 this.editing = false;
6009 this.store.afterReject(this);
6014 * Usually called by the {@link Roo.data.Store} which owns the Record.
6015 * Commits all changes made to the Record since either creation, or the last commit operation.
6017 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6018 * of commit operations.
6020 commit : function(){
6022 delete this.modified;
6023 this.editing = false;
6025 this.store.afterCommit(this);
6030 hasError : function(){
6031 return this.error != null;
6035 clearError : function(){
6040 * Creates a copy of this record.
6041 * @param {String} id (optional) A new record id if you don't want to use this record's id
6044 copy : function(newId) {
6045 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6049 * Ext JS Library 1.1.1
6050 * Copyright(c) 2006-2007, Ext JS, LLC.
6052 * Originally Released Under LGPL - original licence link has changed is not relivant.
6055 * <script type="text/javascript">
6061 * @class Roo.data.Store
6062 * @extends Roo.util.Observable
6063 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6064 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6066 * 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
6067 * has no knowledge of the format of the data returned by the Proxy.<br>
6069 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6070 * instances from the data object. These records are cached and made available through accessor functions.
6072 * Creates a new Store.
6073 * @param {Object} config A config object containing the objects needed for the Store to access data,
6074 * and read the data into Records.
6076 Roo.data.Store = function(config){
6077 this.data = new Roo.util.MixedCollection(false);
6078 this.data.getKey = function(o){
6081 this.baseParams = {};
6088 "multisort" : "_multisort"
6091 if(config && config.data){
6092 this.inlineData = config.data;
6096 Roo.apply(this, config);
6098 if(this.reader){ // reader passed
6099 this.reader = Roo.factory(this.reader, Roo.data);
6100 this.reader.xmodule = this.xmodule || false;
6101 if(!this.recordType){
6102 this.recordType = this.reader.recordType;
6104 if(this.reader.onMetaChange){
6105 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6109 if(this.recordType){
6110 this.fields = this.recordType.prototype.fields;
6116 * @event datachanged
6117 * Fires when the data cache has changed, and a widget which is using this Store
6118 * as a Record cache should refresh its view.
6119 * @param {Store} this
6124 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6125 * @param {Store} this
6126 * @param {Object} meta The JSON metadata
6131 * Fires when Records have been added to the Store
6132 * @param {Store} this
6133 * @param {Roo.data.Record[]} records The array of Records added
6134 * @param {Number} index The index at which the record(s) were added
6139 * Fires when a Record has been removed from the Store
6140 * @param {Store} this
6141 * @param {Roo.data.Record} record The Record that was removed
6142 * @param {Number} index The index at which the record was removed
6147 * Fires when a Record has been updated
6148 * @param {Store} this
6149 * @param {Roo.data.Record} record The Record that was updated
6150 * @param {String} operation The update operation being performed. Value may be one of:
6152 Roo.data.Record.EDIT
6153 Roo.data.Record.REJECT
6154 Roo.data.Record.COMMIT
6160 * Fires when the data cache has been cleared.
6161 * @param {Store} this
6166 * Fires before a request is made for a new data object. If the beforeload handler returns false
6167 * the load action will be canceled.
6168 * @param {Store} this
6169 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6173 * @event beforeloadadd
6174 * Fires after a new set of Records has been loaded.
6175 * @param {Store} this
6176 * @param {Roo.data.Record[]} records The Records that were loaded
6177 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6179 beforeloadadd : true,
6182 * Fires after a new set of Records has been loaded, before they are added to the store.
6183 * @param {Store} this
6184 * @param {Roo.data.Record[]} records The Records that were loaded
6185 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6186 * @params {Object} return from reader
6190 * @event loadexception
6191 * Fires if an exception occurs in the Proxy during loading.
6192 * Called with the signature of the Proxy's "loadexception" event.
6193 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6196 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6197 * @param {Object} load options
6198 * @param {Object} jsonData from your request (normally this contains the Exception)
6200 loadexception : true
6204 this.proxy = Roo.factory(this.proxy, Roo.data);
6205 this.proxy.xmodule = this.xmodule || false;
6206 this.relayEvents(this.proxy, ["loadexception"]);
6208 this.sortToggle = {};
6209 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6211 Roo.data.Store.superclass.constructor.call(this);
6213 if(this.inlineData){
6214 this.loadData(this.inlineData);
6215 delete this.inlineData;
6219 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6221 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6222 * without a remote query - used by combo/forms at present.
6226 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6229 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6232 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6233 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6236 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6237 * on any HTTP request
6240 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6243 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6247 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6248 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6253 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6254 * loaded or when a record is removed. (defaults to false).
6256 pruneModifiedRecords : false,
6262 * Add Records to the Store and fires the add event.
6263 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6265 add : function(records){
6266 records = [].concat(records);
6267 for(var i = 0, len = records.length; i < len; i++){
6268 records[i].join(this);
6270 var index = this.data.length;
6271 this.data.addAll(records);
6272 this.fireEvent("add", this, records, index);
6276 * Remove a Record from the Store and fires the remove event.
6277 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6279 remove : function(record){
6280 var index = this.data.indexOf(record);
6281 this.data.removeAt(index);
6282 if(this.pruneModifiedRecords){
6283 this.modified.remove(record);
6285 this.fireEvent("remove", this, record, index);
6289 * Remove all Records from the Store and fires the clear event.
6291 removeAll : function(){
6293 if(this.pruneModifiedRecords){
6296 this.fireEvent("clear", this);
6300 * Inserts Records to the Store at the given index and fires the add event.
6301 * @param {Number} index The start index at which to insert the passed Records.
6302 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6304 insert : function(index, records){
6305 records = [].concat(records);
6306 for(var i = 0, len = records.length; i < len; i++){
6307 this.data.insert(index, records[i]);
6308 records[i].join(this);
6310 this.fireEvent("add", this, records, index);
6314 * Get the index within the cache of the passed Record.
6315 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6316 * @return {Number} The index of the passed Record. Returns -1 if not found.
6318 indexOf : function(record){
6319 return this.data.indexOf(record);
6323 * Get the index within the cache of the Record with the passed id.
6324 * @param {String} id The id of the Record to find.
6325 * @return {Number} The index of the Record. Returns -1 if not found.
6327 indexOfId : function(id){
6328 return this.data.indexOfKey(id);
6332 * Get the Record with the specified id.
6333 * @param {String} id The id of the Record to find.
6334 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6336 getById : function(id){
6337 return this.data.key(id);
6341 * Get the Record at the specified index.
6342 * @param {Number} index The index of the Record to find.
6343 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6345 getAt : function(index){
6346 return this.data.itemAt(index);
6350 * Returns a range of Records between specified indices.
6351 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6352 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6353 * @return {Roo.data.Record[]} An array of Records
6355 getRange : function(start, end){
6356 return this.data.getRange(start, end);
6360 storeOptions : function(o){
6361 o = Roo.apply({}, o);
6364 this.lastOptions = o;
6368 * Loads the Record cache from the configured Proxy using the configured Reader.
6370 * If using remote paging, then the first load call must specify the <em>start</em>
6371 * and <em>limit</em> properties in the options.params property to establish the initial
6372 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6374 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6375 * and this call will return before the new data has been loaded. Perform any post-processing
6376 * in a callback function, or in a "load" event handler.</strong>
6378 * @param {Object} options An object containing properties which control loading options:<ul>
6379 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6380 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6381 * passed the following arguments:<ul>
6382 * <li>r : Roo.data.Record[]</li>
6383 * <li>options: Options object from the load call</li>
6384 * <li>success: Boolean success indicator</li></ul></li>
6385 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6386 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6389 load : function(options){
6390 options = options || {};
6391 if(this.fireEvent("beforeload", this, options) !== false){
6392 this.storeOptions(options);
6393 var p = Roo.apply(options.params || {}, this.baseParams);
6394 // if meta was not loaded from remote source.. try requesting it.
6395 if (!this.reader.metaFromRemote) {
6398 if(this.sortInfo && this.remoteSort){
6399 var pn = this.paramNames;
6400 p[pn["sort"]] = this.sortInfo.field;
6401 p[pn["dir"]] = this.sortInfo.direction;
6403 if (this.multiSort) {
6404 var pn = this.paramNames;
6405 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6408 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6413 * Reloads the Record cache from the configured Proxy using the configured Reader and
6414 * the options from the last load operation performed.
6415 * @param {Object} options (optional) An object containing properties which may override the options
6416 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6417 * the most recently used options are reused).
6419 reload : function(options){
6420 this.load(Roo.applyIf(options||{}, this.lastOptions));
6424 // Called as a callback by the Reader during a load operation.
6425 loadRecords : function(o, options, success){
6426 if(!o || success === false){
6427 if(success !== false){
6428 this.fireEvent("load", this, [], options, o);
6430 if(options.callback){
6431 options.callback.call(options.scope || this, [], options, false);
6435 // if data returned failure - throw an exception.
6436 if (o.success === false) {
6437 // show a message if no listener is registered.
6438 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6439 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6441 // loadmask wil be hooked into this..
6442 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6445 var r = o.records, t = o.totalRecords || r.length;
6447 this.fireEvent("beforeloadadd", this, r, options, o);
6449 if(!options || options.add !== true){
6450 if(this.pruneModifiedRecords){
6453 for(var i = 0, len = r.length; i < len; i++){
6457 this.data = this.snapshot;
6458 delete this.snapshot;
6461 this.data.addAll(r);
6462 this.totalLength = t;
6464 this.fireEvent("datachanged", this);
6466 this.totalLength = Math.max(t, this.data.length+r.length);
6469 this.fireEvent("load", this, r, options, o);
6470 if(options.callback){
6471 options.callback.call(options.scope || this, r, options, true);
6477 * Loads data from a passed data block. A Reader which understands the format of the data
6478 * must have been configured in the constructor.
6479 * @param {Object} data The data block from which to read the Records. The format of the data expected
6480 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6481 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6483 loadData : function(o, append){
6484 var r = this.reader.readRecords(o);
6485 this.loadRecords(r, {add: append}, true);
6489 * Gets the number of cached records.
6491 * <em>If using paging, this may not be the total size of the dataset. If the data object
6492 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6493 * the data set size</em>
6495 getCount : function(){
6496 return this.data.length || 0;
6500 * Gets the total number of records in the dataset as returned by the server.
6502 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6503 * the dataset size</em>
6505 getTotalCount : function(){
6506 return this.totalLength || 0;
6510 * Returns the sort state of the Store as an object with two properties:
6512 field {String} The name of the field by which the Records are sorted
6513 direction {String} The sort order, "ASC" or "DESC"
6516 getSortState : function(){
6517 return this.sortInfo;
6521 applySort : function(){
6522 if(this.sortInfo && !this.remoteSort){
6523 var s = this.sortInfo, f = s.field;
6524 var st = this.fields.get(f).sortType;
6525 var fn = function(r1, r2){
6526 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6527 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6529 this.data.sort(s.direction, fn);
6530 if(this.snapshot && this.snapshot != this.data){
6531 this.snapshot.sort(s.direction, fn);
6537 * Sets the default sort column and order to be used by the next load operation.
6538 * @param {String} fieldName The name of the field to sort by.
6539 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6541 setDefaultSort : function(field, dir){
6542 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6547 * If remote sorting is used, the sort is performed on the server, and the cache is
6548 * reloaded. If local sorting is used, the cache is sorted internally.
6549 * @param {String} fieldName The name of the field to sort by.
6550 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6552 sort : function(fieldName, dir){
6553 var f = this.fields.get(fieldName);
6555 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6557 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6558 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6563 this.sortToggle[f.name] = dir;
6564 this.sortInfo = {field: f.name, direction: dir};
6565 if(!this.remoteSort){
6567 this.fireEvent("datachanged", this);
6569 this.load(this.lastOptions);
6574 * Calls the specified function for each of the Records in the cache.
6575 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6576 * Returning <em>false</em> aborts and exits the iteration.
6577 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6579 each : function(fn, scope){
6580 this.data.each(fn, scope);
6584 * Gets all records modified since the last commit. Modified records are persisted across load operations
6585 * (e.g., during paging).
6586 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6588 getModifiedRecords : function(){
6589 return this.modified;
6593 createFilterFn : function(property, value, anyMatch){
6594 if(!value.exec){ // not a regex
6595 value = String(value);
6596 if(value.length == 0){
6599 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6602 return value.test(r.data[property]);
6607 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6608 * @param {String} property A field on your records
6609 * @param {Number} start The record index to start at (defaults to 0)
6610 * @param {Number} end The last record index to include (defaults to length - 1)
6611 * @return {Number} The sum
6613 sum : function(property, start, end){
6614 var rs = this.data.items, v = 0;
6616 end = (end || end === 0) ? end : rs.length-1;
6618 for(var i = start; i <= end; i++){
6619 v += (rs[i].data[property] || 0);
6625 * Filter the records by a specified property.
6626 * @param {String} field A field on your records
6627 * @param {String/RegExp} value Either a string that the field
6628 * should start with or a RegExp to test against the field
6629 * @param {Boolean} anyMatch True to match any part not just the beginning
6631 filter : function(property, value, anyMatch){
6632 var fn = this.createFilterFn(property, value, anyMatch);
6633 return fn ? this.filterBy(fn) : this.clearFilter();
6637 * Filter by a function. The specified function will be called with each
6638 * record in this data source. If the function returns true the record is included,
6639 * otherwise it is filtered.
6640 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6641 * @param {Object} scope (optional) The scope of the function (defaults to this)
6643 filterBy : function(fn, scope){
6644 this.snapshot = this.snapshot || this.data;
6645 this.data = this.queryBy(fn, scope||this);
6646 this.fireEvent("datachanged", this);
6650 * Query the records by a specified property.
6651 * @param {String} field A field on your records
6652 * @param {String/RegExp} value Either a string that the field
6653 * should start with or a RegExp to test against the field
6654 * @param {Boolean} anyMatch True to match any part not just the beginning
6655 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6657 query : function(property, value, anyMatch){
6658 var fn = this.createFilterFn(property, value, anyMatch);
6659 return fn ? this.queryBy(fn) : this.data.clone();
6663 * Query by a function. The specified function will be called with each
6664 * record in this data source. If the function returns true the record is included
6666 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6667 * @param {Object} scope (optional) The scope of the function (defaults to this)
6668 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6670 queryBy : function(fn, scope){
6671 var data = this.snapshot || this.data;
6672 return data.filterBy(fn, scope||this);
6676 * Collects unique values for a particular dataIndex from this store.
6677 * @param {String} dataIndex The property to collect
6678 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6679 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6680 * @return {Array} An array of the unique values
6682 collect : function(dataIndex, allowNull, bypassFilter){
6683 var d = (bypassFilter === true && this.snapshot) ?
6684 this.snapshot.items : this.data.items;
6685 var v, sv, r = [], l = {};
6686 for(var i = 0, len = d.length; i < len; i++){
6687 v = d[i].data[dataIndex];
6689 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6698 * Revert to a view of the Record cache with no filtering applied.
6699 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6701 clearFilter : function(suppressEvent){
6702 if(this.snapshot && this.snapshot != this.data){
6703 this.data = this.snapshot;
6704 delete this.snapshot;
6705 if(suppressEvent !== true){
6706 this.fireEvent("datachanged", this);
6712 afterEdit : function(record){
6713 if(this.modified.indexOf(record) == -1){
6714 this.modified.push(record);
6716 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6720 afterReject : function(record){
6721 this.modified.remove(record);
6722 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6726 afterCommit : function(record){
6727 this.modified.remove(record);
6728 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6732 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6733 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6735 commitChanges : function(){
6736 var m = this.modified.slice(0);
6738 for(var i = 0, len = m.length; i < len; i++){
6744 * Cancel outstanding changes on all changed records.
6746 rejectChanges : function(){
6747 var m = this.modified.slice(0);
6749 for(var i = 0, len = m.length; i < len; i++){
6754 onMetaChange : function(meta, rtype, o){
6755 this.recordType = rtype;
6756 this.fields = rtype.prototype.fields;
6757 delete this.snapshot;
6758 this.sortInfo = meta.sortInfo || this.sortInfo;
6760 this.fireEvent('metachange', this, this.reader.meta);
6763 moveIndex : function(data, type)
6765 var index = this.indexOf(data);
6767 var newIndex = index + type;
6771 this.insert(newIndex, data);
6776 * Ext JS Library 1.1.1
6777 * Copyright(c) 2006-2007, Ext JS, LLC.
6779 * Originally Released Under LGPL - original licence link has changed is not relivant.
6782 * <script type="text/javascript">
6786 * @class Roo.data.SimpleStore
6787 * @extends Roo.data.Store
6788 * Small helper class to make creating Stores from Array data easier.
6789 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6790 * @cfg {Array} fields An array of field definition objects, or field name strings.
6791 * @cfg {Array} data The multi-dimensional array of data
6793 * @param {Object} config
6795 Roo.data.SimpleStore = function(config){
6796 Roo.data.SimpleStore.superclass.constructor.call(this, {
6798 reader: new Roo.data.ArrayReader({
6801 Roo.data.Record.create(config.fields)
6803 proxy : new Roo.data.MemoryProxy(config.data)
6807 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6809 * Ext JS Library 1.1.1
6810 * Copyright(c) 2006-2007, Ext JS, LLC.
6812 * Originally Released Under LGPL - original licence link has changed is not relivant.
6815 * <script type="text/javascript">
6820 * @extends Roo.data.Store
6821 * @class Roo.data.JsonStore
6822 * Small helper class to make creating Stores for JSON data easier. <br/>
6824 var store = new Roo.data.JsonStore({
6825 url: 'get-images.php',
6827 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6830 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6831 * JsonReader and HttpProxy (unless inline data is provided).</b>
6832 * @cfg {Array} fields An array of field definition objects, or field name strings.
6834 * @param {Object} config
6836 Roo.data.JsonStore = function(c){
6837 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6838 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6839 reader: new Roo.data.JsonReader(c, c.fields)
6842 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6844 * Ext JS Library 1.1.1
6845 * Copyright(c) 2006-2007, Ext JS, LLC.
6847 * Originally Released Under LGPL - original licence link has changed is not relivant.
6850 * <script type="text/javascript">
6854 Roo.data.Field = function(config){
6855 if(typeof config == "string"){
6856 config = {name: config};
6858 Roo.apply(this, config);
6864 var st = Roo.data.SortTypes;
6865 // named sortTypes are supported, here we look them up
6866 if(typeof this.sortType == "string"){
6867 this.sortType = st[this.sortType];
6870 // set default sortType for strings and dates
6874 this.sortType = st.asUCString;
6877 this.sortType = st.asDate;
6880 this.sortType = st.none;
6885 var stripRe = /[\$,%]/g;
6887 // prebuilt conversion function for this field, instead of
6888 // switching every time we're reading a value
6890 var cv, dateFormat = this.dateFormat;
6895 cv = function(v){ return v; };
6898 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6902 return v !== undefined && v !== null && v !== '' ?
6903 parseInt(String(v).replace(stripRe, ""), 10) : '';
6908 return v !== undefined && v !== null && v !== '' ?
6909 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6914 cv = function(v){ return v === true || v === "true" || v == 1; };
6921 if(v instanceof Date){
6925 if(dateFormat == "timestamp"){
6926 return new Date(v*1000);
6928 return Date.parseDate(v, dateFormat);
6930 var parsed = Date.parse(v);
6931 return parsed ? new Date(parsed) : null;
6940 Roo.data.Field.prototype = {
6948 * Ext JS Library 1.1.1
6949 * Copyright(c) 2006-2007, Ext JS, LLC.
6951 * Originally Released Under LGPL - original licence link has changed is not relivant.
6954 * <script type="text/javascript">
6957 // Base class for reading structured data from a data source. This class is intended to be
6958 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6961 * @class Roo.data.DataReader
6962 * Base class for reading structured data from a data source. This class is intended to be
6963 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6966 Roo.data.DataReader = function(meta, recordType){
6970 this.recordType = recordType instanceof Array ?
6971 Roo.data.Record.create(recordType) : recordType;
6974 Roo.data.DataReader.prototype = {
6976 * Create an empty record
6977 * @param {Object} data (optional) - overlay some values
6978 * @return {Roo.data.Record} record created.
6980 newRow : function(d) {
6982 this.recordType.prototype.fields.each(function(c) {
6984 case 'int' : da[c.name] = 0; break;
6985 case 'date' : da[c.name] = new Date(); break;
6986 case 'float' : da[c.name] = 0.0; break;
6987 case 'boolean' : da[c.name] = false; break;
6988 default : da[c.name] = ""; break;
6992 return new this.recordType(Roo.apply(da, d));
6997 * Ext JS Library 1.1.1
6998 * Copyright(c) 2006-2007, Ext JS, LLC.
7000 * Originally Released Under LGPL - original licence link has changed is not relivant.
7003 * <script type="text/javascript">
7007 * @class Roo.data.DataProxy
7008 * @extends Roo.data.Observable
7009 * This class is an abstract base class for implementations which provide retrieval of
7010 * unformatted data objects.<br>
7012 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7013 * (of the appropriate type which knows how to parse the data object) to provide a block of
7014 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7016 * Custom implementations must implement the load method as described in
7017 * {@link Roo.data.HttpProxy#load}.
7019 Roo.data.DataProxy = function(){
7023 * Fires before a network request is made to retrieve a data object.
7024 * @param {Object} This DataProxy object.
7025 * @param {Object} params The params parameter to the load function.
7030 * Fires before the load method's callback is called.
7031 * @param {Object} This DataProxy object.
7032 * @param {Object} o The data object.
7033 * @param {Object} arg The callback argument object passed to the load function.
7037 * @event loadexception
7038 * Fires if an Exception occurs during data retrieval.
7039 * @param {Object} This DataProxy object.
7040 * @param {Object} o The data object.
7041 * @param {Object} arg The callback argument object passed to the load function.
7042 * @param {Object} e The Exception.
7044 loadexception : true
7046 Roo.data.DataProxy.superclass.constructor.call(this);
7049 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7052 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7056 * Ext JS Library 1.1.1
7057 * Copyright(c) 2006-2007, Ext JS, LLC.
7059 * Originally Released Under LGPL - original licence link has changed is not relivant.
7062 * <script type="text/javascript">
7065 * @class Roo.data.MemoryProxy
7066 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7067 * to the Reader when its load method is called.
7069 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7071 Roo.data.MemoryProxy = function(data){
7075 Roo.data.MemoryProxy.superclass.constructor.call(this);
7079 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7081 * Load data from the requested source (in this case an in-memory
7082 * data object passed to the constructor), read the data object into
7083 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7084 * process that block using the passed callback.
7085 * @param {Object} params This parameter is not used by the MemoryProxy class.
7086 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7087 * object into a block of Roo.data.Records.
7088 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7089 * The function must be passed <ul>
7090 * <li>The Record block object</li>
7091 * <li>The "arg" argument from the load function</li>
7092 * <li>A boolean success indicator</li>
7094 * @param {Object} scope The scope in which to call the callback
7095 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7097 load : function(params, reader, callback, scope, arg){
7098 params = params || {};
7101 result = reader.readRecords(this.data);
7103 this.fireEvent("loadexception", this, arg, null, e);
7104 callback.call(scope, null, arg, false);
7107 callback.call(scope, result, arg, true);
7111 update : function(params, records){
7116 * Ext JS Library 1.1.1
7117 * Copyright(c) 2006-2007, Ext JS, LLC.
7119 * Originally Released Under LGPL - original licence link has changed is not relivant.
7122 * <script type="text/javascript">
7125 * @class Roo.data.HttpProxy
7126 * @extends Roo.data.DataProxy
7127 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7128 * configured to reference a certain URL.<br><br>
7130 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7131 * from which the running page was served.<br><br>
7133 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7135 * Be aware that to enable the browser to parse an XML document, the server must set
7136 * the Content-Type header in the HTTP response to "text/xml".
7138 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7139 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7140 * will be used to make the request.
7142 Roo.data.HttpProxy = function(conn){
7143 Roo.data.HttpProxy.superclass.constructor.call(this);
7144 // is conn a conn config or a real conn?
7146 this.useAjax = !conn || !conn.events;
7150 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7151 // thse are take from connection...
7154 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7157 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7158 * extra parameters to each request made by this object. (defaults to undefined)
7161 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7162 * to each request made by this object. (defaults to undefined)
7165 * @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)
7168 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7171 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7177 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7181 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7182 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7183 * a finer-grained basis than the DataProxy events.
7185 getConnection : function(){
7186 return this.useAjax ? Roo.Ajax : this.conn;
7190 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7191 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7192 * process that block using the passed callback.
7193 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7194 * for the request to the remote server.
7195 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7196 * object into a block of Roo.data.Records.
7197 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7198 * The function must be passed <ul>
7199 * <li>The Record block object</li>
7200 * <li>The "arg" argument from the load function</li>
7201 * <li>A boolean success indicator</li>
7203 * @param {Object} scope The scope in which to call the callback
7204 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7206 load : function(params, reader, callback, scope, arg){
7207 if(this.fireEvent("beforeload", this, params) !== false){
7209 params : params || {},
7211 callback : callback,
7216 callback : this.loadResponse,
7220 Roo.applyIf(o, this.conn);
7221 if(this.activeRequest){
7222 Roo.Ajax.abort(this.activeRequest);
7224 this.activeRequest = Roo.Ajax.request(o);
7226 this.conn.request(o);
7229 callback.call(scope||this, null, arg, false);
7234 loadResponse : function(o, success, response){
7235 delete this.activeRequest;
7237 this.fireEvent("loadexception", this, o, response);
7238 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7243 result = o.reader.read(response);
7245 this.fireEvent("loadexception", this, o, response, e);
7246 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7250 this.fireEvent("load", this, o, o.request.arg);
7251 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7255 update : function(dataSet){
7260 updateResponse : function(dataSet){
7265 * Ext JS Library 1.1.1
7266 * Copyright(c) 2006-2007, Ext JS, LLC.
7268 * Originally Released Under LGPL - original licence link has changed is not relivant.
7271 * <script type="text/javascript">
7275 * @class Roo.data.ScriptTagProxy
7276 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7277 * other than the originating domain of the running page.<br><br>
7279 * <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
7280 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7282 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7283 * source code that is used as the source inside a <script> tag.<br><br>
7285 * In order for the browser to process the returned data, the server must wrap the data object
7286 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7287 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7288 * depending on whether the callback name was passed:
7291 boolean scriptTag = false;
7292 String cb = request.getParameter("callback");
7295 response.setContentType("text/javascript");
7297 response.setContentType("application/x-json");
7299 Writer out = response.getWriter();
7301 out.write(cb + "(");
7303 out.print(dataBlock.toJsonString());
7310 * @param {Object} config A configuration object.
7312 Roo.data.ScriptTagProxy = function(config){
7313 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7314 Roo.apply(this, config);
7315 this.head = document.getElementsByTagName("head")[0];
7318 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7320 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7322 * @cfg {String} url The URL from which to request the data object.
7325 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7329 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7330 * the server the name of the callback function set up by the load call to process the returned data object.
7331 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7332 * javascript output which calls this named function passing the data object as its only parameter.
7334 callbackParam : "callback",
7336 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7337 * name to the request.
7342 * Load data from the configured URL, read the data object into
7343 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7344 * process that block using the passed callback.
7345 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7346 * for the request to the remote server.
7347 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7348 * object into a block of Roo.data.Records.
7349 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7350 * The function must be passed <ul>
7351 * <li>The Record block object</li>
7352 * <li>The "arg" argument from the load function</li>
7353 * <li>A boolean success indicator</li>
7355 * @param {Object} scope The scope in which to call the callback
7356 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7358 load : function(params, reader, callback, scope, arg){
7359 if(this.fireEvent("beforeload", this, params) !== false){
7361 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7364 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7366 url += "&_dc=" + (new Date().getTime());
7368 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7371 cb : "stcCallback"+transId,
7372 scriptId : "stcScript"+transId,
7376 callback : callback,
7382 window[trans.cb] = function(o){
7383 conn.handleResponse(o, trans);
7386 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7388 if(this.autoAbort !== false){
7392 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7394 var script = document.createElement("script");
7395 script.setAttribute("src", url);
7396 script.setAttribute("type", "text/javascript");
7397 script.setAttribute("id", trans.scriptId);
7398 this.head.appendChild(script);
7402 callback.call(scope||this, null, arg, false);
7407 isLoading : function(){
7408 return this.trans ? true : false;
7412 * Abort the current server request.
7415 if(this.isLoading()){
7416 this.destroyTrans(this.trans);
7421 destroyTrans : function(trans, isLoaded){
7422 this.head.removeChild(document.getElementById(trans.scriptId));
7423 clearTimeout(trans.timeoutId);
7425 window[trans.cb] = undefined;
7427 delete window[trans.cb];
7430 // if hasn't been loaded, wait for load to remove it to prevent script error
7431 window[trans.cb] = function(){
7432 window[trans.cb] = undefined;
7434 delete window[trans.cb];
7441 handleResponse : function(o, trans){
7443 this.destroyTrans(trans, true);
7446 result = trans.reader.readRecords(o);
7448 this.fireEvent("loadexception", this, o, trans.arg, e);
7449 trans.callback.call(trans.scope||window, null, trans.arg, false);
7452 this.fireEvent("load", this, o, trans.arg);
7453 trans.callback.call(trans.scope||window, result, trans.arg, true);
7457 handleFailure : function(trans){
7459 this.destroyTrans(trans, false);
7460 this.fireEvent("loadexception", this, null, trans.arg);
7461 trans.callback.call(trans.scope||window, null, trans.arg, false);
7465 * Ext JS Library 1.1.1
7466 * Copyright(c) 2006-2007, Ext JS, LLC.
7468 * Originally Released Under LGPL - original licence link has changed is not relivant.
7471 * <script type="text/javascript">
7475 * @class Roo.data.JsonReader
7476 * @extends Roo.data.DataReader
7477 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7478 * based on mappings in a provided Roo.data.Record constructor.
7480 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7481 * in the reply previously.
7486 var RecordDef = Roo.data.Record.create([
7487 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7488 {name: 'occupation'} // This field will use "occupation" as the mapping.
7490 var myReader = new Roo.data.JsonReader({
7491 totalProperty: "results", // The property which contains the total dataset size (optional)
7492 root: "rows", // The property which contains an Array of row objects
7493 id: "id" // The property within each row object that provides an ID for the record (optional)
7497 * This would consume a JSON file like this:
7499 { 'results': 2, 'rows': [
7500 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7501 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7504 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7505 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7506 * paged from the remote server.
7507 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7508 * @cfg {String} root name of the property which contains the Array of row objects.
7509 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7511 * Create a new JsonReader
7512 * @param {Object} meta Metadata configuration options
7513 * @param {Object} recordType Either an Array of field definition objects,
7514 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7516 Roo.data.JsonReader = function(meta, recordType){
7519 // set some defaults:
7521 totalProperty: 'total',
7522 successProperty : 'success',
7527 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7529 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7532 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7533 * Used by Store query builder to append _requestMeta to params.
7536 metaFromRemote : false,
7538 * This method is only used by a DataProxy which has retrieved data from a remote server.
7539 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7540 * @return {Object} data A data block which is used by an Roo.data.Store object as
7541 * a cache of Roo.data.Records.
7543 read : function(response){
7544 var json = response.responseText;
7546 var o = /* eval:var:o */ eval("("+json+")");
7548 throw {message: "JsonReader.read: Json object not found"};
7554 this.metaFromRemote = true;
7555 this.meta = o.metaData;
7556 this.recordType = Roo.data.Record.create(o.metaData.fields);
7557 this.onMetaChange(this.meta, this.recordType, o);
7559 return this.readRecords(o);
7562 // private function a store will implement
7563 onMetaChange : function(meta, recordType, o){
7570 simpleAccess: function(obj, subsc) {
7577 getJsonAccessor: function(){
7579 return function(expr) {
7581 return(re.test(expr))
7582 ? new Function("obj", "return obj." + expr)
7592 * Create a data block containing Roo.data.Records from an XML document.
7593 * @param {Object} o An object which contains an Array of row objects in the property specified
7594 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7595 * which contains the total size of the dataset.
7596 * @return {Object} data A data block which is used by an Roo.data.Store object as
7597 * a cache of Roo.data.Records.
7599 readRecords : function(o){
7601 * After any data loads, the raw JSON data is available for further custom processing.
7605 var s = this.meta, Record = this.recordType,
7606 f = Record.prototype.fields, fi = f.items, fl = f.length;
7608 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7610 if(s.totalProperty) {
7611 this.getTotal = this.getJsonAccessor(s.totalProperty);
7613 if(s.successProperty) {
7614 this.getSuccess = this.getJsonAccessor(s.successProperty);
7616 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7618 var g = this.getJsonAccessor(s.id);
7619 this.getId = function(rec) {
7621 return (r === undefined || r === "") ? null : r;
7624 this.getId = function(){return null;};
7627 for(var jj = 0; jj < fl; jj++){
7629 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7630 this.ef[jj] = this.getJsonAccessor(map);
7634 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7635 if(s.totalProperty){
7636 var vt = parseInt(this.getTotal(o), 10);
7641 if(s.successProperty){
7642 var vs = this.getSuccess(o);
7643 if(vs === false || vs === 'false'){
7648 for(var i = 0; i < c; i++){
7651 var id = this.getId(n);
7652 for(var j = 0; j < fl; j++){
7654 var v = this.ef[j](n);
7656 Roo.log('missing convert for ' + f.name);
7660 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7662 var record = new Record(values, id);
7664 records[i] = record;
7670 totalRecords : totalRecords
7675 * Ext JS Library 1.1.1
7676 * Copyright(c) 2006-2007, Ext JS, LLC.
7678 * Originally Released Under LGPL - original licence link has changed is not relivant.
7681 * <script type="text/javascript">
7685 * @class Roo.data.ArrayReader
7686 * @extends Roo.data.DataReader
7687 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7688 * Each element of that Array represents a row of data fields. The
7689 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7690 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7694 var RecordDef = Roo.data.Record.create([
7695 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7696 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7698 var myReader = new Roo.data.ArrayReader({
7699 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7703 * This would consume an Array like this:
7705 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7707 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7709 * Create a new JsonReader
7710 * @param {Object} meta Metadata configuration options.
7711 * @param {Object} recordType Either an Array of field definition objects
7712 * as specified to {@link Roo.data.Record#create},
7713 * or an {@link Roo.data.Record} object
7714 * created using {@link Roo.data.Record#create}.
7716 Roo.data.ArrayReader = function(meta, recordType){
7717 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7720 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7722 * Create a data block containing Roo.data.Records from an XML document.
7723 * @param {Object} o An Array of row objects which represents the dataset.
7724 * @return {Object} data A data block which is used by an Roo.data.Store object as
7725 * a cache of Roo.data.Records.
7727 readRecords : function(o){
7728 var sid = this.meta ? this.meta.id : null;
7729 var recordType = this.recordType, fields = recordType.prototype.fields;
7732 for(var i = 0; i < root.length; i++){
7735 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7736 for(var j = 0, jlen = fields.length; j < jlen; j++){
7737 var f = fields.items[j];
7738 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7739 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7743 var record = new recordType(values, id);
7745 records[records.length] = record;
7749 totalRecords : records.length
7758 * @class Roo.bootstrap.ComboBox
7759 * @extends Roo.bootstrap.TriggerField
7760 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7761 * @cfg {Boolean} append (true|false) default false
7763 * Create a new ComboBox.
7764 * @param {Object} config Configuration options
7766 Roo.bootstrap.ComboBox = function(config){
7767 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7771 * Fires when the dropdown list is expanded
7772 * @param {Roo.bootstrap.ComboBox} combo This combo box
7777 * Fires when the dropdown list is collapsed
7778 * @param {Roo.bootstrap.ComboBox} combo This combo box
7782 * @event beforeselect
7783 * Fires before a list item is selected. Return false to cancel the selection.
7784 * @param {Roo.bootstrap.ComboBox} combo This combo box
7785 * @param {Roo.data.Record} record The data record returned from the underlying store
7786 * @param {Number} index The index of the selected item in the dropdown list
7788 'beforeselect' : true,
7791 * Fires when a list item is selected
7792 * @param {Roo.bootstrap.ComboBox} combo This combo box
7793 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7794 * @param {Number} index The index of the selected item in the dropdown list
7798 * @event beforequery
7799 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7800 * The event object passed has these properties:
7801 * @param {Roo.bootstrap.ComboBox} combo This combo box
7802 * @param {String} query The query
7803 * @param {Boolean} forceAll true to force "all" query
7804 * @param {Boolean} cancel true to cancel the query
7805 * @param {Object} e The query event object
7807 'beforequery': true,
7810 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7811 * @param {Roo.bootstrap.ComboBox} combo This combo box
7816 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7817 * @param {Roo.bootstrap.ComboBox} combo This combo box
7818 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7823 * Fires when the remove value from the combobox array
7824 * @param {Roo.bootstrap.ComboBox} combo This combo box
7831 this.selectedIndex = -1;
7832 if(this.mode == 'local'){
7833 if(config.queryDelay === undefined){
7834 this.queryDelay = 10;
7836 if(config.minChars === undefined){
7842 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7845 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7846 * rendering into an Roo.Editor, defaults to false)
7849 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7850 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7853 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7856 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7857 * the dropdown list (defaults to undefined, with no header element)
7861 * @cfg {String/Roo.Template} tpl The template to use to render the output
7865 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7867 listWidth: undefined,
7869 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7870 * mode = 'remote' or 'text' if mode = 'local')
7872 displayField: undefined,
7874 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7875 * mode = 'remote' or 'value' if mode = 'local').
7876 * Note: use of a valueField requires the user make a selection
7877 * in order for a value to be mapped.
7879 valueField: undefined,
7883 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7884 * field's data value (defaults to the underlying DOM element's name)
7886 hiddenName: undefined,
7888 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7892 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7894 selectedClass: 'active',
7897 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7901 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7902 * anchor positions (defaults to 'tl-bl')
7904 listAlign: 'tl-bl?',
7906 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7910 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7911 * query specified by the allQuery config option (defaults to 'query')
7913 triggerAction: 'query',
7915 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7916 * (defaults to 4, does not apply if editable = false)
7920 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7921 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7925 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7926 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7930 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7931 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7935 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7936 * when editable = true (defaults to false)
7938 selectOnFocus:false,
7940 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7942 queryParam: 'query',
7944 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7945 * when mode = 'remote' (defaults to 'Loading...')
7947 loadingText: 'Loading...',
7949 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7953 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7957 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7958 * traditional select (defaults to true)
7962 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7966 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7970 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7971 * listWidth has a higher value)
7975 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7976 * allow the user to set arbitrary text into the field (defaults to false)
7978 forceSelection:false,
7980 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7981 * if typeAhead = true (defaults to 250)
7983 typeAheadDelay : 250,
7985 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7986 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7988 valueNotFoundText : undefined,
7990 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7995 * @cfg {Boolean} disableClear Disable showing of clear button.
7997 disableClear : false,
7999 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
8001 alwaysQuery : false,
8004 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8018 // element that contains real text value.. (when hidden is used..)
8021 initEvents: function(){
8024 throw "can not find store for combo";
8026 this.store = Roo.factory(this.store, Roo.data);
8030 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8033 if(this.hiddenName){
8035 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8037 this.hiddenField.dom.value =
8038 this.hiddenValue !== undefined ? this.hiddenValue :
8039 this.value !== undefined ? this.value : '';
8041 // prevent input submission
8042 this.el.dom.removeAttribute('name');
8043 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8048 // this.el.dom.setAttribute('autocomplete', 'off');
8051 var cls = 'x-combo-list';
8052 this.list = this.el.select('ul.dropdown-menu',true).first();
8054 //this.list = new Roo.Layer({
8055 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8058 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8059 this.list.setWidth(lw);
8061 this.list.on('mouseover', this.onViewOver, this);
8062 this.list.on('mousemove', this.onViewMove, this);
8064 this.list.on('scroll', this.onViewScroll, this);
8067 this.list.swallowEvent('mousewheel');
8068 this.assetHeight = 0;
8071 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8072 this.assetHeight += this.header.getHeight();
8075 this.innerList = this.list.createChild({cls:cls+'-inner'});
8076 this.innerList.on('mouseover', this.onViewOver, this);
8077 this.innerList.on('mousemove', this.onViewMove, this);
8078 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8080 if(this.allowBlank && !this.pageSize && !this.disableClear){
8081 this.footer = this.list.createChild({cls:cls+'-ft'});
8082 this.pageTb = new Roo.Toolbar(this.footer);
8086 this.footer = this.list.createChild({cls:cls+'-ft'});
8087 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8088 {pageSize: this.pageSize});
8092 if (this.pageTb && this.allowBlank && !this.disableClear) {
8094 this.pageTb.add(new Roo.Toolbar.Fill(), {
8095 cls: 'x-btn-icon x-btn-clear',
8101 _this.onSelect(false, -1);
8106 this.assetHeight += this.footer.getHeight();
8111 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8114 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8115 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8117 //this.view.wrapEl.setDisplayed(false);
8118 this.view.on('click', this.onViewClick, this);
8122 this.store.on('beforeload', this.onBeforeLoad, this);
8123 this.store.on('load', this.onLoad, this);
8124 this.store.on('loadexception', this.onLoadException, this);
8127 this.resizer = new Roo.Resizable(this.list, {
8128 pinned:true, handles:'se'
8130 this.resizer.on('resize', function(r, w, h){
8131 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8133 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8134 this.restrictHeight();
8136 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8140 this.editable = true;
8141 this.setEditable(false);
8146 if (typeof(this.events.add.listeners) != 'undefined') {
8148 this.addicon = this.wrap.createChild(
8149 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8151 this.addicon.on('click', function(e) {
8152 this.fireEvent('add', this);
8155 if (typeof(this.events.edit.listeners) != 'undefined') {
8157 this.editicon = this.wrap.createChild(
8158 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8160 this.editicon.setStyle('margin-left', '40px');
8162 this.editicon.on('click', function(e) {
8164 // we fire even if inothing is selected..
8165 this.fireEvent('edit', this, this.lastData );
8171 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8173 this.inKeyMode = true;
8177 "down" : function(e){
8178 if(!this.isExpanded()){
8179 this.onTriggerClick();
8181 this.inKeyMode = true;
8186 "enter" : function(e){
8191 "esc" : function(e){
8195 "tab" : function(e){
8198 if(this.fireEvent("specialkey", this, e)){
8199 this.onViewClick(false);
8207 doRelay : function(foo, bar, hname){
8208 if(hname == 'down' || this.scope.isExpanded()){
8209 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8218 this.queryDelay = Math.max(this.queryDelay || 10,
8219 this.mode == 'local' ? 10 : 250);
8222 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8225 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8227 if(this.editable !== false){
8228 this.inputEl().on("keyup", this.onKeyUp, this);
8230 if(this.forceSelection){
8231 this.on('blur', this.doForce, this);
8235 this.choices = this.el.select('ul.select2-choices', true).first();
8236 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8240 onDestroy : function(){
8242 this.view.setStore(null);
8243 this.view.el.removeAllListeners();
8244 this.view.el.remove();
8245 this.view.purgeListeners();
8248 this.list.dom.innerHTML = '';
8251 this.store.un('beforeload', this.onBeforeLoad, this);
8252 this.store.un('load', this.onLoad, this);
8253 this.store.un('loadexception', this.onLoadException, this);
8255 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8259 fireKey : function(e){
8260 if(e.isNavKeyPress() && !this.list.isVisible()){
8261 this.fireEvent("specialkey", this, e);
8266 onResize: function(w, h){
8267 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8269 // if(typeof w != 'number'){
8270 // // we do not handle it!?!?
8273 // var tw = this.trigger.getWidth();
8274 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8275 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8277 // this.inputEl().setWidth( this.adjustWidth('input', x));
8279 // //this.trigger.setStyle('left', x+'px');
8281 // if(this.list && this.listWidth === undefined){
8282 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8283 // this.list.setWidth(lw);
8284 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8292 * Allow or prevent the user from directly editing the field text. If false is passed,
8293 * the user will only be able to select from the items defined in the dropdown list. This method
8294 * is the runtime equivalent of setting the 'editable' config option at config time.
8295 * @param {Boolean} value True to allow the user to directly edit the field text
8297 setEditable : function(value){
8298 if(value == this.editable){
8301 this.editable = value;
8303 this.inputEl().dom.setAttribute('readOnly', true);
8304 this.inputEl().on('mousedown', this.onTriggerClick, this);
8305 this.inputEl().addClass('x-combo-noedit');
8307 this.inputEl().dom.setAttribute('readOnly', false);
8308 this.inputEl().un('mousedown', this.onTriggerClick, this);
8309 this.inputEl().removeClass('x-combo-noedit');
8315 onBeforeLoad : function(combo,opts){
8320 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8322 this.restrictHeight();
8323 this.selectedIndex = -1;
8327 onLoad : function(){
8329 this.hasQuery = false;
8335 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8336 this.loading.hide();
8339 if(this.store.getCount() > 0){
8341 this.restrictHeight();
8342 if(this.lastQuery == this.allQuery){
8344 this.inputEl().dom.select();
8346 if(!this.selectByValue(this.value, true)){
8347 this.select(0, true);
8351 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8352 this.taTask.delay(this.typeAheadDelay);
8356 this.onEmptyResults();
8362 onLoadException : function()
8364 this.hasQuery = false;
8366 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8367 this.loading.hide();
8371 Roo.log(this.store.reader.jsonData);
8372 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8374 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8380 onTypeAhead : function(){
8381 if(this.store.getCount() > 0){
8382 var r = this.store.getAt(0);
8383 var newValue = r.data[this.displayField];
8384 var len = newValue.length;
8385 var selStart = this.getRawValue().length;
8387 if(selStart != len){
8388 this.setRawValue(newValue);
8389 this.selectText(selStart, newValue.length);
8395 onSelect : function(record, index){
8397 if(this.fireEvent('beforeselect', this, record, index) !== false){
8399 this.setFromData(index > -1 ? record.data : false);
8402 this.fireEvent('select', this, record, index);
8407 * Returns the currently selected field value or empty string if no value is set.
8408 * @return {String} value The selected value
8410 getValue : function(){
8413 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8416 if(this.valueField){
8417 return typeof this.value != 'undefined' ? this.value : '';
8419 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8424 * Clears any text/value currently set in the field
8426 clearValue : function(){
8427 if(this.hiddenField){
8428 this.hiddenField.dom.value = '';
8431 this.setRawValue('');
8432 this.lastSelectionText = '';
8437 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8438 * will be displayed in the field. If the value does not match the data value of an existing item,
8439 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8440 * Otherwise the field will be blank (although the value will still be set).
8441 * @param {String} value The value to match
8443 setValue : function(v){
8450 if(this.valueField){
8451 var r = this.findRecord(this.valueField, v);
8453 text = r.data[this.displayField];
8454 }else if(this.valueNotFoundText !== undefined){
8455 text = this.valueNotFoundText;
8458 this.lastSelectionText = text;
8459 if(this.hiddenField){
8460 this.hiddenField.dom.value = v;
8462 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8466 * @property {Object} the last set data for the element
8471 * Sets the value of the field based on a object which is related to the record format for the store.
8472 * @param {Object} value the value to set as. or false on reset?
8474 setFromData : function(o){
8481 var dv = ''; // display value
8482 var vv = ''; // value value..
8484 if (this.displayField) {
8485 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8487 // this is an error condition!!!
8488 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8491 if(this.valueField){
8492 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8495 if(this.hiddenField){
8496 this.hiddenField.dom.value = vv;
8498 this.lastSelectionText = dv;
8499 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8503 // no hidden field.. - we store the value in 'value', but still display
8504 // display field!!!!
8505 this.lastSelectionText = dv;
8506 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8513 // overridden so that last data is reset..
8514 this.setValue(this.originalValue);
8515 this.clearInvalid();
8516 this.lastData = false;
8518 this.view.clearSelections();
8522 findRecord : function(prop, value){
8524 if(this.store.getCount() > 0){
8525 this.store.each(function(r){
8526 if(r.data[prop] == value){
8538 // returns hidden if it's set..
8539 if (!this.rendered) {return ''};
8540 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8544 onViewMove : function(e, t){
8545 this.inKeyMode = false;
8549 onViewOver : function(e, t){
8550 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8553 var item = this.view.findItemFromChild(t);
8555 var index = this.view.indexOf(item);
8556 this.select(index, false);
8561 onViewClick : function(doFocus)
8563 var index = this.view.getSelectedIndexes()[0];
8564 var r = this.store.getAt(index);
8566 this.onSelect(r, index);
8568 if(doFocus !== false && !this.blockFocus){
8569 this.inputEl().focus();
8574 restrictHeight : function(){
8575 //this.innerList.dom.style.height = '';
8576 //var inner = this.innerList.dom;
8577 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8578 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8579 //this.list.beginUpdate();
8580 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8581 this.list.alignTo(this.inputEl(), this.listAlign);
8582 //this.list.endUpdate();
8586 onEmptyResults : function(){
8591 * Returns true if the dropdown list is expanded, else false.
8593 isExpanded : function(){
8594 return this.list.isVisible();
8598 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8599 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8600 * @param {String} value The data value of the item to select
8601 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8602 * selected item if it is not currently in view (defaults to true)
8603 * @return {Boolean} True if the value matched an item in the list, else false
8605 selectByValue : function(v, scrollIntoView){
8606 if(v !== undefined && v !== null){
8607 var r = this.findRecord(this.valueField || this.displayField, v);
8609 this.select(this.store.indexOf(r), scrollIntoView);
8617 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8618 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8619 * @param {Number} index The zero-based index of the list item to select
8620 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8621 * selected item if it is not currently in view (defaults to true)
8623 select : function(index, scrollIntoView){
8624 this.selectedIndex = index;
8625 this.view.select(index);
8626 if(scrollIntoView !== false){
8627 var el = this.view.getNode(index);
8629 //this.innerList.scrollChildIntoView(el, false);
8636 selectNext : function(){
8637 var ct = this.store.getCount();
8639 if(this.selectedIndex == -1){
8641 }else if(this.selectedIndex < ct-1){
8642 this.select(this.selectedIndex+1);
8648 selectPrev : function(){
8649 var ct = this.store.getCount();
8651 if(this.selectedIndex == -1){
8653 }else if(this.selectedIndex != 0){
8654 this.select(this.selectedIndex-1);
8660 onKeyUp : function(e){
8661 if(this.editable !== false && !e.isSpecialKey()){
8662 this.lastKey = e.getKey();
8663 this.dqTask.delay(this.queryDelay);
8668 validateBlur : function(){
8669 return !this.list || !this.list.isVisible();
8673 initQuery : function(){
8674 this.doQuery(this.getRawValue());
8678 doForce : function(){
8679 if(this.el.dom.value.length > 0){
8681 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8687 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8688 * query allowing the query action to be canceled if needed.
8689 * @param {String} query The SQL query to execute
8690 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8691 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8692 * saved in the current store (defaults to false)
8694 doQuery : function(q, forceAll){
8696 if(q === undefined || q === null){
8705 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8710 forceAll = qe.forceAll;
8711 if(forceAll === true || (q.length >= this.minChars)){
8713 this.hasQuery = true;
8715 if(this.lastQuery != q || this.alwaysQuery){
8717 if(this.mode == 'local'){
8718 this.selectedIndex = -1;
8720 this.store.clearFilter();
8722 this.store.filter(this.displayField, q);
8726 this.store.baseParams[this.queryParam] = q;
8728 var options = {params : this.getParams(q)};
8732 options.params.start = this.page * this.pageSize;
8735 this.store.load(options);
8739 this.selectedIndex = -1;
8744 this.loadNext = false;
8748 getParams : function(q){
8750 //p[this.queryParam] = q;
8754 p.limit = this.pageSize;
8760 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8762 collapse : function(){
8763 if(!this.isExpanded()){
8768 Roo.get(document).un('mousedown', this.collapseIf, this);
8769 Roo.get(document).un('mousewheel', this.collapseIf, this);
8770 if (!this.editable) {
8771 Roo.get(document).un('keydown', this.listKeyPress, this);
8773 this.fireEvent('collapse', this);
8777 collapseIf : function(e){
8778 var in_combo = e.within(this.el);
8779 var in_list = e.within(this.list);
8781 if (in_combo || in_list) {
8782 //e.stopPropagation();
8791 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8793 expand : function(){
8795 if(this.isExpanded() || !this.hasFocus){
8799 this.list.alignTo(this.inputEl(), this.listAlign);
8801 Roo.get(document).on('mousedown', this.collapseIf, this);
8802 Roo.get(document).on('mousewheel', this.collapseIf, this);
8803 if (!this.editable) {
8804 Roo.get(document).on('keydown', this.listKeyPress, this);
8807 this.fireEvent('expand', this);
8811 // Implements the default empty TriggerField.onTriggerClick function
8812 onTriggerClick : function()
8814 Roo.log('trigger click');
8821 this.loadNext = false;
8823 if(this.isExpanded()){
8825 if (!this.blockFocus) {
8826 this.inputEl().focus();
8830 this.hasFocus = true;
8831 if(this.triggerAction == 'all') {
8832 this.doQuery(this.allQuery, true);
8834 this.doQuery(this.getRawValue());
8836 if (!this.blockFocus) {
8837 this.inputEl().focus();
8841 listKeyPress : function(e)
8843 //Roo.log('listkeypress');
8844 // scroll to first matching element based on key pres..
8845 if (e.isSpecialKey()) {
8848 var k = String.fromCharCode(e.getKey()).toUpperCase();
8851 var csel = this.view.getSelectedNodes();
8852 var cselitem = false;
8854 var ix = this.view.indexOf(csel[0]);
8855 cselitem = this.store.getAt(ix);
8856 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8862 this.store.each(function(v) {
8864 // start at existing selection.
8865 if (cselitem.id == v.id) {
8871 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8872 match = this.store.indexOf(v);
8878 if (match === false) {
8879 return true; // no more action?
8882 this.view.select(match);
8883 var sn = Roo.get(this.view.getSelectedNodes()[0])
8884 //sn.scrollIntoView(sn.dom.parentNode, false);
8887 onViewScroll : function(e, t){
8889 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8893 this.hasQuery = true;
8895 this.loading = this.list.select('.loading', true).first();
8897 if(this.loading === null){
8898 this.list.createChild({
8900 cls: 'loading select2-more-results select2-active',
8901 html: 'Loading more results...'
8904 this.loading = this.list.select('.loading', true).first();
8906 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8908 this.loading.hide();
8911 this.loading.show();
8916 this.loadNext = true;
8918 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8923 addItem : function(o)
8925 var dv = ''; // display value
8927 if (this.displayField) {
8928 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8930 // this is an error condition!!!
8931 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8938 var choice = this.choices.createChild({
8940 cls: 'select2-search-choice',
8949 cls: 'select2-search-choice-close',
8954 }, this.searchField);
8956 var close = choice.select('a.select2-search-choice-close', true).first()
8958 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8965 this.inputEl().dom.value = '';
8969 onRemoveItem : function(e, _self, o)
8971 Roo.log('remove item');
8972 var index = this.item.indexOf(o.data) * 1;
8975 Roo.log('not this item?!');
8979 this.item.splice(index, 1);
8984 this.fireEvent('remove', this);
8988 syncValue : function()
8990 if(!this.item.length){
8997 Roo.each(this.item, function(i){
8998 if(_this.valueField){
8999 value.push(i[_this.valueField]);
9006 this.value = value.join(',');
9008 if(this.hiddenField){
9009 this.hiddenField.dom.value = this.value;
9013 clearItem : function()
9021 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9031 * @cfg {Boolean} grow
9035 * @cfg {Number} growMin
9039 * @cfg {Number} growMax
9049 * Ext JS Library 1.1.1
9050 * Copyright(c) 2006-2007, Ext JS, LLC.
9052 * Originally Released Under LGPL - original licence link has changed is not relivant.
9055 * <script type="text/javascript">
9060 * @extends Roo.util.Observable
9061 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9062 * This class also supports single and multi selection modes. <br>
9063 * Create a data model bound view:
9065 var store = new Roo.data.Store(...);
9067 var view = new Roo.View({
9069 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9072 selectedClass: "ydataview-selected",
9076 // listen for node click?
9077 view.on("click", function(vw, index, node, e){
9078 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9082 dataModel.load("foobar.xml");
9084 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9086 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9087 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9089 * Note: old style constructor is still suported (container, template, config)
9093 * @param {Object} config The config object
9096 Roo.View = function(config, depreciated_tpl, depreciated_config){
9098 if (typeof(depreciated_tpl) == 'undefined') {
9099 // new way.. - universal constructor.
9100 Roo.apply(this, config);
9101 this.el = Roo.get(this.el);
9104 this.el = Roo.get(config);
9105 this.tpl = depreciated_tpl;
9106 Roo.apply(this, depreciated_config);
9108 this.wrapEl = this.el.wrap().wrap();
9109 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9112 if(typeof(this.tpl) == "string"){
9113 this.tpl = new Roo.Template(this.tpl);
9115 // support xtype ctors..
9116 this.tpl = new Roo.factory(this.tpl, Roo);
9128 * @event beforeclick
9129 * Fires before a click is processed. Returns false to cancel the default action.
9130 * @param {Roo.View} this
9131 * @param {Number} index The index of the target node
9132 * @param {HTMLElement} node The target node
9133 * @param {Roo.EventObject} e The raw event object
9135 "beforeclick" : true,
9138 * Fires when a template node is clicked.
9139 * @param {Roo.View} this
9140 * @param {Number} index The index of the target node
9141 * @param {HTMLElement} node The target node
9142 * @param {Roo.EventObject} e The raw event object
9147 * Fires when a template node is double clicked.
9148 * @param {Roo.View} this
9149 * @param {Number} index The index of the target node
9150 * @param {HTMLElement} node The target node
9151 * @param {Roo.EventObject} e The raw event object
9155 * @event contextmenu
9156 * Fires when a template node is right clicked.
9157 * @param {Roo.View} this
9158 * @param {Number} index The index of the target node
9159 * @param {HTMLElement} node The target node
9160 * @param {Roo.EventObject} e The raw event object
9162 "contextmenu" : true,
9164 * @event selectionchange
9165 * Fires when the selected nodes change.
9166 * @param {Roo.View} this
9167 * @param {Array} selections Array of the selected nodes
9169 "selectionchange" : true,
9172 * @event beforeselect
9173 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9174 * @param {Roo.View} this
9175 * @param {HTMLElement} node The node to be selected
9176 * @param {Array} selections Array of currently selected nodes
9178 "beforeselect" : true,
9180 * @event preparedata
9181 * Fires on every row to render, to allow you to change the data.
9182 * @param {Roo.View} this
9183 * @param {Object} data to be rendered (change this)
9185 "preparedata" : true
9193 "click": this.onClick,
9194 "dblclick": this.onDblClick,
9195 "contextmenu": this.onContextMenu,
9199 this.selections = [];
9201 this.cmp = new Roo.CompositeElementLite([]);
9203 this.store = Roo.factory(this.store, Roo.data);
9204 this.setStore(this.store, true);
9207 if ( this.footer && this.footer.xtype) {
9209 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9211 this.footer.dataSource = this.store
9212 this.footer.container = fctr;
9213 this.footer = Roo.factory(this.footer, Roo);
9214 fctr.insertFirst(this.el);
9216 // this is a bit insane - as the paging toolbar seems to detach the el..
9217 // dom.parentNode.parentNode.parentNode
9218 // they get detached?
9222 Roo.View.superclass.constructor.call(this);
9227 Roo.extend(Roo.View, Roo.util.Observable, {
9230 * @cfg {Roo.data.Store} store Data store to load data from.
9235 * @cfg {String|Roo.Element} el The container element.
9240 * @cfg {String|Roo.Template} tpl The template used by this View
9244 * @cfg {String} dataName the named area of the template to use as the data area
9245 * Works with domtemplates roo-name="name"
9249 * @cfg {String} selectedClass The css class to add to selected nodes
9251 selectedClass : "x-view-selected",
9253 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9258 * @cfg {String} text to display on mask (default Loading)
9262 * @cfg {Boolean} multiSelect Allow multiple selection
9264 multiSelect : false,
9266 * @cfg {Boolean} singleSelect Allow single selection
9268 singleSelect: false,
9271 * @cfg {Boolean} toggleSelect - selecting
9273 toggleSelect : false,
9276 * Returns the element this view is bound to.
9277 * @return {Roo.Element}
9286 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9288 refresh : function(){
9292 // if we are using something like 'domtemplate', then
9293 // the what gets used is:
9294 // t.applySubtemplate(NAME, data, wrapping data..)
9295 // the outer template then get' applied with
9296 // the store 'extra data'
9297 // and the body get's added to the
9298 // roo-name="data" node?
9299 // <span class='roo-tpl-{name}'></span> ?????
9303 this.clearSelections();
9306 var records = this.store.getRange();
9307 if(records.length < 1) {
9309 // is this valid?? = should it render a template??
9311 this.el.update(this.emptyText);
9315 if (this.dataName) {
9316 this.el.update(t.apply(this.store.meta)); //????
9317 el = this.el.child('.roo-tpl-' + this.dataName);
9320 for(var i = 0, len = records.length; i < len; i++){
9321 var data = this.prepareData(records[i].data, i, records[i]);
9322 this.fireEvent("preparedata", this, data, i, records[i]);
9323 html[html.length] = Roo.util.Format.trim(
9325 t.applySubtemplate(this.dataName, data, this.store.meta) :
9332 el.update(html.join(""));
9333 this.nodes = el.dom.childNodes;
9334 this.updateIndexes(0);
9339 * Function to override to reformat the data that is sent to
9340 * the template for each node.
9341 * DEPRICATED - use the preparedata event handler.
9342 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9343 * a JSON object for an UpdateManager bound view).
9345 prepareData : function(data, index, record)
9347 this.fireEvent("preparedata", this, data, index, record);
9351 onUpdate : function(ds, record){
9352 Roo.log('on update');
9353 this.clearSelections();
9354 var index = this.store.indexOf(record);
9355 var n = this.nodes[index];
9356 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9357 n.parentNode.removeChild(n);
9358 this.updateIndexes(index, index);
9364 onAdd : function(ds, records, index)
9366 Roo.log(['on Add', ds, records, index] );
9367 this.clearSelections();
9368 if(this.nodes.length == 0){
9372 var n = this.nodes[index];
9373 for(var i = 0, len = records.length; i < len; i++){
9374 var d = this.prepareData(records[i].data, i, records[i]);
9376 this.tpl.insertBefore(n, d);
9379 this.tpl.append(this.el, d);
9382 this.updateIndexes(index);
9385 onRemove : function(ds, record, index){
9386 Roo.log('onRemove');
9387 this.clearSelections();
9388 var el = this.dataName ?
9389 this.el.child('.roo-tpl-' + this.dataName) :
9392 el.dom.removeChild(this.nodes[index]);
9393 this.updateIndexes(index);
9397 * Refresh an individual node.
9398 * @param {Number} index
9400 refreshNode : function(index){
9401 this.onUpdate(this.store, this.store.getAt(index));
9404 updateIndexes : function(startIndex, endIndex){
9405 var ns = this.nodes;
9406 startIndex = startIndex || 0;
9407 endIndex = endIndex || ns.length - 1;
9408 for(var i = startIndex; i <= endIndex; i++){
9409 ns[i].nodeIndex = i;
9414 * Changes the data store this view uses and refresh the view.
9415 * @param {Store} store
9417 setStore : function(store, initial){
9418 if(!initial && this.store){
9419 this.store.un("datachanged", this.refresh);
9420 this.store.un("add", this.onAdd);
9421 this.store.un("remove", this.onRemove);
9422 this.store.un("update", this.onUpdate);
9423 this.store.un("clear", this.refresh);
9424 this.store.un("beforeload", this.onBeforeLoad);
9425 this.store.un("load", this.onLoad);
9426 this.store.un("loadexception", this.onLoad);
9430 store.on("datachanged", this.refresh, this);
9431 store.on("add", this.onAdd, this);
9432 store.on("remove", this.onRemove, this);
9433 store.on("update", this.onUpdate, this);
9434 store.on("clear", this.refresh, this);
9435 store.on("beforeload", this.onBeforeLoad, this);
9436 store.on("load", this.onLoad, this);
9437 store.on("loadexception", this.onLoad, this);
9445 * onbeforeLoad - masks the loading area.
9448 onBeforeLoad : function(store,opts)
9450 Roo.log('onBeforeLoad');
9454 this.el.mask(this.mask ? this.mask : "Loading" );
9456 onLoad : function ()
9463 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9464 * @param {HTMLElement} node
9465 * @return {HTMLElement} The template node
9467 findItemFromChild : function(node){
9468 var el = this.dataName ?
9469 this.el.child('.roo-tpl-' + this.dataName,true) :
9472 if(!node || node.parentNode == el){
9475 var p = node.parentNode;
9476 while(p && p != el){
9477 if(p.parentNode == el){
9486 onClick : function(e){
9487 var item = this.findItemFromChild(e.getTarget());
9489 var index = this.indexOf(item);
9490 if(this.onItemClick(item, index, e) !== false){
9491 this.fireEvent("click", this, index, item, e);
9494 this.clearSelections();
9499 onContextMenu : function(e){
9500 var item = this.findItemFromChild(e.getTarget());
9502 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9507 onDblClick : function(e){
9508 var item = this.findItemFromChild(e.getTarget());
9510 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9514 onItemClick : function(item, index, e)
9516 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9519 if (this.toggleSelect) {
9520 var m = this.isSelected(item) ? 'unselect' : 'select';
9523 _t[m](item, true, false);
9526 if(this.multiSelect || this.singleSelect){
9527 if(this.multiSelect && e.shiftKey && this.lastSelection){
9528 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9530 this.select(item, this.multiSelect && e.ctrlKey);
9531 this.lastSelection = item;
9539 * Get the number of selected nodes.
9542 getSelectionCount : function(){
9543 return this.selections.length;
9547 * Get the currently selected nodes.
9548 * @return {Array} An array of HTMLElements
9550 getSelectedNodes : function(){
9551 return this.selections;
9555 * Get the indexes of the selected nodes.
9558 getSelectedIndexes : function(){
9559 var indexes = [], s = this.selections;
9560 for(var i = 0, len = s.length; i < len; i++){
9561 indexes.push(s[i].nodeIndex);
9567 * Clear all selections
9568 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9570 clearSelections : function(suppressEvent){
9571 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9572 this.cmp.elements = this.selections;
9573 this.cmp.removeClass(this.selectedClass);
9574 this.selections = [];
9576 this.fireEvent("selectionchange", this, this.selections);
9582 * Returns true if the passed node is selected
9583 * @param {HTMLElement/Number} node The node or node index
9586 isSelected : function(node){
9587 var s = this.selections;
9591 node = this.getNode(node);
9592 return s.indexOf(node) !== -1;
9597 * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
9598 * @param {Boolean} keepExisting (optional) true to keep existing selections
9599 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9601 select : function(nodeInfo, keepExisting, suppressEvent){
9602 if(nodeInfo instanceof Array){
9604 this.clearSelections(true);
9606 for(var i = 0, len = nodeInfo.length; i < len; i++){
9607 this.select(nodeInfo[i], true, true);
9611 var node = this.getNode(nodeInfo);
9612 if(!node || this.isSelected(node)){
9613 return; // already selected.
9616 this.clearSelections(true);
9618 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9619 Roo.fly(node).addClass(this.selectedClass);
9620 this.selections.push(node);
9622 this.fireEvent("selectionchange", this, this.selections);
9630 * @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
9631 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9632 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9634 unselect : function(nodeInfo, keepExisting, suppressEvent)
9636 if(nodeInfo instanceof Array){
9637 Roo.each(this.selections, function(s) {
9638 this.unselect(s, nodeInfo);
9642 var node = this.getNode(nodeInfo);
9643 if(!node || !this.isSelected(node)){
9644 Roo.log("not selected");
9645 return; // not selected.
9649 Roo.each(this.selections, function(s) {
9651 Roo.fly(node).removeClass(this.selectedClass);
9658 this.selections= ns;
9659 this.fireEvent("selectionchange", this, this.selections);
9663 * Gets a template node.
9664 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9665 * @return {HTMLElement} The node or null if it wasn't found
9667 getNode : function(nodeInfo){
9668 if(typeof nodeInfo == "string"){
9669 return document.getElementById(nodeInfo);
9670 }else if(typeof nodeInfo == "number"){
9671 return this.nodes[nodeInfo];
9677 * Gets a range template nodes.
9678 * @param {Number} startIndex
9679 * @param {Number} endIndex
9680 * @return {Array} An array of nodes
9682 getNodes : function(start, end){
9683 var ns = this.nodes;
9685 end = typeof end == "undefined" ? ns.length - 1 : end;
9688 for(var i = start; i <= end; i++){
9692 for(var i = start; i >= end; i--){
9700 * Finds the index of the passed node
9701 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9702 * @return {Number} The index of the node or -1
9704 indexOf : function(node){
9705 node = this.getNode(node);
9706 if(typeof node.nodeIndex == "number"){
9707 return node.nodeIndex;
9709 var ns = this.nodes;
9710 for(var i = 0, len = ns.length; i < len; i++){
9721 * based on jquery fullcalendar
9725 Roo.bootstrap = Roo.bootstrap || {};
9727 * @class Roo.bootstrap.Calendar
9728 * @extends Roo.bootstrap.Component
9729 * Bootstrap Calendar class
9730 * @cfg {Boolean} loadMask (true|false) default false
9733 * Create a new Container
9734 * @param {Object} config The config object
9739 Roo.bootstrap.Calendar = function(config){
9740 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9744 * Fires when a date is selected
9745 * @param {DatePicker} this
9746 * @param {Date} date The selected date
9750 * @event monthchange
9751 * Fires when the displayed month changes
9752 * @param {DatePicker} this
9753 * @param {Date} date The selected month
9755 'monthchange': true,
9758 * Fires when mouse over an event
9759 * @param {Calendar} this
9760 * @param {event} Event
9765 * Fires when the mouse leaves an
9766 * @param {Calendar} this
9772 * Fires when the mouse click an
9773 * @param {Calendar} this
9782 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9785 * @cfg {Number} startDay
9786 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9792 getAutoCreate : function(){
9795 var fc_button = function(name, corner, style, content ) {
9796 return Roo.apply({},{
9798 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9800 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9803 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9811 style : 'width:100%',
9818 cls : 'fc-header-left',
9820 fc_button('prev', 'left', 'arrow', '‹' ),
9821 fc_button('next', 'right', 'arrow', '›' ),
9822 { tag: 'span', cls: 'fc-header-space' },
9823 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9831 cls : 'fc-header-center',
9835 cls: 'fc-header-title',
9838 html : 'month / year'
9846 cls : 'fc-header-right',
9848 /* fc_button('month', 'left', '', 'month' ),
9849 fc_button('week', '', '', 'week' ),
9850 fc_button('day', 'right', '', 'day' )
9862 var cal_heads = function() {
9864 // fixme - handle this.
9866 for (var i =0; i < Date.dayNames.length; i++) {
9867 var d = Date.dayNames[i];
9870 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9871 html : d.substring(0,3)
9875 ret[0].cls += ' fc-first';
9876 ret[6].cls += ' fc-last';
9879 var cal_cell = function(n) {
9882 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9887 cls: 'fc-day-number',
9891 cls: 'fc-day-content',
9895 style: 'position: relative;' // height: 17px;
9907 var cal_rows = function() {
9910 for (var r = 0; r < 6; r++) {
9917 for (var i =0; i < Date.dayNames.length; i++) {
9918 var d = Date.dayNames[i];
9919 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9922 row.cn[0].cls+=' fc-first';
9923 row.cn[0].cn[0].style = 'min-height:90px';
9924 row.cn[6].cls+=' fc-last';
9928 ret[0].cls += ' fc-first';
9929 ret[4].cls += ' fc-prev-last';
9930 ret[5].cls += ' fc-last';
9937 cls: 'fc-border-separate',
9938 style : 'width:100%',
9946 cls : 'fc-first fc-last',
9965 style : "position: relative;",
9968 cls : 'fc-view fc-view-month fc-grid',
9969 style : 'position: relative',
9970 unselectable : 'on',
9973 cls : 'fc-event-container',
9974 style : 'position:absolute;z-index:8;top:0;left:0;'
9992 initEvents : function()
9995 throw "can not find store for calendar";
10001 style: "text-align:center",
10005 style: "background-color:white;width:50%;margin:250 auto",
10009 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10020 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10022 var size = this.el.select('.fc-content', true).first().getSize();
10023 this.maskEl.setSize(size.width, size.height);
10024 this.maskEl.enableDisplayMode("block");
10025 if(!this.loadMask){
10026 this.maskEl.hide();
10029 this.store = Roo.factory(this.store, Roo.data);
10030 this.store.on('load', this.onLoad, this);
10031 this.store.on('beforeload', this.onBeforeLoad, this);
10035 this.cells = this.el.select('.fc-day',true);
10036 //Roo.log(this.cells);
10037 this.textNodes = this.el.query('.fc-day-number');
10038 this.cells.addClassOnOver('fc-state-hover');
10040 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10041 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10042 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10043 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10045 this.on('monthchange', this.onMonthChange, this);
10047 this.update(new Date().clearTime());
10050 resize : function() {
10051 var sz = this.el.getSize();
10053 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10054 this.el.select('.fc-day-content div',true).setHeight(34);
10059 showPrevMonth : function(e){
10060 this.update(this.activeDate.add("mo", -1));
10062 showToday : function(e){
10063 this.update(new Date().clearTime());
10066 showNextMonth : function(e){
10067 this.update(this.activeDate.add("mo", 1));
10071 showPrevYear : function(){
10072 this.update(this.activeDate.add("y", -1));
10076 showNextYear : function(){
10077 this.update(this.activeDate.add("y", 1));
10082 update : function(date)
10084 var vd = this.activeDate;
10085 this.activeDate = date;
10086 // if(vd && this.el){
10087 // var t = date.getTime();
10088 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10089 // Roo.log('using add remove');
10091 // this.fireEvent('monthchange', this, date);
10093 // this.cells.removeClass("fc-state-highlight");
10094 // this.cells.each(function(c){
10095 // if(c.dateValue == t){
10096 // c.addClass("fc-state-highlight");
10097 // setTimeout(function(){
10098 // try{c.dom.firstChild.focus();}catch(e){}
10108 var days = date.getDaysInMonth();
10110 var firstOfMonth = date.getFirstDateOfMonth();
10111 var startingPos = firstOfMonth.getDay()-this.startDay;
10113 if(startingPos < this.startDay){
10117 var pm = date.add(Date.MONTH, -1);
10118 var prevStart = pm.getDaysInMonth()-startingPos;
10120 this.cells = this.el.select('.fc-day',true);
10121 this.textNodes = this.el.query('.fc-day-number');
10122 this.cells.addClassOnOver('fc-state-hover');
10124 var cells = this.cells.elements;
10125 var textEls = this.textNodes;
10127 Roo.each(cells, function(cell){
10128 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10131 days += startingPos;
10133 // convert everything to numbers so it's fast
10134 var day = 86400000;
10135 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10138 //Roo.log(prevStart);
10140 var today = new Date().clearTime().getTime();
10141 var sel = date.clearTime().getTime();
10142 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10143 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10144 var ddMatch = this.disabledDatesRE;
10145 var ddText = this.disabledDatesText;
10146 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10147 var ddaysText = this.disabledDaysText;
10148 var format = this.format;
10150 var setCellClass = function(cal, cell){
10152 //Roo.log('set Cell Class');
10154 var t = d.getTime();
10158 cell.dateValue = t;
10160 cell.className += " fc-today";
10161 cell.className += " fc-state-highlight";
10162 cell.title = cal.todayText;
10165 // disable highlight in other month..
10166 //cell.className += " fc-state-highlight";
10171 cell.className = " fc-state-disabled";
10172 cell.title = cal.minText;
10176 cell.className = " fc-state-disabled";
10177 cell.title = cal.maxText;
10181 if(ddays.indexOf(d.getDay()) != -1){
10182 cell.title = ddaysText;
10183 cell.className = " fc-state-disabled";
10186 if(ddMatch && format){
10187 var fvalue = d.dateFormat(format);
10188 if(ddMatch.test(fvalue)){
10189 cell.title = ddText.replace("%0", fvalue);
10190 cell.className = " fc-state-disabled";
10194 if (!cell.initialClassName) {
10195 cell.initialClassName = cell.dom.className;
10198 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10203 for(; i < startingPos; i++) {
10204 textEls[i].innerHTML = (++prevStart);
10205 d.setDate(d.getDate()+1);
10207 cells[i].className = "fc-past fc-other-month";
10208 setCellClass(this, cells[i]);
10213 for(; i < days; i++){
10214 intDay = i - startingPos + 1;
10215 textEls[i].innerHTML = (intDay);
10216 d.setDate(d.getDate()+1);
10218 cells[i].className = ''; // "x-date-active";
10219 setCellClass(this, cells[i]);
10223 for(; i < 42; i++) {
10224 textEls[i].innerHTML = (++extraDays);
10225 d.setDate(d.getDate()+1);
10227 cells[i].className = "fc-future fc-other-month";
10228 setCellClass(this, cells[i]);
10231 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10233 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10235 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10236 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10238 if(totalRows != 6){
10239 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10240 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10243 this.fireEvent('monthchange', this, date);
10247 if(!this.internalRender){
10248 var main = this.el.dom.firstChild;
10249 var w = main.offsetWidth;
10250 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10251 Roo.fly(main).setWidth(w);
10252 this.internalRender = true;
10253 // opera does not respect the auto grow header center column
10254 // then, after it gets a width opera refuses to recalculate
10255 // without a second pass
10256 if(Roo.isOpera && !this.secondPass){
10257 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10258 this.secondPass = true;
10259 this.update.defer(10, this, [date]);
10266 findCell : function(dt) {
10267 dt = dt.clearTime().getTime();
10269 this.cells.each(function(c){
10270 //Roo.log("check " +c.dateValue + '?=' + dt);
10271 if(c.dateValue == dt){
10281 findCells : function(ev) {
10282 var s = ev.start.clone().clearTime().getTime();
10284 var e= ev.end.clone().clearTime().getTime();
10287 this.cells.each(function(c){
10288 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10290 if(c.dateValue > e){
10293 if(c.dateValue < s){
10302 findBestRow: function(cells)
10306 for (var i =0 ; i < cells.length;i++) {
10307 ret = Math.max(cells[i].rows || 0,ret);
10314 addItem : function(ev)
10316 // look for vertical location slot in
10317 var cells = this.findCells(ev);
10319 ev.row = this.findBestRow(cells);
10321 // work out the location.
10325 for(var i =0; i < cells.length; i++) {
10333 if (crow.start.getY() == cells[i].getY()) {
10335 crow.end = cells[i];
10351 for (var i = 0; i < cells.length;i++) {
10352 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10356 this.calevents.push(ev);
10359 clearEvents: function() {
10361 if(!this.calevents){
10365 Roo.each(this.cells.elements, function(c){
10369 Roo.each(this.calevents, function(e) {
10370 Roo.each(e.els, function(el) {
10371 el.un('mouseenter' ,this.onEventEnter, this);
10372 el.un('mouseleave' ,this.onEventLeave, this);
10379 renderEvents: function()
10381 // first make sure there is enough space..
10383 this.cells.each(function(c) {
10385 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10388 for (var e = 0; e < this.calevents.length; e++) {
10389 var ev = this.calevents[e];
10390 var cells = ev.cells;
10391 var rows = ev.rows;
10393 for(var i =0; i < rows.length; i++) {
10396 // how many rows should it span..
10399 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10400 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10402 unselectable : "on",
10405 cls: 'fc-event-inner',
10409 // cls: 'fc-event-time',
10410 // html : cells.length > 1 ? '' : ev.time
10414 cls: 'fc-event-title',
10415 html : String.format('{0}', ev.title)
10422 cls: 'ui-resizable-handle ui-resizable-e',
10423 html : '  '
10429 cfg.cls += ' fc-event-start';
10431 if ((i+1) == rows.length) {
10432 cfg.cls += ' fc-event-end';
10435 var ctr = this.el.select('.fc-event-container',true).first();
10436 var cg = ctr.createChild(cfg);
10438 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10439 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10440 cg.on('click', this.onEventClick, this, ev);
10444 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10445 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10447 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10448 cg.setWidth(ebox.right - sbox.x -2);
10456 onEventEnter: function (e, el,event,d) {
10457 this.fireEvent('evententer', this, el, event);
10460 onEventLeave: function (e, el,event,d) {
10461 this.fireEvent('eventleave', this, el, event);
10464 onEventClick: function (e, el,event,d) {
10465 this.fireEvent('eventclick', this, el, event);
10468 onMonthChange: function () {
10472 onLoad: function ()
10474 this.calevents = [];
10477 if(this.store.getCount() > 0){
10478 this.store.data.each(function(d){
10481 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10482 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10483 time : d.data.start_time,
10484 title : d.data.title,
10485 description : d.data.description,
10486 venue : d.data.venue
10491 this.renderEvents();
10494 this.maskEl.hide();
10498 onBeforeLoad: function()
10500 this.clearEvents();
10503 this.maskEl.show();
10517 * @class Roo.bootstrap.Popover
10518 * @extends Roo.bootstrap.Component
10519 * Bootstrap Popover class
10520 * @cfg {String} html contents of the popover (or false to use children..)
10521 * @cfg {String} title of popover (or false to hide)
10522 * @cfg {String} placement how it is placed
10523 * @cfg {String} trigger click || hover (or false to trigger manually)
10524 * @cfg {String} over what (parent or false to trigger manually.)
10527 * Create a new Popover
10528 * @param {Object} config The config object
10531 Roo.bootstrap.Popover = function(config){
10532 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10535 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10537 title: 'Fill in a title',
10540 placement : 'right',
10541 trigger : 'hover', // hover
10545 can_build_overlaid : false,
10547 getChildContainer : function()
10549 return this.el.select('.popover-content',true).first();
10552 getAutoCreate : function(){
10553 Roo.log('make popover?');
10555 cls : 'popover roo-dynamic',
10556 style: 'display:block',
10562 cls : 'popover-inner',
10566 cls: 'popover-title',
10570 cls : 'popover-content',
10581 setTitle: function(str)
10583 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10585 setContent: function(str)
10587 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10589 // as it get's added to the bottom of the page.
10590 onRender : function(ct, position)
10592 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10594 var cfg = Roo.apply({}, this.getAutoCreate());
10598 cfg.cls += ' ' + this.cls;
10601 cfg.style = this.style;
10603 Roo.log("adding to ")
10604 this.el = Roo.get(document.body).createChild(cfg, position);
10610 initEvents : function()
10612 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10613 this.el.enableDisplayMode('block');
10615 if (this.over === false) {
10618 if (this.triggers === false) {
10621 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10622 var triggers = this.trigger ? this.trigger.split(' ') : [];
10623 Roo.each(triggers, function(trigger) {
10625 if (trigger == 'click') {
10626 on_el.on('click', this.toggle, this);
10627 } else if (trigger != 'manual') {
10628 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10629 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10631 on_el.on(eventIn ,this.enter, this);
10632 on_el.on(eventOut, this.leave, this);
10643 toggle : function () {
10644 this.hoverState == 'in' ? this.leave() : this.enter();
10647 enter : function () {
10650 clearTimeout(this.timeout);
10652 this.hoverState = 'in'
10654 if (!this.delay || !this.delay.show) {
10659 this.timeout = setTimeout(function () {
10660 if (_t.hoverState == 'in') {
10663 }, this.delay.show)
10665 leave : function() {
10666 clearTimeout(this.timeout);
10668 this.hoverState = 'out'
10670 if (!this.delay || !this.delay.hide) {
10675 this.timeout = setTimeout(function () {
10676 if (_t.hoverState == 'out') {
10679 }, this.delay.hide)
10682 show : function (on_el)
10685 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10688 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10689 if (this.html !== false) {
10690 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10692 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10693 if (!this.title.length) {
10694 this.el.select('.popover-title',true).hide();
10697 var placement = typeof this.placement == 'function' ?
10698 this.placement.call(this, this.el, on_el) :
10701 var autoToken = /\s?auto?\s?/i;
10702 var autoPlace = autoToken.test(placement);
10704 placement = placement.replace(autoToken, '') || 'top';
10708 //this.el.setXY([0,0]);
10710 this.el.dom.style.display='block';
10711 this.el.addClass(placement);
10713 //this.el.appendTo(on_el);
10715 var p = this.getPosition();
10716 var box = this.el.getBox();
10721 var align = Roo.bootstrap.Popover.alignment[placement]
10722 this.el.alignTo(on_el, align[0],align[1]);
10723 //var arrow = this.el.select('.arrow',true).first();
10724 //arrow.set(align[2],
10726 this.el.addClass('in');
10727 this.hoverState = null;
10729 if (this.el.hasClass('fade')) {
10736 this.el.setXY([0,0]);
10737 this.el.removeClass('in');
10744 Roo.bootstrap.Popover.alignment = {
10745 'left' : ['r-l', [-10,0], 'right'],
10746 'right' : ['l-r', [10,0], 'left'],
10747 'bottom' : ['t-b', [0,10], 'top'],
10748 'top' : [ 'b-t', [0,-10], 'bottom']
10759 * @class Roo.bootstrap.Progress
10760 * @extends Roo.bootstrap.Component
10761 * Bootstrap Progress class
10762 * @cfg {Boolean} striped striped of the progress bar
10763 * @cfg {Boolean} active animated of the progress bar
10767 * Create a new Progress
10768 * @param {Object} config The config object
10771 Roo.bootstrap.Progress = function(config){
10772 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10775 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10780 getAutoCreate : function(){
10788 cfg.cls += ' progress-striped';
10792 cfg.cls += ' active';
10811 * @class Roo.bootstrap.ProgressBar
10812 * @extends Roo.bootstrap.Component
10813 * Bootstrap ProgressBar class
10814 * @cfg {Number} aria_valuenow aria-value now
10815 * @cfg {Number} aria_valuemin aria-value min
10816 * @cfg {Number} aria_valuemax aria-value max
10817 * @cfg {String} label label for the progress bar
10818 * @cfg {String} panel (success | info | warning | danger )
10819 * @cfg {String} role role of the progress bar
10820 * @cfg {String} sr_only text
10824 * Create a new ProgressBar
10825 * @param {Object} config The config object
10828 Roo.bootstrap.ProgressBar = function(config){
10829 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10832 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10836 aria_valuemax : 100,
10842 getAutoCreate : function()
10847 cls: 'progress-bar',
10848 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10860 cfg.role = this.role;
10863 if(this.aria_valuenow){
10864 cfg['aria-valuenow'] = this.aria_valuenow;
10867 if(this.aria_valuemin){
10868 cfg['aria-valuemin'] = this.aria_valuemin;
10871 if(this.aria_valuemax){
10872 cfg['aria-valuemax'] = this.aria_valuemax;
10875 if(this.label && !this.sr_only){
10876 cfg.html = this.label;
10880 cfg.cls += ' progress-bar-' + this.panel;
10886 update : function(aria_valuenow)
10888 this.aria_valuenow = aria_valuenow;
10890 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10905 * @class Roo.bootstrap.TabPanel
10906 * @extends Roo.bootstrap.Component
10907 * Bootstrap TabPanel class
10908 * @cfg {Boolean} active panel active
10909 * @cfg {String} html panel content
10910 * @cfg {String} tabId tab relate id
10914 * Create a new TabPanel
10915 * @param {Object} config The config object
10918 Roo.bootstrap.TabPanel = function(config){
10919 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10922 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10928 getAutoCreate : function(){
10932 html: this.html || ''
10936 cfg.cls += ' active';
10940 cfg.tabId = this.tabId;
10958 * @class Roo.bootstrap.DateField
10959 * @extends Roo.bootstrap.Input
10960 * Bootstrap DateField class
10961 * @cfg {Number} weekStart default 0
10962 * @cfg {Number} weekStart default 0
10963 * @cfg {Number} viewMode default empty, (months|years)
10964 * @cfg {Number} minViewMode default empty, (months|years)
10965 * @cfg {Number} startDate default -Infinity
10966 * @cfg {Number} endDate default Infinity
10967 * @cfg {Boolean} todayHighlight default false
10968 * @cfg {Boolean} todayBtn default false
10969 * @cfg {Boolean} calendarWeeks default false
10970 * @cfg {Object} daysOfWeekDisabled default empty
10972 * @cfg {Boolean} keyboardNavigation default true
10973 * @cfg {String} language default en
10976 * Create a new DateField
10977 * @param {Object} config The config object
10980 Roo.bootstrap.DateField = function(config){
10981 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10985 * Fires when this field show.
10986 * @param {Roo.bootstrap.DateField} this
10987 * @param {Mixed} date The date value
10992 * Fires when this field hide.
10993 * @param {Roo.bootstrap.DateField} this
10994 * @param {Mixed} date The date value
10999 * Fires when select a date.
11000 * @param {Roo.bootstrap.DateField} this
11001 * @param {Mixed} date The date value
11007 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
11010 * @cfg {String} format
11011 * The default date format string which can be overriden for localization support. The format must be
11012 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11016 * @cfg {String} altFormats
11017 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11018 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11020 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11028 todayHighlight : false,
11034 keyboardNavigation: true,
11036 calendarWeeks: false,
11038 startDate: -Infinity,
11042 daysOfWeekDisabled: [],
11046 UTCDate: function()
11048 return new Date(Date.UTC.apply(Date, arguments));
11051 UTCToday: function()
11053 var today = new Date();
11054 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11057 getDate: function() {
11058 var d = this.getUTCDate();
11059 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11062 getUTCDate: function() {
11066 setDate: function(d) {
11067 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11070 setUTCDate: function(d) {
11072 this.setValue(this.formatDate(this.date));
11075 onRender: function(ct, position)
11078 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11080 this.language = this.language || 'en';
11081 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11082 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11084 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11085 this.format = this.format || 'm/d/y';
11086 this.isInline = false;
11087 this.isInput = true;
11088 this.component = this.el.select('.add-on', true).first() || false;
11089 this.component = (this.component && this.component.length === 0) ? false : this.component;
11090 this.hasInput = this.component && this.inputEL().length;
11092 if (typeof(this.minViewMode === 'string')) {
11093 switch (this.minViewMode) {
11095 this.minViewMode = 1;
11098 this.minViewMode = 2;
11101 this.minViewMode = 0;
11106 if (typeof(this.viewMode === 'string')) {
11107 switch (this.viewMode) {
11120 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11122 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11124 this.picker().on('mousedown', this.onMousedown, this);
11125 this.picker().on('click', this.onClick, this);
11127 this.picker().addClass('datepicker-dropdown');
11129 this.startViewMode = this.viewMode;
11132 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11133 if(!this.calendarWeeks){
11138 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11139 v.attr('colspan', function(i, val){
11140 return parseInt(val) + 1;
11145 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11147 this.setStartDate(this.startDate);
11148 this.setEndDate(this.endDate);
11150 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11157 if(this.isInline) {
11162 picker : function()
11164 return this.el.select('.datepicker', true).first();
11167 fillDow: function()
11169 var dowCnt = this.weekStart;
11178 if(this.calendarWeeks){
11186 while (dowCnt < this.weekStart + 7) {
11190 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11194 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11197 fillMonths: function()
11200 var months = this.picker().select('>.datepicker-months td', true).first();
11202 months.dom.innerHTML = '';
11208 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11211 months.createChild(month);
11216 update: function(){
11218 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11220 if (this.date < this.startDate) {
11221 this.viewDate = new Date(this.startDate);
11222 } else if (this.date > this.endDate) {
11223 this.viewDate = new Date(this.endDate);
11225 this.viewDate = new Date(this.date);
11232 var d = new Date(this.viewDate),
11233 year = d.getUTCFullYear(),
11234 month = d.getUTCMonth(),
11235 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11236 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11237 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11238 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11239 currentDate = this.date && this.date.valueOf(),
11240 today = this.UTCToday();
11242 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11244 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11246 // this.picker.select('>tfoot th.today').
11247 // .text(dates[this.language].today)
11248 // .toggle(this.todayBtn !== false);
11250 this.updateNavArrows();
11253 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11255 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11257 prevMonth.setUTCDate(day);
11259 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11261 var nextMonth = new Date(prevMonth);
11263 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11265 nextMonth = nextMonth.valueOf();
11267 var fillMonths = false;
11269 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11271 while(prevMonth.valueOf() < nextMonth) {
11274 if (prevMonth.getUTCDay() === this.weekStart) {
11276 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11284 if(this.calendarWeeks){
11285 // ISO 8601: First week contains first thursday.
11286 // ISO also states week starts on Monday, but we can be more abstract here.
11288 // Start of current week: based on weekstart/current date
11289 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11290 // Thursday of this week
11291 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11292 // First Thursday of year, year from thursday
11293 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11294 // Calendar week: ms between thursdays, div ms per day, div 7 days
11295 calWeek = (th - yth) / 864e5 / 7 + 1;
11297 fillMonths.cn.push({
11305 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11307 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11310 if (this.todayHighlight &&
11311 prevMonth.getUTCFullYear() == today.getFullYear() &&
11312 prevMonth.getUTCMonth() == today.getMonth() &&
11313 prevMonth.getUTCDate() == today.getDate()) {
11314 clsName += ' today';
11317 if (currentDate && prevMonth.valueOf() === currentDate) {
11318 clsName += ' active';
11321 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11322 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11323 clsName += ' disabled';
11326 fillMonths.cn.push({
11328 cls: 'day ' + clsName,
11329 html: prevMonth.getDate()
11332 prevMonth.setDate(prevMonth.getDate()+1);
11335 var currentYear = this.date && this.date.getUTCFullYear();
11336 var currentMonth = this.date && this.date.getUTCMonth();
11338 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11340 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11341 v.removeClass('active');
11343 if(currentYear === year && k === currentMonth){
11344 v.addClass('active');
11347 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11348 v.addClass('disabled');
11354 year = parseInt(year/10, 10) * 10;
11356 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11358 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11361 for (var i = -1; i < 11; i++) {
11362 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11364 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11372 showMode: function(dir) {
11374 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11376 Roo.each(this.picker().select('>div',true).elements, function(v){
11377 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11380 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11385 if(this.isInline) return;
11387 this.picker().removeClass(['bottom', 'top']);
11389 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11391 * place to the top of element!
11395 this.picker().addClass('top');
11396 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11401 this.picker().addClass('bottom');
11403 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11406 parseDate : function(value){
11407 if(!value || value instanceof Date){
11410 var v = Date.parseDate(value, this.format);
11411 if (!v && this.useIso) {
11412 v = Date.parseDate(value, 'Y-m-d');
11414 if(!v && this.altFormats){
11415 if(!this.altFormatsArray){
11416 this.altFormatsArray = this.altFormats.split("|");
11418 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11419 v = Date.parseDate(value, this.altFormatsArray[i]);
11425 formatDate : function(date, fmt){
11426 return (!date || !(date instanceof Date)) ?
11427 date : date.dateFormat(fmt || this.format);
11430 onFocus : function()
11432 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11436 onBlur : function()
11438 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11444 this.picker().show();
11448 this.fireEvent('show', this, this.date);
11453 if(this.isInline) return;
11454 this.picker().hide();
11455 this.viewMode = this.startViewMode;
11458 this.fireEvent('hide', this, this.date);
11462 onMousedown: function(e){
11463 e.stopPropagation();
11464 e.preventDefault();
11467 keyup: function(e){
11468 Roo.bootstrap.DateField.superclass.keyup.call(this);
11473 setValue: function(v){
11474 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11476 this.fireEvent('select', this, this.date);
11480 fireKey: function(e){
11481 if (!this.picker().isVisible()){
11482 if (e.keyCode == 27) // allow escape to hide and re-show picker
11486 var dateChanged = false,
11488 newDate, newViewDate;
11492 e.preventDefault();
11496 if (!this.keyboardNavigation) break;
11497 dir = e.keyCode == 37 ? -1 : 1;
11500 newDate = this.moveYear(this.date, dir);
11501 newViewDate = this.moveYear(this.viewDate, dir);
11502 } else if (e.shiftKey){
11503 newDate = this.moveMonth(this.date, dir);
11504 newViewDate = this.moveMonth(this.viewDate, dir);
11506 newDate = new Date(this.date);
11507 newDate.setUTCDate(this.date.getUTCDate() + dir);
11508 newViewDate = new Date(this.viewDate);
11509 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11511 if (this.dateWithinRange(newDate)){
11512 this.date = newDate;
11513 this.viewDate = newViewDate;
11514 this.setValue(this.formatDate(this.date));
11516 e.preventDefault();
11517 dateChanged = true;
11522 if (!this.keyboardNavigation) break;
11523 dir = e.keyCode == 38 ? -1 : 1;
11525 newDate = this.moveYear(this.date, dir);
11526 newViewDate = this.moveYear(this.viewDate, dir);
11527 } else if (e.shiftKey){
11528 newDate = this.moveMonth(this.date, dir);
11529 newViewDate = this.moveMonth(this.viewDate, dir);
11531 newDate = new Date(this.date);
11532 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11533 newViewDate = new Date(this.viewDate);
11534 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11536 if (this.dateWithinRange(newDate)){
11537 this.date = newDate;
11538 this.viewDate = newViewDate;
11539 this.setValue(this.formatDate(this.date));
11541 e.preventDefault();
11542 dateChanged = true;
11546 this.setValue(this.formatDate(this.date));
11548 e.preventDefault();
11551 this.setValue(this.formatDate(this.date));
11558 onClick: function(e) {
11559 e.stopPropagation();
11560 e.preventDefault();
11562 var target = e.getTarget();
11564 if(target.nodeName.toLowerCase() === 'i'){
11565 target = Roo.get(target).dom.parentNode;
11568 var nodeName = target.nodeName;
11569 var className = target.className;
11570 var html = target.innerHTML;
11572 switch(nodeName.toLowerCase()) {
11574 switch(className) {
11580 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11581 switch(this.viewMode){
11583 this.viewDate = this.moveMonth(this.viewDate, dir);
11587 this.viewDate = this.moveYear(this.viewDate, dir);
11593 var date = new Date();
11594 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11596 this.setValue(this.formatDate(this.date));
11602 if (className.indexOf('disabled') === -1) {
11603 this.viewDate.setUTCDate(1);
11604 if (className.indexOf('month') !== -1) {
11605 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11607 var year = parseInt(html, 10) || 0;
11608 this.viewDate.setUTCFullYear(year);
11617 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11618 var day = parseInt(html, 10) || 1;
11619 var year = this.viewDate.getUTCFullYear(),
11620 month = this.viewDate.getUTCMonth();
11622 if (className.indexOf('old') !== -1) {
11629 } else if (className.indexOf('new') !== -1) {
11637 this.date = this.UTCDate(year, month, day,0,0,0,0);
11638 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11640 this.setValue(this.formatDate(this.date));
11647 setStartDate: function(startDate){
11648 this.startDate = startDate || -Infinity;
11649 if (this.startDate !== -Infinity) {
11650 this.startDate = this.parseDate(this.startDate);
11653 this.updateNavArrows();
11656 setEndDate: function(endDate){
11657 this.endDate = endDate || Infinity;
11658 if (this.endDate !== Infinity) {
11659 this.endDate = this.parseDate(this.endDate);
11662 this.updateNavArrows();
11665 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11666 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11667 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11668 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11670 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11671 return parseInt(d, 10);
11674 this.updateNavArrows();
11677 updateNavArrows: function() {
11678 var d = new Date(this.viewDate),
11679 year = d.getUTCFullYear(),
11680 month = d.getUTCMonth();
11682 Roo.each(this.picker().select('.prev', true).elements, function(v){
11684 switch (this.viewMode) {
11687 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11693 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11700 Roo.each(this.picker().select('.next', true).elements, function(v){
11702 switch (this.viewMode) {
11705 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11711 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11719 moveMonth: function(date, dir){
11720 if (!dir) return date;
11721 var new_date = new Date(date.valueOf()),
11722 day = new_date.getUTCDate(),
11723 month = new_date.getUTCMonth(),
11724 mag = Math.abs(dir),
11726 dir = dir > 0 ? 1 : -1;
11729 // If going back one month, make sure month is not current month
11730 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11732 return new_date.getUTCMonth() == month;
11734 // If going forward one month, make sure month is as expected
11735 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11737 return new_date.getUTCMonth() != new_month;
11739 new_month = month + dir;
11740 new_date.setUTCMonth(new_month);
11741 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11742 if (new_month < 0 || new_month > 11)
11743 new_month = (new_month + 12) % 12;
11745 // For magnitudes >1, move one month at a time...
11746 for (var i=0; i<mag; i++)
11747 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11748 new_date = this.moveMonth(new_date, dir);
11749 // ...then reset the day, keeping it in the new month
11750 new_month = new_date.getUTCMonth();
11751 new_date.setUTCDate(day);
11753 return new_month != new_date.getUTCMonth();
11756 // Common date-resetting loop -- if date is beyond end of month, make it
11759 new_date.setUTCDate(--day);
11760 new_date.setUTCMonth(new_month);
11765 moveYear: function(date, dir){
11766 return this.moveMonth(date, dir*12);
11769 dateWithinRange: function(date){
11770 return date >= this.startDate && date <= this.endDate;
11774 remove: function() {
11775 this.picker().remove();
11780 Roo.apply(Roo.bootstrap.DateField, {
11791 html: '<i class="icon-arrow-left"/>'
11801 html: '<i class="icon-arrow-right"/>'
11843 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11844 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11845 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11846 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11847 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11860 navFnc: 'FullYear',
11865 navFnc: 'FullYear',
11870 Roo.apply(Roo.bootstrap.DateField, {
11874 cls: 'datepicker dropdown-menu',
11878 cls: 'datepicker-days',
11882 cls: 'table-condensed',
11884 Roo.bootstrap.DateField.head,
11888 Roo.bootstrap.DateField.footer
11895 cls: 'datepicker-months',
11899 cls: 'table-condensed',
11901 Roo.bootstrap.DateField.head,
11902 Roo.bootstrap.DateField.content,
11903 Roo.bootstrap.DateField.footer
11910 cls: 'datepicker-years',
11914 cls: 'table-condensed',
11916 Roo.bootstrap.DateField.head,
11917 Roo.bootstrap.DateField.content,
11918 Roo.bootstrap.DateField.footer
11937 * @class Roo.bootstrap.TimeField
11938 * @extends Roo.bootstrap.Input
11939 * Bootstrap DateField class
11943 * Create a new TimeField
11944 * @param {Object} config The config object
11947 Roo.bootstrap.TimeField = function(config){
11948 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11952 * Fires when this field show.
11953 * @param {Roo.bootstrap.DateField} this
11954 * @param {Mixed} date The date value
11959 * Fires when this field hide.
11960 * @param {Roo.bootstrap.DateField} this
11961 * @param {Mixed} date The date value
11966 * Fires when select a date.
11967 * @param {Roo.bootstrap.DateField} this
11968 * @param {Mixed} date The date value
11974 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
11977 * @cfg {String} format
11978 * The default time format string which can be overriden for localization support. The format must be
11979 * valid according to {@link Date#parseDate} (defaults to 'H:i').
11983 onRender: function(ct, position)
11986 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11988 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11990 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11992 this.pop = this.picker().select('>.datepicker-time',true).first();
11993 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
11995 this.picker().on('mousedown', this.onMousedown, this);
11996 this.picker().on('click', this.onClick, this);
11998 this.picker().addClass('datepicker-dropdown');
12003 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12004 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12005 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12006 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12007 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12008 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12012 fireKey: function(e){
12013 if (!this.picker().isVisible()){
12014 if (e.keyCode == 27) // allow escape to hide and re-show picker
12019 e.preventDefault();
12027 this.onTogglePeriod();
12030 this.onIncrementMinutes();
12033 this.onDecrementMinutes();
12042 onClick: function(e) {
12043 e.stopPropagation();
12044 e.preventDefault();
12047 picker : function()
12049 return this.el.select('.datepicker', true).first();
12052 fillTime: function()
12054 var time = this.pop.select('tbody', true).first();
12056 time.dom.innerHTML = '';
12071 cls: 'hours-up glyphicon glyphicon-chevron-up'
12091 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12112 cls: 'timepicker-hour',
12127 cls: 'timepicker-minute',
12142 cls: 'btn btn-primary period',
12164 cls: 'hours-down glyphicon glyphicon-chevron-down'
12184 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12202 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12209 var hours = this.time.getHours();
12210 var minutes = this.time.getMinutes();
12223 hours = hours - 12;
12227 hours = '0' + hours;
12231 minutes = '0' + minutes;
12234 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12235 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12236 this.pop.select('button', true).first().dom.innerHTML = period;
12242 this.picker().removeClass(['bottom', 'top']);
12244 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12246 * place to the top of element!
12250 this.picker().addClass('top');
12251 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12256 this.picker().addClass('bottom');
12258 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12261 onFocus : function()
12263 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12267 onBlur : function()
12269 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12275 this.picker().show();
12280 this.fireEvent('show', this, this.date);
12285 this.picker().hide();
12288 this.fireEvent('hide', this, this.date);
12291 setTime : function()
12294 this.setValue(this.time.format(this.format));
12296 this.fireEvent('select', this, this.date);
12301 onMousedown: function(e){
12302 e.stopPropagation();
12303 e.preventDefault();
12306 onIncrementHours: function()
12308 Roo.log('onIncrementHours');
12309 this.time = this.time.add(Date.HOUR, 1);
12314 onDecrementHours: function()
12316 Roo.log('onDecrementHours');
12317 this.time = this.time.add(Date.HOUR, -1);
12321 onIncrementMinutes: function()
12323 Roo.log('onIncrementMinutes');
12324 this.time = this.time.add(Date.MINUTE, 1);
12328 onDecrementMinutes: function()
12330 Roo.log('onDecrementMinutes');
12331 this.time = this.time.add(Date.MINUTE, -1);
12335 onTogglePeriod: function()
12337 Roo.log('onTogglePeriod');
12338 this.time = this.time.add(Date.HOUR, 12);
12345 Roo.apply(Roo.bootstrap.TimeField, {
12375 cls: 'btn btn-info ok',
12387 Roo.apply(Roo.bootstrap.TimeField, {
12391 cls: 'datepicker dropdown-menu',
12395 cls: 'datepicker-time',
12399 cls: 'table-condensed',
12401 Roo.bootstrap.TimeField.content,
12402 Roo.bootstrap.TimeField.footer
12421 * @class Roo.bootstrap.CheckBox
12422 * @extends Roo.bootstrap.Input
12423 * Bootstrap CheckBox class
12425 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12426 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12427 * @cfg {String} boxLabel The text that appears beside the checkbox
12428 * @cfg {Boolean} checked initnal the element
12431 * Create a new CheckBox
12432 * @param {Object} config The config object
12435 Roo.bootstrap.CheckBox = function(config){
12436 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12441 * Fires when the element is checked or unchecked.
12442 * @param {Roo.bootstrap.CheckBox} this This input
12443 * @param {Boolean} checked The new checked value
12449 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12451 inputType: 'checkbox',
12457 getAutoCreate : function()
12459 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12465 cfg.cls = 'form-group' //input-group
12470 type : this.inputType,
12471 value : (!this.checked) ? this.valueOff : this.inputValue,
12473 placeholder : this.placeholder || ''
12477 if (this.disabled) {
12478 input.disabled=true;
12482 input.checked = this.checked;
12486 input.name = this.name;
12490 input.cls += ' input-' + this.size;
12494 ['xs','sm','md','lg'].map(function(size){
12495 if (settings[size]) {
12496 cfg.cls += ' col-' + size + '-' + settings[size];
12500 var inputblock = input;
12502 if (this.before || this.after) {
12505 cls : 'input-group',
12509 inputblock.cn.push({
12511 cls : 'input-group-addon',
12515 inputblock.cn.push(input);
12517 inputblock.cn.push({
12519 cls : 'input-group-addon',
12526 if (align ==='left' && this.fieldLabel.length) {
12527 Roo.log("left and has label");
12533 cls : 'control-label col-md-' + this.labelWidth,
12534 html : this.fieldLabel
12538 cls : "col-md-" + (12 - this.labelWidth),
12545 } else if ( this.fieldLabel.length) {
12550 tag: this.boxLabel ? 'span' : 'label',
12552 cls: 'control-label box-input-label',
12553 //cls : 'input-group-addon',
12554 html : this.fieldLabel
12564 Roo.log(" no label && no align");
12579 html: this.boxLabel
12588 * return the real input element.
12590 inputEl: function ()
12592 return this.el.select('input.form-box',true).first();
12595 initEvents : function()
12597 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12599 this.inputEl().on('click', this.onClick, this);
12603 onClick : function()
12605 this.setChecked(!this.checked);
12608 setChecked : function(state,suppressEvent)
12610 this.checked = state;
12612 this.inputEl().dom.checked = state;
12614 if(suppressEvent !== true){
12615 this.fireEvent('check', this, state);
12618 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12622 setValue : function(v,suppressEvent)
12624 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12638 * @class Roo.bootstrap.Radio
12639 * @extends Roo.bootstrap.CheckBox
12640 * Bootstrap Radio class
12643 * Create a new Radio
12644 * @param {Object} config The config object
12647 Roo.bootstrap.Radio = function(config){
12648 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12652 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12654 inputType: 'radio',
12658 getAutoCreate : function()
12660 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12666 cfg.cls = 'form-group' //input-group
12671 type : this.inputType,
12672 value : (!this.checked) ? this.valueOff : this.inputValue,
12674 placeholder : this.placeholder || ''
12678 if (this.disabled) {
12679 input.disabled=true;
12683 input.checked = this.checked;
12687 input.name = this.name;
12691 input.cls += ' input-' + this.size;
12695 ['xs','sm','md','lg'].map(function(size){
12696 if (settings[size]) {
12697 cfg.cls += ' col-' + size + '-' + settings[size];
12701 var inputblock = input;
12703 if (this.before || this.after) {
12706 cls : 'input-group',
12710 inputblock.cn.push({
12712 cls : 'input-group-addon',
12716 inputblock.cn.push(input);
12718 inputblock.cn.push({
12720 cls : 'input-group-addon',
12727 if (align ==='left' && this.fieldLabel.length) {
12728 Roo.log("left and has label");
12734 cls : 'control-label col-md-' + this.labelWidth,
12735 html : this.fieldLabel
12739 cls : "col-md-" + (12 - this.labelWidth),
12746 } else if ( this.fieldLabel.length) {
12753 cls: 'control-label box-input-label',
12754 //cls : 'input-group-addon',
12755 html : this.fieldLabel
12765 Roo.log(" no label && no align");
12780 html: this.boxLabel
12788 onClick : function()
12790 this.setChecked(true);
12793 setChecked : function(state,suppressEvent)
12796 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12797 v.dom.checked = false;
12801 this.checked = state;
12802 this.inputEl().dom.checked = state;
12804 if(suppressEvent !== true){
12805 this.fireEvent('check', this, state);
12808 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12812 getGroupValue : function()
12815 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12816 if(v.dom.checked == true){
12817 value = v.dom.value;
12825 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12826 * @return {Mixed} value The field value
12828 getValue : function(){
12829 return this.getGroupValue();
12835 //<script type="text/javascript">
12838 * Based Ext JS Library 1.1.1
12839 * Copyright(c) 2006-2007, Ext JS, LLC.
12845 * @class Roo.HtmlEditorCore
12846 * @extends Roo.Component
12847 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12849 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12852 Roo.HtmlEditorCore = function(config){
12855 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12858 * @event initialize
12859 * Fires when the editor is fully initialized (including the iframe)
12860 * @param {Roo.HtmlEditorCore} this
12865 * Fires when the editor is first receives the focus. Any insertion must wait
12866 * until after this event.
12867 * @param {Roo.HtmlEditorCore} this
12871 * @event beforesync
12872 * Fires before the textarea is updated with content from the editor iframe. Return false
12873 * to cancel the sync.
12874 * @param {Roo.HtmlEditorCore} this
12875 * @param {String} html
12879 * @event beforepush
12880 * Fires before the iframe editor is updated with content from the textarea. Return false
12881 * to cancel the push.
12882 * @param {Roo.HtmlEditorCore} this
12883 * @param {String} html
12888 * Fires when the textarea is updated with content from the editor iframe.
12889 * @param {Roo.HtmlEditorCore} this
12890 * @param {String} html
12895 * Fires when the iframe editor is updated with content from the textarea.
12896 * @param {Roo.HtmlEditorCore} this
12897 * @param {String} html
12902 * @event editorevent
12903 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12904 * @param {Roo.HtmlEditorCore} this
12912 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12916 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12922 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12927 * @cfg {Number} height (in pixels)
12931 * @cfg {Number} width (in pixels)
12936 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12939 stylesheets: false,
12944 // private properties
12945 validationEvent : false,
12947 initialized : false,
12949 sourceEditMode : false,
12950 onFocus : Roo.emptyFn,
12952 hideMode:'offsets',
12960 * Protected method that will not generally be called directly. It
12961 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12962 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12964 getDocMarkup : function(){
12967 Roo.log(this.stylesheets);
12969 // inherit styels from page...??
12970 if (this.stylesheets === false) {
12972 Roo.get(document.head).select('style').each(function(node) {
12973 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12976 Roo.get(document.head).select('link').each(function(node) {
12977 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12980 } else if (!this.stylesheets.length) {
12982 st = '<style type="text/css">' +
12983 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12986 Roo.each(this.stylesheets, function(s) {
12987 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12992 st += '<style type="text/css">' +
12993 'IMG { cursor: pointer } ' +
12997 return '<html><head>' + st +
12998 //<style type="text/css">' +
12999 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13001 ' </head><body class="roo-htmleditor-body"></body></html>';
13005 onRender : function(ct, position)
13008 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13009 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13012 this.el.dom.style.border = '0 none';
13013 this.el.dom.setAttribute('tabIndex', -1);
13014 this.el.addClass('x-hidden hide');
13018 if(Roo.isIE){ // fix IE 1px bogus margin
13019 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13023 this.frameId = Roo.id();
13027 var iframe = this.owner.wrap.createChild({
13029 cls: 'form-control', // bootstrap..
13031 name: this.frameId,
13032 frameBorder : 'no',
13033 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13038 this.iframe = iframe.dom;
13040 this.assignDocWin();
13042 this.doc.designMode = 'on';
13045 this.doc.write(this.getDocMarkup());
13049 var task = { // must defer to wait for browser to be ready
13051 //console.log("run task?" + this.doc.readyState);
13052 this.assignDocWin();
13053 if(this.doc.body || this.doc.readyState == 'complete'){
13055 this.doc.designMode="on";
13059 Roo.TaskMgr.stop(task);
13060 this.initEditor.defer(10, this);
13067 Roo.TaskMgr.start(task);
13074 onResize : function(w, h)
13076 Roo.log('resize: ' +w + ',' + h );
13077 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13081 if(typeof w == 'number'){
13083 this.iframe.style.width = w + 'px';
13085 if(typeof h == 'number'){
13087 this.iframe.style.height = h + 'px';
13089 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13096 * Toggles the editor between standard and source edit mode.
13097 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13099 toggleSourceEdit : function(sourceEditMode){
13101 this.sourceEditMode = sourceEditMode === true;
13103 if(this.sourceEditMode){
13105 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13108 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13109 //this.iframe.className = '';
13112 //this.setSize(this.owner.wrap.getSize());
13113 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13120 * Protected method that will not generally be called directly. If you need/want
13121 * custom HTML cleanup, this is the method you should override.
13122 * @param {String} html The HTML to be cleaned
13123 * return {String} The cleaned HTML
13125 cleanHtml : function(html){
13126 html = String(html);
13127 if(html.length > 5){
13128 if(Roo.isSafari){ // strip safari nonsense
13129 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13132 if(html == ' '){
13139 * HTML Editor -> Textarea
13140 * Protected method that will not generally be called directly. Syncs the contents
13141 * of the editor iframe with the textarea.
13143 syncValue : function(){
13144 if(this.initialized){
13145 var bd = (this.doc.body || this.doc.documentElement);
13146 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13147 var html = bd.innerHTML;
13149 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13150 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13152 html = '<div style="'+m[0]+'">' + html + '</div>';
13155 html = this.cleanHtml(html);
13156 // fix up the special chars.. normaly like back quotes in word...
13157 // however we do not want to do this with chinese..
13158 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13159 var cc = b.charCodeAt();
13161 (cc >= 0x4E00 && cc < 0xA000 ) ||
13162 (cc >= 0x3400 && cc < 0x4E00 ) ||
13163 (cc >= 0xf900 && cc < 0xfb00 )
13169 if(this.owner.fireEvent('beforesync', this, html) !== false){
13170 this.el.dom.value = html;
13171 this.owner.fireEvent('sync', this, html);
13177 * Protected method that will not generally be called directly. Pushes the value of the textarea
13178 * into the iframe editor.
13180 pushValue : function(){
13181 if(this.initialized){
13182 var v = this.el.dom.value.trim();
13184 // if(v.length < 1){
13188 if(this.owner.fireEvent('beforepush', this, v) !== false){
13189 var d = (this.doc.body || this.doc.documentElement);
13191 this.cleanUpPaste();
13192 this.el.dom.value = d.innerHTML;
13193 this.owner.fireEvent('push', this, v);
13199 deferFocus : function(){
13200 this.focus.defer(10, this);
13204 focus : function(){
13205 if(this.win && !this.sourceEditMode){
13212 assignDocWin: function()
13214 var iframe = this.iframe;
13217 this.doc = iframe.contentWindow.document;
13218 this.win = iframe.contentWindow;
13220 if (!Roo.get(this.frameId)) {
13223 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13224 this.win = Roo.get(this.frameId).dom.contentWindow;
13229 initEditor : function(){
13230 //console.log("INIT EDITOR");
13231 this.assignDocWin();
13235 this.doc.designMode="on";
13237 this.doc.write(this.getDocMarkup());
13240 var dbody = (this.doc.body || this.doc.documentElement);
13241 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13242 // this copies styles from the containing element into thsi one..
13243 // not sure why we need all of this..
13244 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13245 ss['background-attachment'] = 'fixed'; // w3c
13246 dbody.bgProperties = 'fixed'; // ie
13247 Roo.DomHelper.applyStyles(dbody, ss);
13248 Roo.EventManager.on(this.doc, {
13249 //'mousedown': this.onEditorEvent,
13250 'mouseup': this.onEditorEvent,
13251 'dblclick': this.onEditorEvent,
13252 'click': this.onEditorEvent,
13253 'keyup': this.onEditorEvent,
13258 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13260 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13261 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13263 this.initialized = true;
13265 this.owner.fireEvent('initialize', this);
13270 onDestroy : function(){
13276 //for (var i =0; i < this.toolbars.length;i++) {
13277 // // fixme - ask toolbars for heights?
13278 // this.toolbars[i].onDestroy();
13281 //this.wrap.dom.innerHTML = '';
13282 //this.wrap.remove();
13287 onFirstFocus : function(){
13289 this.assignDocWin();
13292 this.activated = true;
13295 if(Roo.isGecko){ // prevent silly gecko errors
13297 var s = this.win.getSelection();
13298 if(!s.focusNode || s.focusNode.nodeType != 3){
13299 var r = s.getRangeAt(0);
13300 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13305 this.execCmd('useCSS', true);
13306 this.execCmd('styleWithCSS', false);
13309 this.owner.fireEvent('activate', this);
13313 adjustFont: function(btn){
13314 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13315 //if(Roo.isSafari){ // safari
13318 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13319 if(Roo.isSafari){ // safari
13320 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13321 v = (v < 10) ? 10 : v;
13322 v = (v > 48) ? 48 : v;
13323 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13328 v = Math.max(1, v+adjust);
13330 this.execCmd('FontSize', v );
13333 onEditorEvent : function(e){
13334 this.owner.fireEvent('editorevent', this, e);
13335 // this.updateToolbar();
13336 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13339 insertTag : function(tg)
13341 // could be a bit smarter... -> wrap the current selected tRoo..
13342 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13344 range = this.createRange(this.getSelection());
13345 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13346 wrappingNode.appendChild(range.extractContents());
13347 range.insertNode(wrappingNode);
13354 this.execCmd("formatblock", tg);
13358 insertText : function(txt)
13362 var range = this.createRange();
13363 range.deleteContents();
13364 //alert(Sender.getAttribute('label'));
13366 range.insertNode(this.doc.createTextNode(txt));
13372 * Executes a Midas editor command on the editor document and performs necessary focus and
13373 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13374 * @param {String} cmd The Midas command
13375 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13377 relayCmd : function(cmd, value){
13379 this.execCmd(cmd, value);
13380 this.owner.fireEvent('editorevent', this);
13381 //this.updateToolbar();
13382 this.owner.deferFocus();
13386 * Executes a Midas editor command directly on the editor document.
13387 * For visual commands, you should use {@link #relayCmd} instead.
13388 * <b>This should only be called after the editor is initialized.</b>
13389 * @param {String} cmd The Midas command
13390 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13392 execCmd : function(cmd, value){
13393 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13400 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13402 * @param {String} text | dom node..
13404 insertAtCursor : function(text)
13409 if(!this.activated){
13415 var r = this.doc.selection.createRange();
13426 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13430 // from jquery ui (MIT licenced)
13432 var win = this.win;
13434 if (win.getSelection && win.getSelection().getRangeAt) {
13435 range = win.getSelection().getRangeAt(0);
13436 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13437 range.insertNode(node);
13438 } else if (win.document.selection && win.document.selection.createRange) {
13439 // no firefox support
13440 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13441 win.document.selection.createRange().pasteHTML(txt);
13443 // no firefox support
13444 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13445 this.execCmd('InsertHTML', txt);
13454 mozKeyPress : function(e){
13456 var c = e.getCharCode(), cmd;
13459 c = String.fromCharCode(c).toLowerCase();
13473 this.cleanUpPaste.defer(100, this);
13481 e.preventDefault();
13489 fixKeys : function(){ // load time branching for fastest keydown performance
13491 return function(e){
13492 var k = e.getKey(), r;
13495 r = this.doc.selection.createRange();
13498 r.pasteHTML('    ');
13505 r = this.doc.selection.createRange();
13507 var target = r.parentElement();
13508 if(!target || target.tagName.toLowerCase() != 'li'){
13510 r.pasteHTML('<br />');
13516 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13517 this.cleanUpPaste.defer(100, this);
13523 }else if(Roo.isOpera){
13524 return function(e){
13525 var k = e.getKey();
13529 this.execCmd('InsertHTML','    ');
13532 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13533 this.cleanUpPaste.defer(100, this);
13538 }else if(Roo.isSafari){
13539 return function(e){
13540 var k = e.getKey();
13544 this.execCmd('InsertText','\t');
13548 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13549 this.cleanUpPaste.defer(100, this);
13557 getAllAncestors: function()
13559 var p = this.getSelectedNode();
13562 a.push(p); // push blank onto stack..
13563 p = this.getParentElement();
13567 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13571 a.push(this.doc.body);
13575 lastSelNode : false,
13578 getSelection : function()
13580 this.assignDocWin();
13581 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13584 getSelectedNode: function()
13586 // this may only work on Gecko!!!
13588 // should we cache this!!!!
13593 var range = this.createRange(this.getSelection()).cloneRange();
13596 var parent = range.parentElement();
13598 var testRange = range.duplicate();
13599 testRange.moveToElementText(parent);
13600 if (testRange.inRange(range)) {
13603 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13606 parent = parent.parentElement;
13611 // is ancestor a text element.
13612 var ac = range.commonAncestorContainer;
13613 if (ac.nodeType == 3) {
13614 ac = ac.parentNode;
13617 var ar = ac.childNodes;
13620 var other_nodes = [];
13621 var has_other_nodes = false;
13622 for (var i=0;i<ar.length;i++) {
13623 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13626 // fullly contained node.
13628 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13633 // probably selected..
13634 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13635 other_nodes.push(ar[i]);
13639 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13644 has_other_nodes = true;
13646 if (!nodes.length && other_nodes.length) {
13647 nodes= other_nodes;
13649 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13655 createRange: function(sel)
13657 // this has strange effects when using with
13658 // top toolbar - not sure if it's a great idea.
13659 //this.editor.contentWindow.focus();
13660 if (typeof sel != "undefined") {
13662 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13664 return this.doc.createRange();
13667 return this.doc.createRange();
13670 getParentElement: function()
13673 this.assignDocWin();
13674 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13676 var range = this.createRange(sel);
13679 var p = range.commonAncestorContainer;
13680 while (p.nodeType == 3) { // text node
13691 * Range intersection.. the hard stuff...
13695 * [ -- selected range --- ]
13699 * if end is before start or hits it. fail.
13700 * if start is after end or hits it fail.
13702 * if either hits (but other is outside. - then it's not
13708 // @see http://www.thismuchiknow.co.uk/?p=64.
13709 rangeIntersectsNode : function(range, node)
13711 var nodeRange = node.ownerDocument.createRange();
13713 nodeRange.selectNode(node);
13715 nodeRange.selectNodeContents(node);
13718 var rangeStartRange = range.cloneRange();
13719 rangeStartRange.collapse(true);
13721 var rangeEndRange = range.cloneRange();
13722 rangeEndRange.collapse(false);
13724 var nodeStartRange = nodeRange.cloneRange();
13725 nodeStartRange.collapse(true);
13727 var nodeEndRange = nodeRange.cloneRange();
13728 nodeEndRange.collapse(false);
13730 return rangeStartRange.compareBoundaryPoints(
13731 Range.START_TO_START, nodeEndRange) == -1 &&
13732 rangeEndRange.compareBoundaryPoints(
13733 Range.START_TO_START, nodeStartRange) == 1;
13737 rangeCompareNode : function(range, node)
13739 var nodeRange = node.ownerDocument.createRange();
13741 nodeRange.selectNode(node);
13743 nodeRange.selectNodeContents(node);
13747 range.collapse(true);
13749 nodeRange.collapse(true);
13751 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13752 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13754 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13756 var nodeIsBefore = ss == 1;
13757 var nodeIsAfter = ee == -1;
13759 if (nodeIsBefore && nodeIsAfter)
13761 if (!nodeIsBefore && nodeIsAfter)
13762 return 1; //right trailed.
13764 if (nodeIsBefore && !nodeIsAfter)
13765 return 2; // left trailed.
13770 // private? - in a new class?
13771 cleanUpPaste : function()
13773 // cleans up the whole document..
13774 Roo.log('cleanuppaste');
13776 this.cleanUpChildren(this.doc.body);
13777 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13778 if (clean != this.doc.body.innerHTML) {
13779 this.doc.body.innerHTML = clean;
13784 cleanWordChars : function(input) {// change the chars to hex code
13785 var he = Roo.HtmlEditorCore;
13787 var output = input;
13788 Roo.each(he.swapCodes, function(sw) {
13789 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13791 output = output.replace(swapper, sw[1]);
13798 cleanUpChildren : function (n)
13800 if (!n.childNodes.length) {
13803 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13804 this.cleanUpChild(n.childNodes[i]);
13811 cleanUpChild : function (node)
13814 //console.log(node);
13815 if (node.nodeName == "#text") {
13816 // clean up silly Windows -- stuff?
13819 if (node.nodeName == "#comment") {
13820 node.parentNode.removeChild(node);
13821 // clean up silly Windows -- stuff?
13825 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13827 node.parentNode.removeChild(node);
13832 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13834 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13835 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13837 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13838 // remove_keep_children = true;
13841 if (remove_keep_children) {
13842 this.cleanUpChildren(node);
13843 // inserts everything just before this node...
13844 while (node.childNodes.length) {
13845 var cn = node.childNodes[0];
13846 node.removeChild(cn);
13847 node.parentNode.insertBefore(cn, node);
13849 node.parentNode.removeChild(node);
13853 if (!node.attributes || !node.attributes.length) {
13854 this.cleanUpChildren(node);
13858 function cleanAttr(n,v)
13861 if (v.match(/^\./) || v.match(/^\//)) {
13864 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13867 if (v.match(/^#/)) {
13870 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13871 node.removeAttribute(n);
13875 function cleanStyle(n,v)
13877 if (v.match(/expression/)) { //XSS?? should we even bother..
13878 node.removeAttribute(n);
13881 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13882 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13885 var parts = v.split(/;/);
13888 Roo.each(parts, function(p) {
13889 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13893 var l = p.split(':').shift().replace(/\s+/g,'');
13894 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13896 if ( cblack.indexOf(l) > -1) {
13897 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13898 //node.removeAttribute(n);
13902 // only allow 'c whitelisted system attributes'
13903 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13904 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13905 //node.removeAttribute(n);
13915 if (clean.length) {
13916 node.setAttribute(n, clean.join(';'));
13918 node.removeAttribute(n);
13924 for (var i = node.attributes.length-1; i > -1 ; i--) {
13925 var a = node.attributes[i];
13928 if (a.name.toLowerCase().substr(0,2)=='on') {
13929 node.removeAttribute(a.name);
13932 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13933 node.removeAttribute(a.name);
13936 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13937 cleanAttr(a.name,a.value); // fixme..
13940 if (a.name == 'style') {
13941 cleanStyle(a.name,a.value);
13944 /// clean up MS crap..
13945 // tecnically this should be a list of valid class'es..
13948 if (a.name == 'class') {
13949 if (a.value.match(/^Mso/)) {
13950 node.className = '';
13953 if (a.value.match(/body/)) {
13954 node.className = '';
13965 this.cleanUpChildren(node);
13971 // hide stuff that is not compatible
13985 * @event specialkey
13989 * @cfg {String} fieldClass @hide
13992 * @cfg {String} focusClass @hide
13995 * @cfg {String} autoCreate @hide
13998 * @cfg {String} inputType @hide
14001 * @cfg {String} invalidClass @hide
14004 * @cfg {String} invalidText @hide
14007 * @cfg {String} msgFx @hide
14010 * @cfg {String} validateOnBlur @hide
14014 Roo.HtmlEditorCore.white = [
14015 'area', 'br', 'img', 'input', 'hr', 'wbr',
14017 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14018 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14019 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14020 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14021 'table', 'ul', 'xmp',
14023 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14026 'dir', 'menu', 'ol', 'ul', 'dl',
14032 Roo.HtmlEditorCore.black = [
14033 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14035 'base', 'basefont', 'bgsound', 'blink', 'body',
14036 'frame', 'frameset', 'head', 'html', 'ilayer',
14037 'iframe', 'layer', 'link', 'meta', 'object',
14038 'script', 'style' ,'title', 'xml' // clean later..
14040 Roo.HtmlEditorCore.clean = [
14041 'script', 'style', 'title', 'xml'
14043 Roo.HtmlEditorCore.remove = [
14048 Roo.HtmlEditorCore.ablack = [
14052 Roo.HtmlEditorCore.aclean = [
14053 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14057 Roo.HtmlEditorCore.pwhite= [
14058 'http', 'https', 'mailto'
14061 // white listed style attributes.
14062 Roo.HtmlEditorCore.cwhite= [
14063 // 'text-align', /// default is to allow most things..
14069 // black listed style attributes.
14070 Roo.HtmlEditorCore.cblack= [
14071 // 'font-size' -- this can be set by the project
14075 Roo.HtmlEditorCore.swapCodes =[
14094 * @class Roo.bootstrap.HtmlEditor
14095 * @extends Roo.bootstrap.TextArea
14096 * Bootstrap HtmlEditor class
14099 * Create a new HtmlEditor
14100 * @param {Object} config The config object
14103 Roo.bootstrap.HtmlEditor = function(config){
14104 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14105 if (!this.toolbars) {
14106 this.toolbars = [];
14108 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14111 * @event initialize
14112 * Fires when the editor is fully initialized (including the iframe)
14113 * @param {HtmlEditor} this
14118 * Fires when the editor is first receives the focus. Any insertion must wait
14119 * until after this event.
14120 * @param {HtmlEditor} this
14124 * @event beforesync
14125 * Fires before the textarea is updated with content from the editor iframe. Return false
14126 * to cancel the sync.
14127 * @param {HtmlEditor} this
14128 * @param {String} html
14132 * @event beforepush
14133 * Fires before the iframe editor is updated with content from the textarea. Return false
14134 * to cancel the push.
14135 * @param {HtmlEditor} this
14136 * @param {String} html
14141 * Fires when the textarea is updated with content from the editor iframe.
14142 * @param {HtmlEditor} this
14143 * @param {String} html
14148 * Fires when the iframe editor is updated with content from the textarea.
14149 * @param {HtmlEditor} this
14150 * @param {String} html
14154 * @event editmodechange
14155 * Fires when the editor switches edit modes
14156 * @param {HtmlEditor} this
14157 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14159 editmodechange: true,
14161 * @event editorevent
14162 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14163 * @param {HtmlEditor} this
14167 * @event firstfocus
14168 * Fires when on first focus - needed by toolbars..
14169 * @param {HtmlEditor} this
14174 * Auto save the htmlEditor value as a file into Events
14175 * @param {HtmlEditor} this
14179 * @event savedpreview
14180 * preview the saved version of htmlEditor
14181 * @param {HtmlEditor} this
14188 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14192 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14197 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14202 * @cfg {Number} height (in pixels)
14206 * @cfg {Number} width (in pixels)
14211 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14214 stylesheets: false,
14219 // private properties
14220 validationEvent : false,
14222 initialized : false,
14225 onFocus : Roo.emptyFn,
14227 hideMode:'offsets',
14230 tbContainer : false,
14232 toolbarContainer :function() {
14233 return this.wrap.select('.x-html-editor-tb',true).first();
14237 * Protected method that will not generally be called directly. It
14238 * is called when the editor creates its toolbar. Override this method if you need to
14239 * add custom toolbar buttons.
14240 * @param {HtmlEditor} editor
14242 createToolbar : function(){
14244 Roo.log("create toolbars");
14246 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14247 this.toolbars[0].render(this.toolbarContainer());
14251 // if (!editor.toolbars || !editor.toolbars.length) {
14252 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14255 // for (var i =0 ; i < editor.toolbars.length;i++) {
14256 // editor.toolbars[i] = Roo.factory(
14257 // typeof(editor.toolbars[i]) == 'string' ?
14258 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14259 // Roo.bootstrap.HtmlEditor);
14260 // editor.toolbars[i].init(editor);
14266 onRender : function(ct, position)
14268 // Roo.log("Call onRender: " + this.xtype);
14270 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14272 this.wrap = this.inputEl().wrap({
14273 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14276 this.editorcore.onRender(ct, position);
14278 if (this.resizable) {
14279 this.resizeEl = new Roo.Resizable(this.wrap, {
14283 minHeight : this.height,
14284 height: this.height,
14285 handles : this.resizable,
14288 resize : function(r, w, h) {
14289 _t.onResize(w,h); // -something
14295 this.createToolbar(this);
14298 if(!this.width && this.resizable){
14299 this.setSize(this.wrap.getSize());
14301 if (this.resizeEl) {
14302 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14303 // should trigger onReize..
14309 onResize : function(w, h)
14311 Roo.log('resize: ' +w + ',' + h );
14312 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14316 if(this.inputEl() ){
14317 if(typeof w == 'number'){
14318 var aw = w - this.wrap.getFrameWidth('lr');
14319 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14322 if(typeof h == 'number'){
14323 var tbh = -11; // fixme it needs to tool bar size!
14324 for (var i =0; i < this.toolbars.length;i++) {
14325 // fixme - ask toolbars for heights?
14326 tbh += this.toolbars[i].el.getHeight();
14327 //if (this.toolbars[i].footer) {
14328 // tbh += this.toolbars[i].footer.el.getHeight();
14336 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14337 ah -= 5; // knock a few pixes off for look..
14338 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14342 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14343 this.editorcore.onResize(ew,eh);
14348 * Toggles the editor between standard and source edit mode.
14349 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14351 toggleSourceEdit : function(sourceEditMode)
14353 this.editorcore.toggleSourceEdit(sourceEditMode);
14355 if(this.editorcore.sourceEditMode){
14356 Roo.log('editor - showing textarea');
14359 // Roo.log(this.syncValue());
14361 this.inputEl().removeClass('hide');
14362 this.inputEl().dom.removeAttribute('tabIndex');
14363 this.inputEl().focus();
14365 Roo.log('editor - hiding textarea');
14367 // Roo.log(this.pushValue());
14370 this.inputEl().addClass('hide');
14371 this.inputEl().dom.setAttribute('tabIndex', -1);
14372 //this.deferFocus();
14375 if(this.resizable){
14376 this.setSize(this.wrap.getSize());
14379 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14382 // private (for BoxComponent)
14383 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14385 // private (for BoxComponent)
14386 getResizeEl : function(){
14390 // private (for BoxComponent)
14391 getPositionEl : function(){
14396 initEvents : function(){
14397 this.originalValue = this.getValue();
14401 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14404 // markInvalid : Roo.emptyFn,
14406 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14409 // clearInvalid : Roo.emptyFn,
14411 setValue : function(v){
14412 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14413 this.editorcore.pushValue();
14418 deferFocus : function(){
14419 this.focus.defer(10, this);
14423 focus : function(){
14424 this.editorcore.focus();
14430 onDestroy : function(){
14436 for (var i =0; i < this.toolbars.length;i++) {
14437 // fixme - ask toolbars for heights?
14438 this.toolbars[i].onDestroy();
14441 this.wrap.dom.innerHTML = '';
14442 this.wrap.remove();
14447 onFirstFocus : function(){
14448 //Roo.log("onFirstFocus");
14449 this.editorcore.onFirstFocus();
14450 for (var i =0; i < this.toolbars.length;i++) {
14451 this.toolbars[i].onFirstFocus();
14457 syncValue : function()
14459 this.editorcore.syncValue();
14462 pushValue : function()
14464 this.editorcore.pushValue();
14468 // hide stuff that is not compatible
14482 * @event specialkey
14486 * @cfg {String} fieldClass @hide
14489 * @cfg {String} focusClass @hide
14492 * @cfg {String} autoCreate @hide
14495 * @cfg {String} inputType @hide
14498 * @cfg {String} invalidClass @hide
14501 * @cfg {String} invalidText @hide
14504 * @cfg {String} msgFx @hide
14507 * @cfg {String} validateOnBlur @hide
14518 * @class Roo.bootstrap.HtmlEditorToolbar1
14523 new Roo.bootstrap.HtmlEditor({
14526 new Roo.bootstrap.HtmlEditorToolbar1({
14527 disable : { fonts: 1 , format: 1, ..., ... , ...],
14533 * @cfg {Object} disable List of elements to disable..
14534 * @cfg {Array} btns List of additional buttons.
14538 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14541 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14544 Roo.apply(this, config);
14546 // default disabled, based on 'good practice'..
14547 this.disable = this.disable || {};
14548 Roo.applyIf(this.disable, {
14551 specialElements : true
14553 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14555 this.editor = config.editor;
14556 this.editorcore = config.editor.editorcore;
14558 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14560 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14561 // dont call parent... till later.
14563 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14569 editorcore : false,
14574 "h1","h2","h3","h4","h5","h6",
14576 "abbr", "acronym", "address", "cite", "samp", "var",
14580 onRender : function(ct, position)
14582 // Roo.log("Call onRender: " + this.xtype);
14584 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14586 this.el.dom.style.marginBottom = '0';
14588 var editorcore = this.editorcore;
14589 var editor= this.editor;
14592 var btn = function(id,cmd , toggle, handler){
14594 var event = toggle ? 'toggle' : 'click';
14599 xns: Roo.bootstrap,
14602 enableToggle:toggle !== false,
14604 pressed : toggle ? false : null,
14607 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14608 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14617 xns: Roo.bootstrap,
14618 glyphicon : 'font',
14622 xns: Roo.bootstrap,
14626 Roo.each(this.formats, function(f) {
14627 style.menu.items.push({
14629 xns: Roo.bootstrap,
14630 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14635 editorcore.insertTag(this.tagname);
14642 children.push(style);
14645 btn('bold',false,true);
14646 btn('italic',false,true);
14647 btn('align-left', 'justifyleft',true);
14648 btn('align-center', 'justifycenter',true);
14649 btn('align-right' , 'justifyright',true);
14650 btn('link', false, false, function(btn) {
14651 //Roo.log("create link?");
14652 var url = prompt(this.createLinkText, this.defaultLinkValue);
14653 if(url && url != 'http:/'+'/'){
14654 this.editorcore.relayCmd('createlink', url);
14657 btn('list','insertunorderedlist',true);
14658 btn('pencil', false,true, function(btn){
14661 this.toggleSourceEdit(btn.pressed);
14667 xns: Roo.bootstrap,
14672 xns: Roo.bootstrap,
14677 cog.menu.items.push({
14679 xns: Roo.bootstrap,
14680 html : Clean styles,
14685 editorcore.insertTag(this.tagname);
14694 this.xtype = 'Navbar';
14696 for(var i=0;i< children.length;i++) {
14698 this.buttons.add(this.addxtypeChild(children[i]));
14702 editor.on('editorevent', this.updateToolbar, this);
14704 onBtnClick : function(id)
14706 this.editorcore.relayCmd(id);
14707 this.editorcore.focus();
14711 * Protected method that will not generally be called directly. It triggers
14712 * a toolbar update by reading the markup state of the current selection in the editor.
14714 updateToolbar: function(){
14716 if(!this.editorcore.activated){
14717 this.editor.onFirstFocus(); // is this neeed?
14721 var btns = this.buttons;
14722 var doc = this.editorcore.doc;
14723 btns.get('bold').setActive(doc.queryCommandState('bold'));
14724 btns.get('italic').setActive(doc.queryCommandState('italic'));
14725 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14727 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14728 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14729 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14731 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14732 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14735 var ans = this.editorcore.getAllAncestors();
14736 if (this.formatCombo) {
14739 var store = this.formatCombo.store;
14740 this.formatCombo.setValue("");
14741 for (var i =0; i < ans.length;i++) {
14742 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14744 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14752 // hides menus... - so this cant be on a menu...
14753 Roo.bootstrap.MenuMgr.hideAll();
14755 Roo.bootstrap.MenuMgr.hideAll();
14756 //this.editorsyncValue();
14758 onFirstFocus: function() {
14759 this.buttons.each(function(item){
14763 toggleSourceEdit : function(sourceEditMode){
14766 if(sourceEditMode){
14767 Roo.log("disabling buttons");
14768 this.buttons.each( function(item){
14769 if(item.cmd != 'pencil'){
14775 Roo.log("enabling buttons");
14776 if(this.editorcore.initialized){
14777 this.buttons.each( function(item){
14783 Roo.log("calling toggole on editor");
14784 // tell the editor that it's been pressed..
14785 this.editor.toggleSourceEdit(sourceEditMode);
14795 * @class Roo.bootstrap.Table.AbstractSelectionModel
14796 * @extends Roo.util.Observable
14797 * Abstract base class for grid SelectionModels. It provides the interface that should be
14798 * implemented by descendant classes. This class should not be directly instantiated.
14801 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14802 this.locked = false;
14803 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14807 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14808 /** @ignore Called by the grid automatically. Do not call directly. */
14809 init : function(grid){
14815 * Locks the selections.
14818 this.locked = true;
14822 * Unlocks the selections.
14824 unlock : function(){
14825 this.locked = false;
14829 * Returns true if the selections are locked.
14830 * @return {Boolean}
14832 isLocked : function(){
14833 return this.locked;
14837 * @class Roo.bootstrap.Table.ColumnModel
14838 * @extends Roo.util.Observable
14839 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14840 * the columns in the table.
14843 * @param {Object} config An Array of column config objects. See this class's
14844 * config objects for details.
14846 Roo.bootstrap.Table.ColumnModel = function(config){
14848 * The config passed into the constructor
14850 this.config = config;
14853 // if no id, create one
14854 // if the column does not have a dataIndex mapping,
14855 // map it to the order it is in the config
14856 for(var i = 0, len = config.length; i < len; i++){
14858 if(typeof c.dataIndex == "undefined"){
14861 if(typeof c.renderer == "string"){
14862 c.renderer = Roo.util.Format[c.renderer];
14864 if(typeof c.id == "undefined"){
14867 // if(c.editor && c.editor.xtype){
14868 // c.editor = Roo.factory(c.editor, Roo.grid);
14870 // if(c.editor && c.editor.isFormField){
14871 // c.editor = new Roo.grid.GridEditor(c.editor);
14874 this.lookup[c.id] = c;
14878 * The width of columns which have no width specified (defaults to 100)
14881 this.defaultWidth = 100;
14884 * Default sortable of columns which have no sortable specified (defaults to false)
14887 this.defaultSortable = false;
14891 * @event widthchange
14892 * Fires when the width of a column changes.
14893 * @param {ColumnModel} this
14894 * @param {Number} columnIndex The column index
14895 * @param {Number} newWidth The new width
14897 "widthchange": true,
14899 * @event headerchange
14900 * Fires when the text of a header changes.
14901 * @param {ColumnModel} this
14902 * @param {Number} columnIndex The column index
14903 * @param {Number} newText The new header text
14905 "headerchange": true,
14907 * @event hiddenchange
14908 * Fires when a column is hidden or "unhidden".
14909 * @param {ColumnModel} this
14910 * @param {Number} columnIndex The column index
14911 * @param {Boolean} hidden true if hidden, false otherwise
14913 "hiddenchange": true,
14915 * @event columnmoved
14916 * Fires when a column is moved.
14917 * @param {ColumnModel} this
14918 * @param {Number} oldIndex
14919 * @param {Number} newIndex
14921 "columnmoved" : true,
14923 * @event columlockchange
14924 * Fires when a column's locked state is changed
14925 * @param {ColumnModel} this
14926 * @param {Number} colIndex
14927 * @param {Boolean} locked true if locked
14929 "columnlockchange" : true
14931 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14933 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14935 * @cfg {String} header The header text to display in the Grid view.
14938 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14939 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14940 * specified, the column's index is used as an index into the Record's data Array.
14943 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14944 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14947 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14948 * Defaults to the value of the {@link #defaultSortable} property.
14949 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14952 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14955 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14958 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14961 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14964 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14965 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14966 * default renderer uses the raw data value.
14969 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14973 * Returns the id of the column at the specified index.
14974 * @param {Number} index The column index
14975 * @return {String} the id
14977 getColumnId : function(index){
14978 return this.config[index].id;
14982 * Returns the column for a specified id.
14983 * @param {String} id The column id
14984 * @return {Object} the column
14986 getColumnById : function(id){
14987 return this.lookup[id];
14992 * Returns the column for a specified dataIndex.
14993 * @param {String} dataIndex The column dataIndex
14994 * @return {Object|Boolean} the column or false if not found
14996 getColumnByDataIndex: function(dataIndex){
14997 var index = this.findColumnIndex(dataIndex);
14998 return index > -1 ? this.config[index] : false;
15002 * Returns the index for a specified column id.
15003 * @param {String} id The column id
15004 * @return {Number} the index, or -1 if not found
15006 getIndexById : function(id){
15007 for(var i = 0, len = this.config.length; i < len; i++){
15008 if(this.config[i].id == id){
15016 * Returns the index for a specified column dataIndex.
15017 * @param {String} dataIndex The column dataIndex
15018 * @return {Number} the index, or -1 if not found
15021 findColumnIndex : function(dataIndex){
15022 for(var i = 0, len = this.config.length; i < len; i++){
15023 if(this.config[i].dataIndex == dataIndex){
15031 moveColumn : function(oldIndex, newIndex){
15032 var c = this.config[oldIndex];
15033 this.config.splice(oldIndex, 1);
15034 this.config.splice(newIndex, 0, c);
15035 this.dataMap = null;
15036 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15039 isLocked : function(colIndex){
15040 return this.config[colIndex].locked === true;
15043 setLocked : function(colIndex, value, suppressEvent){
15044 if(this.isLocked(colIndex) == value){
15047 this.config[colIndex].locked = value;
15048 if(!suppressEvent){
15049 this.fireEvent("columnlockchange", this, colIndex, value);
15053 getTotalLockedWidth : function(){
15054 var totalWidth = 0;
15055 for(var i = 0; i < this.config.length; i++){
15056 if(this.isLocked(i) && !this.isHidden(i)){
15057 this.totalWidth += this.getColumnWidth(i);
15063 getLockedCount : function(){
15064 for(var i = 0, len = this.config.length; i < len; i++){
15065 if(!this.isLocked(i)){
15072 * Returns the number of columns.
15075 getColumnCount : function(visibleOnly){
15076 if(visibleOnly === true){
15078 for(var i = 0, len = this.config.length; i < len; i++){
15079 if(!this.isHidden(i)){
15085 return this.config.length;
15089 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15090 * @param {Function} fn
15091 * @param {Object} scope (optional)
15092 * @return {Array} result
15094 getColumnsBy : function(fn, scope){
15096 for(var i = 0, len = this.config.length; i < len; i++){
15097 var c = this.config[i];
15098 if(fn.call(scope||this, c, i) === true){
15106 * Returns true if the specified column is sortable.
15107 * @param {Number} col The column index
15108 * @return {Boolean}
15110 isSortable : function(col){
15111 if(typeof this.config[col].sortable == "undefined"){
15112 return this.defaultSortable;
15114 return this.config[col].sortable;
15118 * Returns the rendering (formatting) function defined for the column.
15119 * @param {Number} col The column index.
15120 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15122 getRenderer : function(col){
15123 if(!this.config[col].renderer){
15124 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15126 return this.config[col].renderer;
15130 * Sets the rendering (formatting) function for a column.
15131 * @param {Number} col The column index
15132 * @param {Function} fn The function to use to process the cell's raw data
15133 * to return HTML markup for the grid view. The render function is called with
15134 * the following parameters:<ul>
15135 * <li>Data value.</li>
15136 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15137 * <li>css A CSS style string to apply to the table cell.</li>
15138 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15139 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15140 * <li>Row index</li>
15141 * <li>Column index</li>
15142 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15144 setRenderer : function(col, fn){
15145 this.config[col].renderer = fn;
15149 * Returns the width for the specified column.
15150 * @param {Number} col The column index
15153 getColumnWidth : function(col){
15154 return this.config[col].width * 1 || this.defaultWidth;
15158 * Sets the width for a column.
15159 * @param {Number} col The column index
15160 * @param {Number} width The new width
15162 setColumnWidth : function(col, width, suppressEvent){
15163 this.config[col].width = width;
15164 this.totalWidth = null;
15165 if(!suppressEvent){
15166 this.fireEvent("widthchange", this, col, width);
15171 * Returns the total width of all columns.
15172 * @param {Boolean} includeHidden True to include hidden column widths
15175 getTotalWidth : function(includeHidden){
15176 if(!this.totalWidth){
15177 this.totalWidth = 0;
15178 for(var i = 0, len = this.config.length; i < len; i++){
15179 if(includeHidden || !this.isHidden(i)){
15180 this.totalWidth += this.getColumnWidth(i);
15184 return this.totalWidth;
15188 * Returns the header for the specified column.
15189 * @param {Number} col The column index
15192 getColumnHeader : function(col){
15193 return this.config[col].header;
15197 * Sets the header for a column.
15198 * @param {Number} col The column index
15199 * @param {String} header The new header
15201 setColumnHeader : function(col, header){
15202 this.config[col].header = header;
15203 this.fireEvent("headerchange", this, col, header);
15207 * Returns the tooltip for the specified column.
15208 * @param {Number} col The column index
15211 getColumnTooltip : function(col){
15212 return this.config[col].tooltip;
15215 * Sets the tooltip for a column.
15216 * @param {Number} col The column index
15217 * @param {String} tooltip The new tooltip
15219 setColumnTooltip : function(col, tooltip){
15220 this.config[col].tooltip = tooltip;
15224 * Returns the dataIndex for the specified column.
15225 * @param {Number} col The column index
15228 getDataIndex : function(col){
15229 return this.config[col].dataIndex;
15233 * Sets the dataIndex for a column.
15234 * @param {Number} col The column index
15235 * @param {Number} dataIndex The new dataIndex
15237 setDataIndex : function(col, dataIndex){
15238 this.config[col].dataIndex = dataIndex;
15244 * Returns true if the cell is editable.
15245 * @param {Number} colIndex The column index
15246 * @param {Number} rowIndex The row index
15247 * @return {Boolean}
15249 isCellEditable : function(colIndex, rowIndex){
15250 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15254 * Returns the editor defined for the cell/column.
15255 * return false or null to disable editing.
15256 * @param {Number} colIndex The column index
15257 * @param {Number} rowIndex The row index
15260 getCellEditor : function(colIndex, rowIndex){
15261 return this.config[colIndex].editor;
15265 * Sets if a column is editable.
15266 * @param {Number} col The column index
15267 * @param {Boolean} editable True if the column is editable
15269 setEditable : function(col, editable){
15270 this.config[col].editable = editable;
15275 * Returns true if the column is hidden.
15276 * @param {Number} colIndex The column index
15277 * @return {Boolean}
15279 isHidden : function(colIndex){
15280 return this.config[colIndex].hidden;
15285 * Returns true if the column width cannot be changed
15287 isFixed : function(colIndex){
15288 return this.config[colIndex].fixed;
15292 * Returns true if the column can be resized
15293 * @return {Boolean}
15295 isResizable : function(colIndex){
15296 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15299 * Sets if a column is hidden.
15300 * @param {Number} colIndex The column index
15301 * @param {Boolean} hidden True if the column is hidden
15303 setHidden : function(colIndex, hidden){
15304 this.config[colIndex].hidden = hidden;
15305 this.totalWidth = null;
15306 this.fireEvent("hiddenchange", this, colIndex, hidden);
15310 * Sets the editor for a column.
15311 * @param {Number} col The column index
15312 * @param {Object} editor The editor object
15314 setEditor : function(col, editor){
15315 this.config[col].editor = editor;
15319 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15320 if(typeof value == "string" && value.length < 1){
15326 // Alias for backwards compatibility
15327 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15330 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15331 * @class Roo.bootstrap.Table.RowSelectionModel
15332 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15333 * It supports multiple selections and keyboard selection/navigation.
15335 * @param {Object} config
15338 Roo.bootstrap.Table.RowSelectionModel = function(config){
15339 Roo.apply(this, config);
15340 this.selections = new Roo.util.MixedCollection(false, function(o){
15345 this.lastActive = false;
15349 * @event selectionchange
15350 * Fires when the selection changes
15351 * @param {SelectionModel} this
15353 "selectionchange" : true,
15355 * @event afterselectionchange
15356 * Fires after the selection changes (eg. by key press or clicking)
15357 * @param {SelectionModel} this
15359 "afterselectionchange" : true,
15361 * @event beforerowselect
15362 * Fires when a row is selected being selected, return false to cancel.
15363 * @param {SelectionModel} this
15364 * @param {Number} rowIndex The selected index
15365 * @param {Boolean} keepExisting False if other selections will be cleared
15367 "beforerowselect" : true,
15370 * Fires when a row is selected.
15371 * @param {SelectionModel} this
15372 * @param {Number} rowIndex The selected index
15373 * @param {Roo.data.Record} r The record
15375 "rowselect" : true,
15377 * @event rowdeselect
15378 * Fires when a row is deselected.
15379 * @param {SelectionModel} this
15380 * @param {Number} rowIndex The selected index
15382 "rowdeselect" : true
15384 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15385 this.locked = false;
15388 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15390 * @cfg {Boolean} singleSelect
15391 * True to allow selection of only one row at a time (defaults to false)
15393 singleSelect : false,
15396 initEvents : function(){
15398 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15399 this.grid.on("mousedown", this.handleMouseDown, this);
15400 }else{ // allow click to work like normal
15401 this.grid.on("rowclick", this.handleDragableRowClick, this);
15404 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15405 "up" : function(e){
15407 this.selectPrevious(e.shiftKey);
15408 }else if(this.last !== false && this.lastActive !== false){
15409 var last = this.last;
15410 this.selectRange(this.last, this.lastActive-1);
15411 this.grid.getView().focusRow(this.lastActive);
15412 if(last !== false){
15416 this.selectFirstRow();
15418 this.fireEvent("afterselectionchange", this);
15420 "down" : function(e){
15422 this.selectNext(e.shiftKey);
15423 }else if(this.last !== false && this.lastActive !== false){
15424 var last = this.last;
15425 this.selectRange(this.last, this.lastActive+1);
15426 this.grid.getView().focusRow(this.lastActive);
15427 if(last !== false){
15431 this.selectFirstRow();
15433 this.fireEvent("afterselectionchange", this);
15438 var view = this.grid.view;
15439 view.on("refresh", this.onRefresh, this);
15440 view.on("rowupdated", this.onRowUpdated, this);
15441 view.on("rowremoved", this.onRemove, this);
15445 onRefresh : function(){
15446 var ds = this.grid.dataSource, i, v = this.grid.view;
15447 var s = this.selections;
15448 s.each(function(r){
15449 if((i = ds.indexOfId(r.id)) != -1){
15458 onRemove : function(v, index, r){
15459 this.selections.remove(r);
15463 onRowUpdated : function(v, index, r){
15464 if(this.isSelected(r)){
15465 v.onRowSelect(index);
15471 * @param {Array} records The records to select
15472 * @param {Boolean} keepExisting (optional) True to keep existing selections
15474 selectRecords : function(records, keepExisting){
15476 this.clearSelections();
15478 var ds = this.grid.dataSource;
15479 for(var i = 0, len = records.length; i < len; i++){
15480 this.selectRow(ds.indexOf(records[i]), true);
15485 * Gets the number of selected rows.
15488 getCount : function(){
15489 return this.selections.length;
15493 * Selects the first row in the grid.
15495 selectFirstRow : function(){
15500 * Select the last row.
15501 * @param {Boolean} keepExisting (optional) True to keep existing selections
15503 selectLastRow : function(keepExisting){
15504 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15508 * Selects the row immediately following the last selected row.
15509 * @param {Boolean} keepExisting (optional) True to keep existing selections
15511 selectNext : function(keepExisting){
15512 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15513 this.selectRow(this.last+1, keepExisting);
15514 this.grid.getView().focusRow(this.last);
15519 * Selects the row that precedes the last selected row.
15520 * @param {Boolean} keepExisting (optional) True to keep existing selections
15522 selectPrevious : function(keepExisting){
15524 this.selectRow(this.last-1, keepExisting);
15525 this.grid.getView().focusRow(this.last);
15530 * Returns the selected records
15531 * @return {Array} Array of selected records
15533 getSelections : function(){
15534 return [].concat(this.selections.items);
15538 * Returns the first selected record.
15541 getSelected : function(){
15542 return this.selections.itemAt(0);
15547 * Clears all selections.
15549 clearSelections : function(fast){
15550 if(this.locked) return;
15552 var ds = this.grid.dataSource;
15553 var s = this.selections;
15554 s.each(function(r){
15555 this.deselectRow(ds.indexOfId(r.id));
15559 this.selections.clear();
15566 * Selects all rows.
15568 selectAll : function(){
15569 if(this.locked) return;
15570 this.selections.clear();
15571 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15572 this.selectRow(i, true);
15577 * Returns True if there is a selection.
15578 * @return {Boolean}
15580 hasSelection : function(){
15581 return this.selections.length > 0;
15585 * Returns True if the specified row is selected.
15586 * @param {Number/Record} record The record or index of the record to check
15587 * @return {Boolean}
15589 isSelected : function(index){
15590 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15591 return (r && this.selections.key(r.id) ? true : false);
15595 * Returns True if the specified record id is selected.
15596 * @param {String} id The id of record to check
15597 * @return {Boolean}
15599 isIdSelected : function(id){
15600 return (this.selections.key(id) ? true : false);
15604 handleMouseDown : function(e, t){
15605 var view = this.grid.getView(), rowIndex;
15606 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15609 if(e.shiftKey && this.last !== false){
15610 var last = this.last;
15611 this.selectRange(last, rowIndex, e.ctrlKey);
15612 this.last = last; // reset the last
15613 view.focusRow(rowIndex);
15615 var isSelected = this.isSelected(rowIndex);
15616 if(e.button !== 0 && isSelected){
15617 view.focusRow(rowIndex);
15618 }else if(e.ctrlKey && isSelected){
15619 this.deselectRow(rowIndex);
15620 }else if(!isSelected){
15621 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15622 view.focusRow(rowIndex);
15625 this.fireEvent("afterselectionchange", this);
15628 handleDragableRowClick : function(grid, rowIndex, e)
15630 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15631 this.selectRow(rowIndex, false);
15632 grid.view.focusRow(rowIndex);
15633 this.fireEvent("afterselectionchange", this);
15638 * Selects multiple rows.
15639 * @param {Array} rows Array of the indexes of the row to select
15640 * @param {Boolean} keepExisting (optional) True to keep existing selections
15642 selectRows : function(rows, keepExisting){
15644 this.clearSelections();
15646 for(var i = 0, len = rows.length; i < len; i++){
15647 this.selectRow(rows[i], true);
15652 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15653 * @param {Number} startRow The index of the first row in the range
15654 * @param {Number} endRow The index of the last row in the range
15655 * @param {Boolean} keepExisting (optional) True to retain existing selections
15657 selectRange : function(startRow, endRow, keepExisting){
15658 if(this.locked) return;
15660 this.clearSelections();
15662 if(startRow <= endRow){
15663 for(var i = startRow; i <= endRow; i++){
15664 this.selectRow(i, true);
15667 for(var i = startRow; i >= endRow; i--){
15668 this.selectRow(i, true);
15674 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15675 * @param {Number} startRow The index of the first row in the range
15676 * @param {Number} endRow The index of the last row in the range
15678 deselectRange : function(startRow, endRow, preventViewNotify){
15679 if(this.locked) return;
15680 for(var i = startRow; i <= endRow; i++){
15681 this.deselectRow(i, preventViewNotify);
15687 * @param {Number} row The index of the row to select
15688 * @param {Boolean} keepExisting (optional) True to keep existing selections
15690 selectRow : function(index, keepExisting, preventViewNotify){
15691 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15692 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15693 if(!keepExisting || this.singleSelect){
15694 this.clearSelections();
15696 var r = this.grid.dataSource.getAt(index);
15697 this.selections.add(r);
15698 this.last = this.lastActive = index;
15699 if(!preventViewNotify){
15700 this.grid.getView().onRowSelect(index);
15702 this.fireEvent("rowselect", this, index, r);
15703 this.fireEvent("selectionchange", this);
15709 * @param {Number} row The index of the row to deselect
15711 deselectRow : function(index, preventViewNotify){
15712 if(this.locked) return;
15713 if(this.last == index){
15716 if(this.lastActive == index){
15717 this.lastActive = false;
15719 var r = this.grid.dataSource.getAt(index);
15720 this.selections.remove(r);
15721 if(!preventViewNotify){
15722 this.grid.getView().onRowDeselect(index);
15724 this.fireEvent("rowdeselect", this, index);
15725 this.fireEvent("selectionchange", this);
15729 restoreLast : function(){
15731 this.last = this._last;
15736 acceptsNav : function(row, col, cm){
15737 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15741 onEditorKey : function(field, e){
15742 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15747 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15749 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15751 }else if(k == e.ENTER && !e.ctrlKey){
15755 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15757 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15759 }else if(k == e.ESC){
15763 g.startEditing(newCell[0], newCell[1]);
15774 * @class Roo.bootstrap.MessageBar
15775 * @extends Roo.bootstrap.Component
15776 * Bootstrap MessageBar class
15777 * @cfg {String} html contents of the MessageBar
15778 * @cfg {String} weight (info | success | warning | danger) default info
15779 * @cfg {String} beforeClass insert the bar before the given class
15780 * @cfg {Boolean} closable (true | false) default false
15781 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15784 * Create a new Element
15785 * @param {Object} config The config object
15788 Roo.bootstrap.MessageBar = function(config){
15789 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15792 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15798 beforeClass: 'bootstrap-sticky-wrap',
15800 getAutoCreate : function(){
15804 cls: 'alert alert-dismissable alert-' + this.weight,
15809 html: this.html || ''
15815 cfg.cls += ' alert-messages-fixed';
15829 onRender : function(ct, position)
15831 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15834 var cfg = Roo.apply({}, this.getAutoCreate());
15838 cfg.cls += ' ' + this.cls;
15841 cfg.style = this.style;
15843 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15845 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15848 this.el.select('>button.close').on('click', this.hide, this);
15854 if (!this.rendered) {
15860 this.fireEvent('show', this);
15866 if (!this.rendered) {
15872 this.fireEvent('hide', this);
15875 update : function()
15877 // var e = this.el.dom.firstChild;
15879 // if(this.closable){
15880 // e = e.nextSibling;
15883 // e.data = this.html || '';
15885 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';