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(e,el)
2973 var col = Roo.get(el)
2975 if(!col.hasClass('sortable')){
2979 var sort = col.attr('sort');
2982 if(col.hasClass('glyphicon-arrow-up')){
2986 this.store.sortInfo = {field : sort, direction : dir};
2991 renderHeader : function()
3000 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3002 var config = cm.config[i];
3006 html: cm.getColumnHeader(i)
3009 if(typeof(config.dataIndex) != 'undefined'){
3010 c.sort = config.dataIndex;
3013 if(typeof(config.sortable) != 'undefined' && config.sortable){
3017 if(typeof(config.width) != 'undefined'){
3018 c.style = 'width:' + config.width + 'px';
3027 renderBody : function()
3037 renderFooter : function()
3049 Roo.log('ds onload');
3054 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3055 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3057 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3058 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3061 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3062 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3066 var tbody = this.el.select('tbody', true).first();
3070 if(this.store.getCount() > 0){
3071 this.store.data.each(function(d){
3077 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3078 var renderer = cm.getRenderer(i);
3079 var config = cm.config[i];
3083 if(typeof(renderer) !== 'undefined'){
3084 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3087 if(typeof(value) === 'object'){
3097 html: (typeof(value) === 'object') ? '' : value
3100 if(typeof(config.width) != 'undefined'){
3101 td.style = 'width:' + config.width + 'px';
3108 tbody.createChild(row);
3116 Roo.each(renders, function(r){
3117 _this.renderColumn(r);
3121 // if(this.loadMask){
3122 // this.maskEl.hide();
3126 onBeforeLoad : function()
3128 Roo.log('ds onBeforeLoad');
3132 // if(this.loadMask){
3133 // this.maskEl.show();
3139 this.el.select('tbody', true).first().dom.innerHTML = '';
3142 getSelectionModel : function(){
3144 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3146 return this.selModel;
3149 renderColumn : function(r)
3152 r.cfg.render(Roo.get(r.id));
3155 Roo.each(r.cfg.cn, function(c){
3160 _this.renderColumn(child);
3177 * @class Roo.bootstrap.TableCell
3178 * @extends Roo.bootstrap.Component
3179 * Bootstrap TableCell class
3180 * @cfg {String} html cell contain text
3181 * @cfg {String} cls cell class
3182 * @cfg {String} tag cell tag (td|th) default td
3183 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3184 * @cfg {String} align Aligns the content in a cell
3185 * @cfg {String} axis Categorizes cells
3186 * @cfg {String} bgcolor Specifies the background color of a cell
3187 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3188 * @cfg {Number} colspan Specifies the number of columns a cell should span
3189 * @cfg {String} headers Specifies one or more header cells a cell is related to
3190 * @cfg {Number} height Sets the height of a cell
3191 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3192 * @cfg {Number} rowspan Sets the number of rows a cell should span
3193 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3194 * @cfg {String} valign Vertical aligns the content in a cell
3195 * @cfg {Number} width Specifies the width of a cell
3198 * Create a new TableCell
3199 * @param {Object} config The config object
3202 Roo.bootstrap.TableCell = function(config){
3203 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3206 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3226 getAutoCreate : function(){
3227 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3247 cfg.align=this.align
3253 cfg.bgcolor=this.bgcolor
3256 cfg.charoff=this.charoff
3259 cfg.colspan=this.colspan
3262 cfg.headers=this.headers
3265 cfg.height=this.height
3268 cfg.nowrap=this.nowrap
3271 cfg.rowspan=this.rowspan
3274 cfg.scope=this.scope
3277 cfg.valign=this.valign
3280 cfg.width=this.width
3299 * @class Roo.bootstrap.TableRow
3300 * @extends Roo.bootstrap.Component
3301 * Bootstrap TableRow class
3302 * @cfg {String} cls row class
3303 * @cfg {String} align Aligns the content in a table row
3304 * @cfg {String} bgcolor Specifies a background color for a table row
3305 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3306 * @cfg {String} valign Vertical aligns the content in a table row
3309 * Create a new TableRow
3310 * @param {Object} config The config object
3313 Roo.bootstrap.TableRow = function(config){
3314 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3317 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3325 getAutoCreate : function(){
3326 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3336 cfg.align = this.align;
3339 cfg.bgcolor = this.bgcolor;
3342 cfg.charoff = this.charoff;
3345 cfg.valign = this.valign;
3363 * @class Roo.bootstrap.TableBody
3364 * @extends Roo.bootstrap.Component
3365 * Bootstrap TableBody class
3366 * @cfg {String} cls element class
3367 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3368 * @cfg {String} align Aligns the content inside the element
3369 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3370 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3373 * Create a new TableBody
3374 * @param {Object} config The config object
3377 Roo.bootstrap.TableBody = function(config){
3378 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3381 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3389 getAutoCreate : function(){
3390 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3404 cfg.align = this.align;
3407 cfg.charoff = this.charoff;
3410 cfg.valign = this.valign;
3417 // initEvents : function()
3424 // this.store = Roo.factory(this.store, Roo.data);
3425 // this.store.on('load', this.onLoad, this);
3427 // this.store.load();
3431 // onLoad: function ()
3433 // this.fireEvent('load', this);
3443 * Ext JS Library 1.1.1
3444 * Copyright(c) 2006-2007, Ext JS, LLC.
3446 * Originally Released Under LGPL - original licence link has changed is not relivant.
3449 * <script type="text/javascript">
3452 // as we use this in bootstrap.
3453 Roo.namespace('Roo.form');
3455 * @class Roo.form.Action
3456 * Internal Class used to handle form actions
3458 * @param {Roo.form.BasicForm} el The form element or its id
3459 * @param {Object} config Configuration options
3464 // define the action interface
3465 Roo.form.Action = function(form, options){
3467 this.options = options || {};
3470 * Client Validation Failed
3473 Roo.form.Action.CLIENT_INVALID = 'client';
3475 * Server Validation Failed
3478 Roo.form.Action.SERVER_INVALID = 'server';
3480 * Connect to Server Failed
3483 Roo.form.Action.CONNECT_FAILURE = 'connect';
3485 * Reading Data from Server Failed
3488 Roo.form.Action.LOAD_FAILURE = 'load';
3490 Roo.form.Action.prototype = {
3492 failureType : undefined,
3493 response : undefined,
3497 run : function(options){
3502 success : function(response){
3507 handleResponse : function(response){
3511 // default connection failure
3512 failure : function(response){
3514 this.response = response;
3515 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3516 this.form.afterAction(this, false);
3519 processResponse : function(response){
3520 this.response = response;
3521 if(!response.responseText){
3524 this.result = this.handleResponse(response);
3528 // utility functions used internally
3529 getUrl : function(appendParams){
3530 var url = this.options.url || this.form.url || this.form.el.dom.action;
3532 var p = this.getParams();
3534 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3540 getMethod : function(){
3541 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3544 getParams : function(){
3545 var bp = this.form.baseParams;
3546 var p = this.options.params;
3548 if(typeof p == "object"){
3549 p = Roo.urlEncode(Roo.applyIf(p, bp));
3550 }else if(typeof p == 'string' && bp){
3551 p += '&' + Roo.urlEncode(bp);
3554 p = Roo.urlEncode(bp);
3559 createCallback : function(){
3561 success: this.success,
3562 failure: this.failure,
3564 timeout: (this.form.timeout*1000),
3565 upload: this.form.fileUpload ? this.success : undefined
3570 Roo.form.Action.Submit = function(form, options){
3571 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3574 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3577 haveProgress : false,
3578 uploadComplete : false,
3580 // uploadProgress indicator.
3581 uploadProgress : function()
3583 if (!this.form.progressUrl) {
3587 if (!this.haveProgress) {
3588 Roo.MessageBox.progress("Uploading", "Uploading");
3590 if (this.uploadComplete) {
3591 Roo.MessageBox.hide();
3595 this.haveProgress = true;
3597 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3599 var c = new Roo.data.Connection();
3601 url : this.form.progressUrl,
3606 success : function(req){
3607 //console.log(data);
3611 rdata = Roo.decode(req.responseText)
3613 Roo.log("Invalid data from server..");
3617 if (!rdata || !rdata.success) {
3619 Roo.MessageBox.alert(Roo.encode(rdata));
3622 var data = rdata.data;
3624 if (this.uploadComplete) {
3625 Roo.MessageBox.hide();
3630 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3631 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3634 this.uploadProgress.defer(2000,this);
3637 failure: function(data) {
3638 Roo.log('progress url failed ');
3649 // run get Values on the form, so it syncs any secondary forms.
3650 this.form.getValues();
3652 var o = this.options;
3653 var method = this.getMethod();
3654 var isPost = method == 'POST';
3655 if(o.clientValidation === false || this.form.isValid()){
3657 if (this.form.progressUrl) {
3658 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3659 (new Date() * 1) + '' + Math.random());
3664 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3665 form:this.form.el.dom,
3666 url:this.getUrl(!isPost),
3668 params:isPost ? this.getParams() : null,
3669 isUpload: this.form.fileUpload
3672 this.uploadProgress();
3674 }else if (o.clientValidation !== false){ // client validation failed
3675 this.failureType = Roo.form.Action.CLIENT_INVALID;
3676 this.form.afterAction(this, false);
3680 success : function(response)
3682 this.uploadComplete= true;
3683 if (this.haveProgress) {
3684 Roo.MessageBox.hide();
3688 var result = this.processResponse(response);
3689 if(result === true || result.success){
3690 this.form.afterAction(this, true);
3694 this.form.markInvalid(result.errors);
3695 this.failureType = Roo.form.Action.SERVER_INVALID;
3697 this.form.afterAction(this, false);
3699 failure : function(response)
3701 this.uploadComplete= true;
3702 if (this.haveProgress) {
3703 Roo.MessageBox.hide();
3706 this.response = response;
3707 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3708 this.form.afterAction(this, false);
3711 handleResponse : function(response){
3712 if(this.form.errorReader){
3713 var rs = this.form.errorReader.read(response);
3716 for(var i = 0, len = rs.records.length; i < len; i++) {
3717 var r = rs.records[i];
3721 if(errors.length < 1){
3725 success : rs.success,
3731 ret = Roo.decode(response.responseText);
3735 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3745 Roo.form.Action.Load = function(form, options){
3746 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3747 this.reader = this.form.reader;
3750 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3755 Roo.Ajax.request(Roo.apply(
3756 this.createCallback(), {
3757 method:this.getMethod(),
3758 url:this.getUrl(false),
3759 params:this.getParams()
3763 success : function(response){
3765 var result = this.processResponse(response);
3766 if(result === true || !result.success || !result.data){
3767 this.failureType = Roo.form.Action.LOAD_FAILURE;
3768 this.form.afterAction(this, false);
3771 this.form.clearInvalid();
3772 this.form.setValues(result.data);
3773 this.form.afterAction(this, true);
3776 handleResponse : function(response){
3777 if(this.form.reader){
3778 var rs = this.form.reader.read(response);
3779 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3781 success : rs.success,
3785 return Roo.decode(response.responseText);
3789 Roo.form.Action.ACTION_TYPES = {
3790 'load' : Roo.form.Action.Load,
3791 'submit' : Roo.form.Action.Submit
3800 * @class Roo.bootstrap.Form
3801 * @extends Roo.bootstrap.Component
3802 * Bootstrap Form class
3803 * @cfg {String} method GET | POST (default POST)
3804 * @cfg {String} labelAlign top | left (default top)
3805 * @cfg {String} align left | right - for navbars
3810 * @param {Object} config The config object
3814 Roo.bootstrap.Form = function(config){
3815 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3818 * @event clientvalidation
3819 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3820 * @param {Form} this
3821 * @param {Boolean} valid true if the form has passed client-side validation
3823 clientvalidation: true,
3825 * @event beforeaction
3826 * Fires before any action is performed. Return false to cancel the action.
3827 * @param {Form} this
3828 * @param {Action} action The action to be performed
3832 * @event actionfailed
3833 * Fires when an action fails.
3834 * @param {Form} this
3835 * @param {Action} action The action that failed
3837 actionfailed : true,
3839 * @event actioncomplete
3840 * Fires when an action is completed.
3841 * @param {Form} this
3842 * @param {Action} action The action that completed
3844 actioncomplete : true
3849 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3852 * @cfg {String} method
3853 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3858 * The URL to use for form actions if one isn't supplied in the action options.
3861 * @cfg {Boolean} fileUpload
3862 * Set to true if this form is a file upload.
3866 * @cfg {Object} baseParams
3867 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3871 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3875 * @cfg {Sting} align (left|right) for navbar forms
3880 activeAction : null,
3883 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3884 * element by passing it or its id or mask the form itself by passing in true.
3887 waitMsgTarget : false,
3892 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3893 * element by passing it or its id or mask the form itself by passing in true.
3897 getAutoCreate : function(){
3901 method : this.method || 'POST',
3902 id : this.id || Roo.id(),
3905 if (this.parent().xtype.match(/^Nav/)) {
3906 cfg.cls = 'navbar-form navbar-' + this.align;
3910 if (this.labelAlign == 'left' ) {
3911 cfg.cls += ' form-horizontal';
3917 initEvents : function()
3919 this.el.on('submit', this.onSubmit, this);
3924 onSubmit : function(e){
3929 * Returns true if client-side validation on the form is successful.
3932 isValid : function(){
3933 var items = this.getItems();
3935 items.each(function(f){
3944 * Returns true if any fields in this form have changed since their original load.
3947 isDirty : function(){
3949 var items = this.getItems();
3950 items.each(function(f){
3960 * Performs a predefined action (submit or load) or custom actions you define on this form.
3961 * @param {String} actionName The name of the action type
3962 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3963 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3964 * accept other config options):
3966 Property Type Description
3967 ---------------- --------------- ----------------------------------------------------------------------------------
3968 url String The url for the action (defaults to the form's url)
3969 method String The form method to use (defaults to the form's method, or POST if not defined)
3970 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3971 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3972 validate the form on the client (defaults to false)
3974 * @return {BasicForm} this
3976 doAction : function(action, options){
3977 if(typeof action == 'string'){
3978 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3980 if(this.fireEvent('beforeaction', this, action) !== false){
3981 this.beforeAction(action);
3982 action.run.defer(100, action);
3988 beforeAction : function(action){
3989 var o = action.options;
3991 // not really supported yet.. ??
3993 //if(this.waitMsgTarget === true){
3994 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3995 //}else if(this.waitMsgTarget){
3996 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3997 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3999 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4005 afterAction : function(action, success){
4006 this.activeAction = null;
4007 var o = action.options;
4009 //if(this.waitMsgTarget === true){
4011 //}else if(this.waitMsgTarget){
4012 // this.waitMsgTarget.unmask();
4014 // Roo.MessageBox.updateProgress(1);
4015 // Roo.MessageBox.hide();
4022 Roo.callback(o.success, o.scope, [this, action]);
4023 this.fireEvent('actioncomplete', this, action);
4027 // failure condition..
4028 // we have a scenario where updates need confirming.
4029 // eg. if a locking scenario exists..
4030 // we look for { errors : { needs_confirm : true }} in the response.
4032 (typeof(action.result) != 'undefined') &&
4033 (typeof(action.result.errors) != 'undefined') &&
4034 (typeof(action.result.errors.needs_confirm) != 'undefined')
4037 Roo.log("not supported yet");
4040 Roo.MessageBox.confirm(
4041 "Change requires confirmation",
4042 action.result.errorMsg,
4047 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4057 Roo.callback(o.failure, o.scope, [this, action]);
4058 // show an error message if no failed handler is set..
4059 if (!this.hasListener('actionfailed')) {
4060 Roo.log("need to add dialog support");
4062 Roo.MessageBox.alert("Error",
4063 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4064 action.result.errorMsg :
4065 "Saving Failed, please check your entries or try again"
4070 this.fireEvent('actionfailed', this, action);
4075 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4076 * @param {String} id The value to search for
4079 findField : function(id){
4080 var items = this.getItems();
4081 var field = items.get(id);
4083 items.each(function(f){
4084 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4091 return field || null;
4094 * Mark fields in this form invalid in bulk.
4095 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4096 * @return {BasicForm} this
4098 markInvalid : function(errors){
4099 if(errors instanceof Array){
4100 for(var i = 0, len = errors.length; i < len; i++){
4101 var fieldError = errors[i];
4102 var f = this.findField(fieldError.id);
4104 f.markInvalid(fieldError.msg);
4110 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4111 field.markInvalid(errors[id]);
4115 //Roo.each(this.childForms || [], function (f) {
4116 // f.markInvalid(errors);
4123 * Set values for fields in this form in bulk.
4124 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4125 * @return {BasicForm} this
4127 setValues : function(values){
4128 if(values instanceof Array){ // array of objects
4129 for(var i = 0, len = values.length; i < len; i++){
4131 var f = this.findField(v.id);
4133 f.setValue(v.value);
4134 if(this.trackResetOnLoad){
4135 f.originalValue = f.getValue();
4139 }else{ // object hash
4142 if(typeof values[id] != 'function' && (field = this.findField(id))){
4144 if (field.setFromData &&
4146 field.displayField &&
4147 // combos' with local stores can
4148 // be queried via setValue()
4149 // to set their value..
4150 (field.store && !field.store.isLocal)
4154 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4155 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4156 field.setFromData(sd);
4159 field.setValue(values[id]);
4163 if(this.trackResetOnLoad){
4164 field.originalValue = field.getValue();
4170 //Roo.each(this.childForms || [], function (f) {
4171 // f.setValues(values);
4178 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4179 * they are returned as an array.
4180 * @param {Boolean} asString
4183 getValues : function(asString){
4184 //if (this.childForms) {
4185 // copy values from the child forms
4186 // Roo.each(this.childForms, function (f) {
4187 // this.setValues(f.getValues());
4193 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4194 if(asString === true){
4197 return Roo.urlDecode(fs);
4201 * Returns the fields in this form as an object with key/value pairs.
4202 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4205 getFieldValues : function(with_hidden)
4207 var items = this.getItems();
4209 items.each(function(f){
4213 var v = f.getValue();
4214 if (f.inputType =='radio') {
4215 if (typeof(ret[f.getName()]) == 'undefined') {
4216 ret[f.getName()] = ''; // empty..
4219 if (!f.el.dom.checked) {
4227 // not sure if this supported any more..
4228 if ((typeof(v) == 'object') && f.getRawValue) {
4229 v = f.getRawValue() ; // dates..
4231 // combo boxes where name != hiddenName...
4232 if (f.name != f.getName()) {
4233 ret[f.name] = f.getRawValue();
4235 ret[f.getName()] = v;
4242 * Clears all invalid messages in this form.
4243 * @return {BasicForm} this
4245 clearInvalid : function(){
4246 var items = this.getItems();
4248 items.each(function(f){
4259 * @return {BasicForm} this
4262 var items = this.getItems();
4263 items.each(function(f){
4267 Roo.each(this.childForms || [], function (f) {
4274 getItems : function()
4276 var r=new Roo.util.MixedCollection(false, function(o){
4277 return o.id || (o.id = Roo.id());
4279 var iter = function(el) {
4286 Roo.each(el.items,function(e) {
4305 * Ext JS Library 1.1.1
4306 * Copyright(c) 2006-2007, Ext JS, LLC.
4308 * Originally Released Under LGPL - original licence link has changed is not relivant.
4311 * <script type="text/javascript">
4314 * @class Roo.form.VTypes
4315 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4318 Roo.form.VTypes = function(){
4319 // closure these in so they are only created once.
4320 var alpha = /^[a-zA-Z_]+$/;
4321 var alphanum = /^[a-zA-Z0-9_]+$/;
4322 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4323 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4325 // All these messages and functions are configurable
4328 * The function used to validate email addresses
4329 * @param {String} value The email address
4331 'email' : function(v){
4332 return email.test(v);
4335 * The error text to display when the email validation function returns false
4338 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4340 * The keystroke filter mask to be applied on email input
4343 'emailMask' : /[a-z0-9_\.\-@]/i,
4346 * The function used to validate URLs
4347 * @param {String} value The URL
4349 'url' : function(v){
4353 * The error text to display when the url validation function returns false
4356 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4359 * The function used to validate alpha values
4360 * @param {String} value The value
4362 'alpha' : function(v){
4363 return alpha.test(v);
4366 * The error text to display when the alpha validation function returns false
4369 'alphaText' : 'This field should only contain letters and _',
4371 * The keystroke filter mask to be applied on alpha input
4374 'alphaMask' : /[a-z_]/i,
4377 * The function used to validate alphanumeric values
4378 * @param {String} value The value
4380 'alphanum' : function(v){
4381 return alphanum.test(v);
4384 * The error text to display when the alphanumeric validation function returns false
4387 'alphanumText' : 'This field should only contain letters, numbers and _',
4389 * The keystroke filter mask to be applied on alphanumeric input
4392 'alphanumMask' : /[a-z0-9_]/i
4402 * @class Roo.bootstrap.Input
4403 * @extends Roo.bootstrap.Component
4404 * Bootstrap Input class
4405 * @cfg {Boolean} disabled is it disabled
4406 * @cfg {String} fieldLabel - the label associated
4407 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4408 * @cfg {String} name name of the input
4409 * @cfg {string} fieldLabel - the label associated
4410 * @cfg {string} inputType - input / file submit ...
4411 * @cfg {string} placeholder - placeholder to put in text.
4412 * @cfg {string} before - input group add on before
4413 * @cfg {string} after - input group add on after
4414 * @cfg {string} size - (lg|sm) or leave empty..
4415 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4416 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4417 * @cfg {Number} md colspan out of 12 for computer-sized screens
4418 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4419 * @cfg {string} value default value of the input
4420 * @cfg {Number} labelWidth set the width of label (0-12)
4421 * @cfg {String} labelAlign (top|left)
4422 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4426 * Create a new Input
4427 * @param {Object} config The config object
4430 Roo.bootstrap.Input = function(config){
4431 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4436 * Fires when this field receives input focus.
4437 * @param {Roo.form.Field} this
4442 * Fires when this field loses input focus.
4443 * @param {Roo.form.Field} this
4448 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4449 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4450 * @param {Roo.form.Field} this
4451 * @param {Roo.EventObject} e The event object
4456 * Fires just before the field blurs if the field value has changed.
4457 * @param {Roo.form.Field} this
4458 * @param {Mixed} newValue The new value
4459 * @param {Mixed} oldValue The original value
4464 * Fires after the field has been marked as invalid.
4465 * @param {Roo.form.Field} this
4466 * @param {String} msg The validation message
4471 * Fires after the field has been validated with no errors.
4472 * @param {Roo.form.Field} this
4477 * Fires after the key up
4478 * @param {Roo.form.Field} this
4479 * @param {Roo.EventObject} e The event Object
4485 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4487 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4488 automatic validation (defaults to "keyup").
4490 validationEvent : "keyup",
4492 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4494 validateOnBlur : true,
4496 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4498 validationDelay : 250,
4500 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4502 focusClass : "x-form-focus", // not needed???
4506 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4508 invalidClass : "has-error",
4511 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4513 selectOnFocus : false,
4516 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4520 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4525 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4527 disableKeyFilter : false,
4530 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4534 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4538 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4540 blankText : "This field is required",
4543 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4547 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4549 maxLength : Number.MAX_VALUE,
4551 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4553 minLengthText : "The minimum length for this field is {0}",
4555 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4557 maxLengthText : "The maximum length for this field is {0}",
4561 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4562 * If available, this function will be called only after the basic validators all return true, and will be passed the
4563 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4567 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4568 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4569 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4573 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4596 parentLabelAlign : function()
4599 while (parent.parent()) {
4600 parent = parent.parent();
4601 if (typeof(parent.labelAlign) !='undefined') {
4602 return parent.labelAlign;
4609 getAutoCreate : function(){
4611 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4617 if(this.inputType != 'hidden'){
4618 cfg.cls = 'form-group' //input-group
4624 type : this.inputType,
4626 cls : 'form-control',
4627 placeholder : this.placeholder || ''
4631 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4632 input.maxLength = this.maxLength;
4635 if (this.disabled) {
4636 input.disabled=true;
4639 if (this.readOnly) {
4640 input.readonly=true;
4644 input.name = this.name;
4647 input.cls += ' input-' + this.size;
4650 ['xs','sm','md','lg'].map(function(size){
4651 if (settings[size]) {
4652 cfg.cls += ' col-' + size + '-' + settings[size];
4656 var inputblock = input;
4658 if (this.before || this.after) {
4661 cls : 'input-group',
4665 inputblock.cn.push({
4667 cls : 'input-group-addon',
4671 inputblock.cn.push(input);
4673 inputblock.cn.push({
4675 cls : 'input-group-addon',
4682 if (align ==='left' && this.fieldLabel.length) {
4683 Roo.log("left and has label");
4689 cls : 'control-label col-sm-' + this.labelWidth,
4690 html : this.fieldLabel
4694 cls : "col-sm-" + (12 - this.labelWidth),
4701 } else if ( this.fieldLabel.length) {
4707 //cls : 'input-group-addon',
4708 html : this.fieldLabel
4718 Roo.log(" no label && no align");
4727 Roo.log('input-parentType: ' + this.parentType);
4729 if (this.parentType === 'Navbar' && this.parent().bar) {
4730 cfg.cls += ' navbar-form';
4738 * return the real input element.
4740 inputEl: function ()
4742 return this.el.select('input.form-control',true).first();
4744 setDisabled : function(v)
4746 var i = this.inputEl().dom;
4748 i.removeAttribute('disabled');
4752 i.setAttribute('disabled','true');
4754 initEvents : function()
4757 this.inputEl().on("keydown" , this.fireKey, this);
4758 this.inputEl().on("focus", this.onFocus, this);
4759 this.inputEl().on("blur", this.onBlur, this);
4761 this.inputEl().relayEvent('keyup', this);
4763 // reference to original value for reset
4764 this.originalValue = this.getValue();
4765 //Roo.form.TextField.superclass.initEvents.call(this);
4766 if(this.validationEvent == 'keyup'){
4767 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4768 this.inputEl().on('keyup', this.filterValidation, this);
4770 else if(this.validationEvent !== false){
4771 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4774 if(this.selectOnFocus){
4775 this.on("focus", this.preFocus, this);
4778 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4779 this.inputEl().on("keypress", this.filterKeys, this);
4782 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4783 this.el.on("click", this.autoSize, this);
4786 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4787 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4791 filterValidation : function(e){
4792 if(!e.isNavKeyPress()){
4793 this.validationTask.delay(this.validationDelay);
4797 * Validates the field value
4798 * @return {Boolean} True if the value is valid, else false
4800 validate : function(){
4801 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4802 if(this.disabled || this.validateValue(this.getRawValue())){
4803 this.clearInvalid();
4811 * Validates a value according to the field's validation rules and marks the field as invalid
4812 * if the validation fails
4813 * @param {Mixed} value The value to validate
4814 * @return {Boolean} True if the value is valid, else false
4816 validateValue : function(value){
4817 if(value.length < 1) { // if it's blank
4818 if(this.allowBlank){
4819 this.clearInvalid();
4822 this.markInvalid(this.blankText);
4826 if(value.length < this.minLength){
4827 this.markInvalid(String.format(this.minLengthText, this.minLength));
4830 if(value.length > this.maxLength){
4831 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4835 var vt = Roo.form.VTypes;
4836 if(!vt[this.vtype](value, this)){
4837 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4841 if(typeof this.validator == "function"){
4842 var msg = this.validator(value);
4844 this.markInvalid(msg);
4848 if(this.regex && !this.regex.test(value)){
4849 this.markInvalid(this.regexText);
4858 fireKey : function(e){
4859 //Roo.log('field ' + e.getKey());
4860 if(e.isNavKeyPress()){
4861 this.fireEvent("specialkey", this, e);
4864 focus : function (selectText){
4866 this.inputEl().focus();
4867 if(selectText === true){
4868 this.inputEl().dom.select();
4874 onFocus : function(){
4875 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4876 // this.el.addClass(this.focusClass);
4879 this.hasFocus = true;
4880 this.startValue = this.getValue();
4881 this.fireEvent("focus", this);
4885 beforeBlur : Roo.emptyFn,
4889 onBlur : function(){
4891 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4892 //this.el.removeClass(this.focusClass);
4894 this.hasFocus = false;
4895 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4898 var v = this.getValue();
4899 if(String(v) !== String(this.startValue)){
4900 this.fireEvent('change', this, v, this.startValue);
4902 this.fireEvent("blur", this);
4906 * Resets the current field value to the originally loaded value and clears any validation messages
4909 this.setValue(this.originalValue);
4910 this.clearInvalid();
4913 * Returns the name of the field
4914 * @return {Mixed} name The name field
4916 getName: function(){
4920 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4921 * @return {Mixed} value The field value
4923 getValue : function(){
4924 return this.inputEl().getValue();
4927 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4928 * @return {Mixed} value The field value
4930 getRawValue : function(){
4931 var v = this.inputEl().getValue();
4937 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4938 * @param {Mixed} value The value to set
4940 setRawValue : function(v){
4941 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4944 selectText : function(start, end){
4945 var v = this.getRawValue();
4947 start = start === undefined ? 0 : start;
4948 end = end === undefined ? v.length : end;
4949 var d = this.inputEl().dom;
4950 if(d.setSelectionRange){
4951 d.setSelectionRange(start, end);
4952 }else if(d.createTextRange){
4953 var range = d.createTextRange();
4954 range.moveStart("character", start);
4955 range.moveEnd("character", v.length-end);
4962 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4963 * @param {Mixed} value The value to set
4965 setValue : function(v){
4968 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4974 processValue : function(value){
4975 if(this.stripCharsRe){
4976 var newValue = value.replace(this.stripCharsRe, '');
4977 if(newValue !== value){
4978 this.setRawValue(newValue);
4985 preFocus : function(){
4987 if(this.selectOnFocus){
4988 this.inputEl().dom.select();
4991 filterKeys : function(e){
4993 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4996 var c = e.getCharCode(), cc = String.fromCharCode(c);
4997 if(Roo.isIE && (e.isSpecialKey() || !cc)){
5000 if(!this.maskRe.test(cc)){
5005 * Clear any invalid styles/messages for this field
5007 clearInvalid : function(){
5009 if(!this.el || this.preventMark){ // not rendered
5012 this.el.removeClass(this.invalidClass);
5014 switch(this.msgTarget){
5016 this.el.dom.qtip = '';
5019 this.el.dom.title = '';
5023 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5028 this.errorIcon.dom.qtip = '';
5029 this.errorIcon.hide();
5030 this.un('resize', this.alignErrorIcon, this);
5034 var t = Roo.getDom(this.msgTarget);
5036 t.style.display = 'none';
5040 this.fireEvent('valid', this);
5043 * Mark this field as invalid
5044 * @param {String} msg The validation message
5046 markInvalid : function(msg){
5047 if(!this.el || this.preventMark){ // not rendered
5050 this.el.addClass(this.invalidClass);
5052 msg = msg || this.invalidText;
5053 switch(this.msgTarget){
5055 this.el.dom.qtip = msg;
5056 this.el.dom.qclass = 'x-form-invalid-tip';
5057 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5058 Roo.QuickTips.enable();
5062 this.el.dom.title = msg;
5066 var elp = this.el.findParent('.x-form-element', 5, true);
5067 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5068 this.errorEl.setWidth(elp.getWidth(true)-20);
5070 this.errorEl.update(msg);
5071 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5074 if(!this.errorIcon){
5075 var elp = this.el.findParent('.x-form-element', 5, true);
5076 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5078 this.alignErrorIcon();
5079 this.errorIcon.dom.qtip = msg;
5080 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5081 this.errorIcon.show();
5082 this.on('resize', this.alignErrorIcon, this);
5085 var t = Roo.getDom(this.msgTarget);
5087 t.style.display = this.msgDisplay;
5091 this.fireEvent('invalid', this, msg);
5094 SafariOnKeyDown : function(event)
5096 // this is a workaround for a password hang bug on chrome/ webkit.
5098 var isSelectAll = false;
5100 if(this.inputEl().dom.selectionEnd > 0){
5101 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5103 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5104 event.preventDefault();
5109 if(isSelectAll){ // backspace and delete key
5111 event.preventDefault();
5112 // this is very hacky as keydown always get's upper case.
5114 var cc = String.fromCharCode(event.getCharCode());
5115 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5119 adjustWidth : function(tag, w){
5120 tag = tag.toLowerCase();
5121 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5122 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5126 if(tag == 'textarea'){
5129 }else if(Roo.isOpera){
5133 if(tag == 'textarea'){
5152 * @class Roo.bootstrap.TextArea
5153 * @extends Roo.bootstrap.Input
5154 * Bootstrap TextArea class
5155 * @cfg {Number} cols Specifies the visible width of a text area
5156 * @cfg {Number} rows Specifies the visible number of lines in a text area
5157 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5158 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5159 * @cfg {string} html text
5162 * Create a new TextArea
5163 * @param {Object} config The config object
5166 Roo.bootstrap.TextArea = function(config){
5167 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5171 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5181 getAutoCreate : function(){
5183 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5194 value : this.value || '',
5195 html: this.html || '',
5196 cls : 'form-control',
5197 placeholder : this.placeholder || ''
5201 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5202 input.maxLength = this.maxLength;
5206 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5210 input.cols = this.cols;
5213 if (this.readOnly) {
5214 input.readonly = true;
5218 input.name = this.name;
5222 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5226 ['xs','sm','md','lg'].map(function(size){
5227 if (settings[size]) {
5228 cfg.cls += ' col-' + size + '-' + settings[size];
5232 var inputblock = input;
5234 if (this.before || this.after) {
5237 cls : 'input-group',
5241 inputblock.cn.push({
5243 cls : 'input-group-addon',
5247 inputblock.cn.push(input);
5249 inputblock.cn.push({
5251 cls : 'input-group-addon',
5258 if (align ==='left' && this.fieldLabel.length) {
5259 Roo.log("left and has label");
5265 cls : 'control-label col-sm-' + this.labelWidth,
5266 html : this.fieldLabel
5270 cls : "col-sm-" + (12 - this.labelWidth),
5277 } else if ( this.fieldLabel.length) {
5283 //cls : 'input-group-addon',
5284 html : this.fieldLabel
5294 Roo.log(" no label && no align");
5304 if (this.disabled) {
5305 input.disabled=true;
5312 * return the real textarea element.
5314 inputEl: function ()
5316 return this.el.select('textarea.form-control',true).first();
5324 * trigger field - base class for combo..
5329 * @class Roo.bootstrap.TriggerField
5330 * @extends Roo.bootstrap.Input
5331 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5332 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5333 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5334 * for which you can provide a custom implementation. For example:
5336 var trigger = new Roo.bootstrap.TriggerField();
5337 trigger.onTriggerClick = myTriggerFn;
5338 trigger.applyTo('my-field');
5341 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5342 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5343 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5344 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5346 * Create a new TriggerField.
5347 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5348 * to the base TextField)
5350 Roo.bootstrap.TriggerField = function(config){
5351 this.mimicing = false;
5352 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5355 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5357 * @cfg {String} triggerClass A CSS class to apply to the trigger
5360 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5364 /** @cfg {Boolean} grow @hide */
5365 /** @cfg {Number} growMin @hide */
5366 /** @cfg {Number} growMax @hide */
5372 autoSize: Roo.emptyFn,
5379 actionMode : 'wrap',
5383 getAutoCreate : function(){
5385 var parent = this.parent();
5387 var align = this.parentLabelAlign();
5392 cls: 'form-group' //input-group
5399 type : this.inputType,
5400 cls : 'form-control',
5401 autocomplete: 'off',
5402 placeholder : this.placeholder || ''
5406 input.name = this.name;
5409 input.cls += ' input-' + this.size;
5412 if (this.disabled) {
5413 input.disabled=true;
5416 var inputblock = input;
5418 if (this.before || this.after) {
5421 cls : 'input-group',
5425 inputblock.cn.push({
5427 cls : 'input-group-addon',
5431 inputblock.cn.push(input);
5433 inputblock.cn.push({
5435 cls : 'input-group-addon',
5448 cls: 'form-hidden-field'
5456 Roo.log('multiple');
5464 cls: 'form-hidden-field'
5468 cls: 'select2-choices',
5472 cls: 'select2-search-field',
5485 cls: 'select2-container input-group',
5490 cls: 'typeahead typeahead-long dropdown-menu',
5491 style: 'display:none'
5499 cls : 'input-group-addon btn dropdown-toggle',
5507 cls: 'combobox-clear',
5521 combobox.cls += ' select2-container-multi';
5524 if (align ==='left' && this.fieldLabel.length) {
5526 Roo.log("left and has label");
5532 cls : 'control-label col-sm-' + this.labelWidth,
5533 html : this.fieldLabel
5537 cls : "col-sm-" + (12 - this.labelWidth),
5544 } else if ( this.fieldLabel.length) {
5550 //cls : 'input-group-addon',
5551 html : this.fieldLabel
5561 Roo.log(" no label && no align");
5568 ['xs','sm','md','lg'].map(function(size){
5569 if (settings[size]) {
5570 cfg.cls += ' col-' + size + '-' + settings[size];
5581 onResize : function(w, h){
5582 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5583 // if(typeof w == 'number'){
5584 // var x = w - this.trigger.getWidth();
5585 // this.inputEl().setWidth(this.adjustWidth('input', x));
5586 // this.trigger.setStyle('left', x+'px');
5591 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5594 getResizeEl : function(){
5595 return this.inputEl();
5599 getPositionEl : function(){
5600 return this.inputEl();
5604 alignErrorIcon : function(){
5605 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5609 initEvents : function(){
5611 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5612 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5614 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5615 if(this.hideTrigger){
5616 this.trigger.setDisplayed(false);
5618 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5622 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5625 //this.trigger.addClassOnOver('x-form-trigger-over');
5626 //this.trigger.addClassOnClick('x-form-trigger-click');
5629 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5634 initTrigger : function(){
5639 onDestroy : function(){
5641 this.trigger.removeAllListeners();
5642 // this.trigger.remove();
5645 // this.wrap.remove();
5647 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5651 onFocus : function(){
5652 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5655 this.wrap.addClass('x-trigger-wrap-focus');
5656 this.mimicing = true;
5657 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5658 if(this.monitorTab){
5659 this.el.on("keydown", this.checkTab, this);
5666 checkTab : function(e){
5667 if(e.getKey() == e.TAB){
5673 onBlur : function(){
5678 mimicBlur : function(e, t){
5680 if(!this.wrap.contains(t) && this.validateBlur()){
5687 triggerBlur : function(){
5688 this.mimicing = false;
5689 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5690 if(this.monitorTab){
5691 this.el.un("keydown", this.checkTab, this);
5693 //this.wrap.removeClass('x-trigger-wrap-focus');
5694 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5698 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5699 validateBlur : function(e, t){
5704 onDisable : function(){
5705 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5707 // this.wrap.addClass('x-item-disabled');
5712 onEnable : function(){
5713 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5715 // this.el.removeClass('x-item-disabled');
5720 onShow : function(){
5721 var ae = this.getActionEl();
5724 ae.dom.style.display = '';
5725 ae.dom.style.visibility = 'visible';
5731 onHide : function(){
5732 var ae = this.getActionEl();
5733 ae.dom.style.display = 'none';
5737 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5738 * by an implementing function.
5740 * @param {EventObject} e
5742 onTriggerClick : Roo.emptyFn
5746 * Ext JS Library 1.1.1
5747 * Copyright(c) 2006-2007, Ext JS, LLC.
5749 * Originally Released Under LGPL - original licence link has changed is not relivant.
5752 * <script type="text/javascript">
5757 * @class Roo.data.SortTypes
5759 * Defines the default sorting (casting?) comparison functions used when sorting data.
5761 Roo.data.SortTypes = {
5763 * Default sort that does nothing
5764 * @param {Mixed} s The value being converted
5765 * @return {Mixed} The comparison value
5772 * The regular expression used to strip tags
5776 stripTagsRE : /<\/?[^>]+>/gi,
5779 * Strips all HTML tags to sort on text only
5780 * @param {Mixed} s The value being converted
5781 * @return {String} The comparison value
5783 asText : function(s){
5784 return String(s).replace(this.stripTagsRE, "");
5788 * Strips all HTML tags to sort on text only - Case insensitive
5789 * @param {Mixed} s The value being converted
5790 * @return {String} The comparison value
5792 asUCText : function(s){
5793 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5797 * Case insensitive string
5798 * @param {Mixed} s The value being converted
5799 * @return {String} The comparison value
5801 asUCString : function(s) {
5802 return String(s).toUpperCase();
5807 * @param {Mixed} s The value being converted
5808 * @return {Number} The comparison value
5810 asDate : function(s) {
5814 if(s instanceof Date){
5817 return Date.parse(String(s));
5822 * @param {Mixed} s The value being converted
5823 * @return {Float} The comparison value
5825 asFloat : function(s) {
5826 var val = parseFloat(String(s).replace(/,/g, ""));
5827 if(isNaN(val)) val = 0;
5833 * @param {Mixed} s The value being converted
5834 * @return {Number} The comparison value
5836 asInt : function(s) {
5837 var val = parseInt(String(s).replace(/,/g, ""));
5838 if(isNaN(val)) val = 0;
5843 * Ext JS Library 1.1.1
5844 * Copyright(c) 2006-2007, Ext JS, LLC.
5846 * Originally Released Under LGPL - original licence link has changed is not relivant.
5849 * <script type="text/javascript">
5853 * @class Roo.data.Record
5854 * Instances of this class encapsulate both record <em>definition</em> information, and record
5855 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5856 * to access Records cached in an {@link Roo.data.Store} object.<br>
5858 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5859 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5862 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5864 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5865 * {@link #create}. The parameters are the same.
5866 * @param {Array} data An associative Array of data values keyed by the field name.
5867 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5868 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5869 * not specified an integer id is generated.
5871 Roo.data.Record = function(data, id){
5872 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5877 * Generate a constructor for a specific record layout.
5878 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5879 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5880 * Each field definition object may contain the following properties: <ul>
5881 * <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,
5882 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5883 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5884 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5885 * is being used, then this is a string containing the javascript expression to reference the data relative to
5886 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5887 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5888 * this may be omitted.</p></li>
5889 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5890 * <ul><li>auto (Default, implies no conversion)</li>
5895 * <li>date</li></ul></p></li>
5896 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5897 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5898 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5899 * by the Reader into an object that will be stored in the Record. It is passed the
5900 * following parameters:<ul>
5901 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5903 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5905 * <br>usage:<br><pre><code>
5906 var TopicRecord = Roo.data.Record.create(
5907 {name: 'title', mapping: 'topic_title'},
5908 {name: 'author', mapping: 'username'},
5909 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5910 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5911 {name: 'lastPoster', mapping: 'user2'},
5912 {name: 'excerpt', mapping: 'post_text'}
5915 var myNewRecord = new TopicRecord({
5916 title: 'Do my job please',
5919 lastPost: new Date(),
5920 lastPoster: 'Animal',
5921 excerpt: 'No way dude!'
5923 myStore.add(myNewRecord);
5928 Roo.data.Record.create = function(o){
5930 f.superclass.constructor.apply(this, arguments);
5932 Roo.extend(f, Roo.data.Record);
5933 var p = f.prototype;
5934 p.fields = new Roo.util.MixedCollection(false, function(field){
5937 for(var i = 0, len = o.length; i < len; i++){
5938 p.fields.add(new Roo.data.Field(o[i]));
5940 f.getField = function(name){
5941 return p.fields.get(name);
5946 Roo.data.Record.AUTO_ID = 1000;
5947 Roo.data.Record.EDIT = 'edit';
5948 Roo.data.Record.REJECT = 'reject';
5949 Roo.data.Record.COMMIT = 'commit';
5951 Roo.data.Record.prototype = {
5953 * Readonly flag - true if this record has been modified.
5962 join : function(store){
5967 * Set the named field to the specified value.
5968 * @param {String} name The name of the field to set.
5969 * @param {Object} value The value to set the field to.
5971 set : function(name, value){
5972 if(this.data[name] == value){
5979 if(typeof this.modified[name] == 'undefined'){
5980 this.modified[name] = this.data[name];
5982 this.data[name] = value;
5983 if(!this.editing && this.store){
5984 this.store.afterEdit(this);
5989 * Get the value of the named field.
5990 * @param {String} name The name of the field to get the value of.
5991 * @return {Object} The value of the field.
5993 get : function(name){
5994 return this.data[name];
5998 beginEdit : function(){
5999 this.editing = true;
6004 cancelEdit : function(){
6005 this.editing = false;
6006 delete this.modified;
6010 endEdit : function(){
6011 this.editing = false;
6012 if(this.dirty && this.store){
6013 this.store.afterEdit(this);
6018 * Usually called by the {@link Roo.data.Store} which owns the Record.
6019 * Rejects all changes made to the Record since either creation, or the last commit operation.
6020 * Modified fields are reverted to their original values.
6022 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6023 * of reject operations.
6025 reject : function(){
6026 var m = this.modified;
6028 if(typeof m[n] != "function"){
6029 this.data[n] = m[n];
6033 delete this.modified;
6034 this.editing = false;
6036 this.store.afterReject(this);
6041 * Usually called by the {@link Roo.data.Store} which owns the Record.
6042 * Commits all changes made to the Record since either creation, or the last commit operation.
6044 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6045 * of commit operations.
6047 commit : function(){
6049 delete this.modified;
6050 this.editing = false;
6052 this.store.afterCommit(this);
6057 hasError : function(){
6058 return this.error != null;
6062 clearError : function(){
6067 * Creates a copy of this record.
6068 * @param {String} id (optional) A new record id if you don't want to use this record's id
6071 copy : function(newId) {
6072 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6076 * Ext JS Library 1.1.1
6077 * Copyright(c) 2006-2007, Ext JS, LLC.
6079 * Originally Released Under LGPL - original licence link has changed is not relivant.
6082 * <script type="text/javascript">
6088 * @class Roo.data.Store
6089 * @extends Roo.util.Observable
6090 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6091 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6093 * 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
6094 * has no knowledge of the format of the data returned by the Proxy.<br>
6096 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6097 * instances from the data object. These records are cached and made available through accessor functions.
6099 * Creates a new Store.
6100 * @param {Object} config A config object containing the objects needed for the Store to access data,
6101 * and read the data into Records.
6103 Roo.data.Store = function(config){
6104 this.data = new Roo.util.MixedCollection(false);
6105 this.data.getKey = function(o){
6108 this.baseParams = {};
6115 "multisort" : "_multisort"
6118 if(config && config.data){
6119 this.inlineData = config.data;
6123 Roo.apply(this, config);
6125 if(this.reader){ // reader passed
6126 this.reader = Roo.factory(this.reader, Roo.data);
6127 this.reader.xmodule = this.xmodule || false;
6128 if(!this.recordType){
6129 this.recordType = this.reader.recordType;
6131 if(this.reader.onMetaChange){
6132 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6136 if(this.recordType){
6137 this.fields = this.recordType.prototype.fields;
6143 * @event datachanged
6144 * Fires when the data cache has changed, and a widget which is using this Store
6145 * as a Record cache should refresh its view.
6146 * @param {Store} this
6151 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6152 * @param {Store} this
6153 * @param {Object} meta The JSON metadata
6158 * Fires when Records have been added to the Store
6159 * @param {Store} this
6160 * @param {Roo.data.Record[]} records The array of Records added
6161 * @param {Number} index The index at which the record(s) were added
6166 * Fires when a Record has been removed from the Store
6167 * @param {Store} this
6168 * @param {Roo.data.Record} record The Record that was removed
6169 * @param {Number} index The index at which the record was removed
6174 * Fires when a Record has been updated
6175 * @param {Store} this
6176 * @param {Roo.data.Record} record The Record that was updated
6177 * @param {String} operation The update operation being performed. Value may be one of:
6179 Roo.data.Record.EDIT
6180 Roo.data.Record.REJECT
6181 Roo.data.Record.COMMIT
6187 * Fires when the data cache has been cleared.
6188 * @param {Store} this
6193 * Fires before a request is made for a new data object. If the beforeload handler returns false
6194 * the load action will be canceled.
6195 * @param {Store} this
6196 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6200 * @event beforeloadadd
6201 * Fires after a new set of Records has been loaded.
6202 * @param {Store} this
6203 * @param {Roo.data.Record[]} records The Records that were loaded
6204 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6206 beforeloadadd : true,
6209 * Fires after a new set of Records has been loaded, before they are added to the store.
6210 * @param {Store} this
6211 * @param {Roo.data.Record[]} records The Records that were loaded
6212 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6213 * @params {Object} return from reader
6217 * @event loadexception
6218 * Fires if an exception occurs in the Proxy during loading.
6219 * Called with the signature of the Proxy's "loadexception" event.
6220 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6223 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6224 * @param {Object} load options
6225 * @param {Object} jsonData from your request (normally this contains the Exception)
6227 loadexception : true
6231 this.proxy = Roo.factory(this.proxy, Roo.data);
6232 this.proxy.xmodule = this.xmodule || false;
6233 this.relayEvents(this.proxy, ["loadexception"]);
6235 this.sortToggle = {};
6236 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6238 Roo.data.Store.superclass.constructor.call(this);
6240 if(this.inlineData){
6241 this.loadData(this.inlineData);
6242 delete this.inlineData;
6246 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6248 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6249 * without a remote query - used by combo/forms at present.
6253 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6256 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6259 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6260 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6263 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6264 * on any HTTP request
6267 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6270 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6274 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6275 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6280 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6281 * loaded or when a record is removed. (defaults to false).
6283 pruneModifiedRecords : false,
6289 * Add Records to the Store and fires the add event.
6290 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6292 add : function(records){
6293 records = [].concat(records);
6294 for(var i = 0, len = records.length; i < len; i++){
6295 records[i].join(this);
6297 var index = this.data.length;
6298 this.data.addAll(records);
6299 this.fireEvent("add", this, records, index);
6303 * Remove a Record from the Store and fires the remove event.
6304 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6306 remove : function(record){
6307 var index = this.data.indexOf(record);
6308 this.data.removeAt(index);
6309 if(this.pruneModifiedRecords){
6310 this.modified.remove(record);
6312 this.fireEvent("remove", this, record, index);
6316 * Remove all Records from the Store and fires the clear event.
6318 removeAll : function(){
6320 if(this.pruneModifiedRecords){
6323 this.fireEvent("clear", this);
6327 * Inserts Records to the Store at the given index and fires the add event.
6328 * @param {Number} index The start index at which to insert the passed Records.
6329 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6331 insert : function(index, records){
6332 records = [].concat(records);
6333 for(var i = 0, len = records.length; i < len; i++){
6334 this.data.insert(index, records[i]);
6335 records[i].join(this);
6337 this.fireEvent("add", this, records, index);
6341 * Get the index within the cache of the passed Record.
6342 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6343 * @return {Number} The index of the passed Record. Returns -1 if not found.
6345 indexOf : function(record){
6346 return this.data.indexOf(record);
6350 * Get the index within the cache of the Record with the passed id.
6351 * @param {String} id The id of the Record to find.
6352 * @return {Number} The index of the Record. Returns -1 if not found.
6354 indexOfId : function(id){
6355 return this.data.indexOfKey(id);
6359 * Get the Record with the specified id.
6360 * @param {String} id The id of the Record to find.
6361 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6363 getById : function(id){
6364 return this.data.key(id);
6368 * Get the Record at the specified index.
6369 * @param {Number} index The index of the Record to find.
6370 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6372 getAt : function(index){
6373 return this.data.itemAt(index);
6377 * Returns a range of Records between specified indices.
6378 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6379 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6380 * @return {Roo.data.Record[]} An array of Records
6382 getRange : function(start, end){
6383 return this.data.getRange(start, end);
6387 storeOptions : function(o){
6388 o = Roo.apply({}, o);
6391 this.lastOptions = o;
6395 * Loads the Record cache from the configured Proxy using the configured Reader.
6397 * If using remote paging, then the first load call must specify the <em>start</em>
6398 * and <em>limit</em> properties in the options.params property to establish the initial
6399 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6401 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6402 * and this call will return before the new data has been loaded. Perform any post-processing
6403 * in a callback function, or in a "load" event handler.</strong>
6405 * @param {Object} options An object containing properties which control loading options:<ul>
6406 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6407 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6408 * passed the following arguments:<ul>
6409 * <li>r : Roo.data.Record[]</li>
6410 * <li>options: Options object from the load call</li>
6411 * <li>success: Boolean success indicator</li></ul></li>
6412 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6413 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6416 load : function(options){
6417 options = options || {};
6418 if(this.fireEvent("beforeload", this, options) !== false){
6419 this.storeOptions(options);
6420 var p = Roo.apply(options.params || {}, this.baseParams);
6421 // if meta was not loaded from remote source.. try requesting it.
6422 if (!this.reader.metaFromRemote) {
6425 if(this.sortInfo && this.remoteSort){
6426 var pn = this.paramNames;
6427 p[pn["sort"]] = this.sortInfo.field;
6428 p[pn["dir"]] = this.sortInfo.direction;
6430 if (this.multiSort) {
6431 var pn = this.paramNames;
6432 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6435 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6440 * Reloads the Record cache from the configured Proxy using the configured Reader and
6441 * the options from the last load operation performed.
6442 * @param {Object} options (optional) An object containing properties which may override the options
6443 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6444 * the most recently used options are reused).
6446 reload : function(options){
6447 this.load(Roo.applyIf(options||{}, this.lastOptions));
6451 // Called as a callback by the Reader during a load operation.
6452 loadRecords : function(o, options, success){
6453 if(!o || success === false){
6454 if(success !== false){
6455 this.fireEvent("load", this, [], options, o);
6457 if(options.callback){
6458 options.callback.call(options.scope || this, [], options, false);
6462 // if data returned failure - throw an exception.
6463 if (o.success === false) {
6464 // show a message if no listener is registered.
6465 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6466 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6468 // loadmask wil be hooked into this..
6469 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6472 var r = o.records, t = o.totalRecords || r.length;
6474 this.fireEvent("beforeloadadd", this, r, options, o);
6476 if(!options || options.add !== true){
6477 if(this.pruneModifiedRecords){
6480 for(var i = 0, len = r.length; i < len; i++){
6484 this.data = this.snapshot;
6485 delete this.snapshot;
6488 this.data.addAll(r);
6489 this.totalLength = t;
6491 this.fireEvent("datachanged", this);
6493 this.totalLength = Math.max(t, this.data.length+r.length);
6496 this.fireEvent("load", this, r, options, o);
6497 if(options.callback){
6498 options.callback.call(options.scope || this, r, options, true);
6504 * Loads data from a passed data block. A Reader which understands the format of the data
6505 * must have been configured in the constructor.
6506 * @param {Object} data The data block from which to read the Records. The format of the data expected
6507 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6508 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6510 loadData : function(o, append){
6511 var r = this.reader.readRecords(o);
6512 this.loadRecords(r, {add: append}, true);
6516 * Gets the number of cached records.
6518 * <em>If using paging, this may not be the total size of the dataset. If the data object
6519 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6520 * the data set size</em>
6522 getCount : function(){
6523 return this.data.length || 0;
6527 * Gets the total number of records in the dataset as returned by the server.
6529 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6530 * the dataset size</em>
6532 getTotalCount : function(){
6533 return this.totalLength || 0;
6537 * Returns the sort state of the Store as an object with two properties:
6539 field {String} The name of the field by which the Records are sorted
6540 direction {String} The sort order, "ASC" or "DESC"
6543 getSortState : function(){
6544 return this.sortInfo;
6548 applySort : function(){
6549 if(this.sortInfo && !this.remoteSort){
6550 var s = this.sortInfo, f = s.field;
6551 var st = this.fields.get(f).sortType;
6552 var fn = function(r1, r2){
6553 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6554 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6556 this.data.sort(s.direction, fn);
6557 if(this.snapshot && this.snapshot != this.data){
6558 this.snapshot.sort(s.direction, fn);
6564 * Sets the default sort column and order to be used by the next load operation.
6565 * @param {String} fieldName The name of the field to sort by.
6566 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6568 setDefaultSort : function(field, dir){
6569 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6574 * If remote sorting is used, the sort is performed on the server, and the cache is
6575 * reloaded. If local sorting is used, the cache is sorted internally.
6576 * @param {String} fieldName The name of the field to sort by.
6577 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6579 sort : function(fieldName, dir){
6580 var f = this.fields.get(fieldName);
6582 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6584 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6585 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6590 this.sortToggle[f.name] = dir;
6591 this.sortInfo = {field: f.name, direction: dir};
6592 if(!this.remoteSort){
6594 this.fireEvent("datachanged", this);
6596 this.load(this.lastOptions);
6601 * Calls the specified function for each of the Records in the cache.
6602 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6603 * Returning <em>false</em> aborts and exits the iteration.
6604 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6606 each : function(fn, scope){
6607 this.data.each(fn, scope);
6611 * Gets all records modified since the last commit. Modified records are persisted across load operations
6612 * (e.g., during paging).
6613 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6615 getModifiedRecords : function(){
6616 return this.modified;
6620 createFilterFn : function(property, value, anyMatch){
6621 if(!value.exec){ // not a regex
6622 value = String(value);
6623 if(value.length == 0){
6626 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6629 return value.test(r.data[property]);
6634 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6635 * @param {String} property A field on your records
6636 * @param {Number} start The record index to start at (defaults to 0)
6637 * @param {Number} end The last record index to include (defaults to length - 1)
6638 * @return {Number} The sum
6640 sum : function(property, start, end){
6641 var rs = this.data.items, v = 0;
6643 end = (end || end === 0) ? end : rs.length-1;
6645 for(var i = start; i <= end; i++){
6646 v += (rs[i].data[property] || 0);
6652 * Filter the records by a specified property.
6653 * @param {String} field A field on your records
6654 * @param {String/RegExp} value Either a string that the field
6655 * should start with or a RegExp to test against the field
6656 * @param {Boolean} anyMatch True to match any part not just the beginning
6658 filter : function(property, value, anyMatch){
6659 var fn = this.createFilterFn(property, value, anyMatch);
6660 return fn ? this.filterBy(fn) : this.clearFilter();
6664 * Filter by a function. The specified function will be called with each
6665 * record in this data source. If the function returns true the record is included,
6666 * otherwise it is filtered.
6667 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6668 * @param {Object} scope (optional) The scope of the function (defaults to this)
6670 filterBy : function(fn, scope){
6671 this.snapshot = this.snapshot || this.data;
6672 this.data = this.queryBy(fn, scope||this);
6673 this.fireEvent("datachanged", this);
6677 * Query the records by a specified property.
6678 * @param {String} field A field on your records
6679 * @param {String/RegExp} value Either a string that the field
6680 * should start with or a RegExp to test against the field
6681 * @param {Boolean} anyMatch True to match any part not just the beginning
6682 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6684 query : function(property, value, anyMatch){
6685 var fn = this.createFilterFn(property, value, anyMatch);
6686 return fn ? this.queryBy(fn) : this.data.clone();
6690 * Query by a function. The specified function will be called with each
6691 * record in this data source. If the function returns true the record is included
6693 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6694 * @param {Object} scope (optional) The scope of the function (defaults to this)
6695 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6697 queryBy : function(fn, scope){
6698 var data = this.snapshot || this.data;
6699 return data.filterBy(fn, scope||this);
6703 * Collects unique values for a particular dataIndex from this store.
6704 * @param {String} dataIndex The property to collect
6705 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6706 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6707 * @return {Array} An array of the unique values
6709 collect : function(dataIndex, allowNull, bypassFilter){
6710 var d = (bypassFilter === true && this.snapshot) ?
6711 this.snapshot.items : this.data.items;
6712 var v, sv, r = [], l = {};
6713 for(var i = 0, len = d.length; i < len; i++){
6714 v = d[i].data[dataIndex];
6716 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6725 * Revert to a view of the Record cache with no filtering applied.
6726 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6728 clearFilter : function(suppressEvent){
6729 if(this.snapshot && this.snapshot != this.data){
6730 this.data = this.snapshot;
6731 delete this.snapshot;
6732 if(suppressEvent !== true){
6733 this.fireEvent("datachanged", this);
6739 afterEdit : function(record){
6740 if(this.modified.indexOf(record) == -1){
6741 this.modified.push(record);
6743 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6747 afterReject : function(record){
6748 this.modified.remove(record);
6749 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6753 afterCommit : function(record){
6754 this.modified.remove(record);
6755 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6759 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6760 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6762 commitChanges : function(){
6763 var m = this.modified.slice(0);
6765 for(var i = 0, len = m.length; i < len; i++){
6771 * Cancel outstanding changes on all changed records.
6773 rejectChanges : function(){
6774 var m = this.modified.slice(0);
6776 for(var i = 0, len = m.length; i < len; i++){
6781 onMetaChange : function(meta, rtype, o){
6782 this.recordType = rtype;
6783 this.fields = rtype.prototype.fields;
6784 delete this.snapshot;
6785 this.sortInfo = meta.sortInfo || this.sortInfo;
6787 this.fireEvent('metachange', this, this.reader.meta);
6790 moveIndex : function(data, type)
6792 var index = this.indexOf(data);
6794 var newIndex = index + type;
6798 this.insert(newIndex, data);
6803 * Ext JS Library 1.1.1
6804 * Copyright(c) 2006-2007, Ext JS, LLC.
6806 * Originally Released Under LGPL - original licence link has changed is not relivant.
6809 * <script type="text/javascript">
6813 * @class Roo.data.SimpleStore
6814 * @extends Roo.data.Store
6815 * Small helper class to make creating Stores from Array data easier.
6816 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6817 * @cfg {Array} fields An array of field definition objects, or field name strings.
6818 * @cfg {Array} data The multi-dimensional array of data
6820 * @param {Object} config
6822 Roo.data.SimpleStore = function(config){
6823 Roo.data.SimpleStore.superclass.constructor.call(this, {
6825 reader: new Roo.data.ArrayReader({
6828 Roo.data.Record.create(config.fields)
6830 proxy : new Roo.data.MemoryProxy(config.data)
6834 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6836 * Ext JS Library 1.1.1
6837 * Copyright(c) 2006-2007, Ext JS, LLC.
6839 * Originally Released Under LGPL - original licence link has changed is not relivant.
6842 * <script type="text/javascript">
6847 * @extends Roo.data.Store
6848 * @class Roo.data.JsonStore
6849 * Small helper class to make creating Stores for JSON data easier. <br/>
6851 var store = new Roo.data.JsonStore({
6852 url: 'get-images.php',
6854 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6857 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6858 * JsonReader and HttpProxy (unless inline data is provided).</b>
6859 * @cfg {Array} fields An array of field definition objects, or field name strings.
6861 * @param {Object} config
6863 Roo.data.JsonStore = function(c){
6864 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6865 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6866 reader: new Roo.data.JsonReader(c, c.fields)
6869 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6871 * Ext JS Library 1.1.1
6872 * Copyright(c) 2006-2007, Ext JS, LLC.
6874 * Originally Released Under LGPL - original licence link has changed is not relivant.
6877 * <script type="text/javascript">
6881 Roo.data.Field = function(config){
6882 if(typeof config == "string"){
6883 config = {name: config};
6885 Roo.apply(this, config);
6891 var st = Roo.data.SortTypes;
6892 // named sortTypes are supported, here we look them up
6893 if(typeof this.sortType == "string"){
6894 this.sortType = st[this.sortType];
6897 // set default sortType for strings and dates
6901 this.sortType = st.asUCString;
6904 this.sortType = st.asDate;
6907 this.sortType = st.none;
6912 var stripRe = /[\$,%]/g;
6914 // prebuilt conversion function for this field, instead of
6915 // switching every time we're reading a value
6917 var cv, dateFormat = this.dateFormat;
6922 cv = function(v){ return v; };
6925 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6929 return v !== undefined && v !== null && v !== '' ?
6930 parseInt(String(v).replace(stripRe, ""), 10) : '';
6935 return v !== undefined && v !== null && v !== '' ?
6936 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6941 cv = function(v){ return v === true || v === "true" || v == 1; };
6948 if(v instanceof Date){
6952 if(dateFormat == "timestamp"){
6953 return new Date(v*1000);
6955 return Date.parseDate(v, dateFormat);
6957 var parsed = Date.parse(v);
6958 return parsed ? new Date(parsed) : null;
6967 Roo.data.Field.prototype = {
6975 * Ext JS Library 1.1.1
6976 * Copyright(c) 2006-2007, Ext JS, LLC.
6978 * Originally Released Under LGPL - original licence link has changed is not relivant.
6981 * <script type="text/javascript">
6984 // Base class for reading structured data from a data source. This class is intended to be
6985 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6988 * @class Roo.data.DataReader
6989 * Base class for reading structured data from a data source. This class is intended to be
6990 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6993 Roo.data.DataReader = function(meta, recordType){
6997 this.recordType = recordType instanceof Array ?
6998 Roo.data.Record.create(recordType) : recordType;
7001 Roo.data.DataReader.prototype = {
7003 * Create an empty record
7004 * @param {Object} data (optional) - overlay some values
7005 * @return {Roo.data.Record} record created.
7007 newRow : function(d) {
7009 this.recordType.prototype.fields.each(function(c) {
7011 case 'int' : da[c.name] = 0; break;
7012 case 'date' : da[c.name] = new Date(); break;
7013 case 'float' : da[c.name] = 0.0; break;
7014 case 'boolean' : da[c.name] = false; break;
7015 default : da[c.name] = ""; break;
7019 return new this.recordType(Roo.apply(da, d));
7024 * Ext JS Library 1.1.1
7025 * Copyright(c) 2006-2007, Ext JS, LLC.
7027 * Originally Released Under LGPL - original licence link has changed is not relivant.
7030 * <script type="text/javascript">
7034 * @class Roo.data.DataProxy
7035 * @extends Roo.data.Observable
7036 * This class is an abstract base class for implementations which provide retrieval of
7037 * unformatted data objects.<br>
7039 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7040 * (of the appropriate type which knows how to parse the data object) to provide a block of
7041 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7043 * Custom implementations must implement the load method as described in
7044 * {@link Roo.data.HttpProxy#load}.
7046 Roo.data.DataProxy = function(){
7050 * Fires before a network request is made to retrieve a data object.
7051 * @param {Object} This DataProxy object.
7052 * @param {Object} params The params parameter to the load function.
7057 * Fires before the load method's callback is called.
7058 * @param {Object} This DataProxy object.
7059 * @param {Object} o The data object.
7060 * @param {Object} arg The callback argument object passed to the load function.
7064 * @event loadexception
7065 * Fires if an Exception occurs during data retrieval.
7066 * @param {Object} This DataProxy object.
7067 * @param {Object} o The data object.
7068 * @param {Object} arg The callback argument object passed to the load function.
7069 * @param {Object} e The Exception.
7071 loadexception : true
7073 Roo.data.DataProxy.superclass.constructor.call(this);
7076 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7079 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7083 * Ext JS Library 1.1.1
7084 * Copyright(c) 2006-2007, Ext JS, LLC.
7086 * Originally Released Under LGPL - original licence link has changed is not relivant.
7089 * <script type="text/javascript">
7092 * @class Roo.data.MemoryProxy
7093 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7094 * to the Reader when its load method is called.
7096 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7098 Roo.data.MemoryProxy = function(data){
7102 Roo.data.MemoryProxy.superclass.constructor.call(this);
7106 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7108 * Load data from the requested source (in this case an in-memory
7109 * data object passed to the constructor), read the data object into
7110 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7111 * process that block using the passed callback.
7112 * @param {Object} params This parameter is not used by the MemoryProxy class.
7113 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7114 * object into a block of Roo.data.Records.
7115 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7116 * The function must be passed <ul>
7117 * <li>The Record block object</li>
7118 * <li>The "arg" argument from the load function</li>
7119 * <li>A boolean success indicator</li>
7121 * @param {Object} scope The scope in which to call the callback
7122 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7124 load : function(params, reader, callback, scope, arg){
7125 params = params || {};
7128 result = reader.readRecords(this.data);
7130 this.fireEvent("loadexception", this, arg, null, e);
7131 callback.call(scope, null, arg, false);
7134 callback.call(scope, result, arg, true);
7138 update : function(params, records){
7143 * Ext JS Library 1.1.1
7144 * Copyright(c) 2006-2007, Ext JS, LLC.
7146 * Originally Released Under LGPL - original licence link has changed is not relivant.
7149 * <script type="text/javascript">
7152 * @class Roo.data.HttpProxy
7153 * @extends Roo.data.DataProxy
7154 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7155 * configured to reference a certain URL.<br><br>
7157 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7158 * from which the running page was served.<br><br>
7160 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7162 * Be aware that to enable the browser to parse an XML document, the server must set
7163 * the Content-Type header in the HTTP response to "text/xml".
7165 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7166 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7167 * will be used to make the request.
7169 Roo.data.HttpProxy = function(conn){
7170 Roo.data.HttpProxy.superclass.constructor.call(this);
7171 // is conn a conn config or a real conn?
7173 this.useAjax = !conn || !conn.events;
7177 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7178 // thse are take from connection...
7181 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7184 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7185 * extra parameters to each request made by this object. (defaults to undefined)
7188 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7189 * to each request made by this object. (defaults to undefined)
7192 * @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)
7195 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7198 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7204 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7208 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7209 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7210 * a finer-grained basis than the DataProxy events.
7212 getConnection : function(){
7213 return this.useAjax ? Roo.Ajax : this.conn;
7217 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7218 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7219 * process that block using the passed callback.
7220 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7221 * for the request to the remote server.
7222 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7223 * object into a block of Roo.data.Records.
7224 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7225 * The function must be passed <ul>
7226 * <li>The Record block object</li>
7227 * <li>The "arg" argument from the load function</li>
7228 * <li>A boolean success indicator</li>
7230 * @param {Object} scope The scope in which to call the callback
7231 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7233 load : function(params, reader, callback, scope, arg){
7234 if(this.fireEvent("beforeload", this, params) !== false){
7236 params : params || {},
7238 callback : callback,
7243 callback : this.loadResponse,
7247 Roo.applyIf(o, this.conn);
7248 if(this.activeRequest){
7249 Roo.Ajax.abort(this.activeRequest);
7251 this.activeRequest = Roo.Ajax.request(o);
7253 this.conn.request(o);
7256 callback.call(scope||this, null, arg, false);
7261 loadResponse : function(o, success, response){
7262 delete this.activeRequest;
7264 this.fireEvent("loadexception", this, o, response);
7265 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7270 result = o.reader.read(response);
7272 this.fireEvent("loadexception", this, o, response, e);
7273 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7277 this.fireEvent("load", this, o, o.request.arg);
7278 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7282 update : function(dataSet){
7287 updateResponse : function(dataSet){
7292 * Ext JS Library 1.1.1
7293 * Copyright(c) 2006-2007, Ext JS, LLC.
7295 * Originally Released Under LGPL - original licence link has changed is not relivant.
7298 * <script type="text/javascript">
7302 * @class Roo.data.ScriptTagProxy
7303 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7304 * other than the originating domain of the running page.<br><br>
7306 * <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
7307 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7309 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7310 * source code that is used as the source inside a <script> tag.<br><br>
7312 * In order for the browser to process the returned data, the server must wrap the data object
7313 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7314 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7315 * depending on whether the callback name was passed:
7318 boolean scriptTag = false;
7319 String cb = request.getParameter("callback");
7322 response.setContentType("text/javascript");
7324 response.setContentType("application/x-json");
7326 Writer out = response.getWriter();
7328 out.write(cb + "(");
7330 out.print(dataBlock.toJsonString());
7337 * @param {Object} config A configuration object.
7339 Roo.data.ScriptTagProxy = function(config){
7340 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7341 Roo.apply(this, config);
7342 this.head = document.getElementsByTagName("head")[0];
7345 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7347 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7349 * @cfg {String} url The URL from which to request the data object.
7352 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7356 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7357 * the server the name of the callback function set up by the load call to process the returned data object.
7358 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7359 * javascript output which calls this named function passing the data object as its only parameter.
7361 callbackParam : "callback",
7363 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7364 * name to the request.
7369 * Load data from the configured URL, read the data object into
7370 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7371 * process that block using the passed callback.
7372 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7373 * for the request to the remote server.
7374 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7375 * object into a block of Roo.data.Records.
7376 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7377 * The function must be passed <ul>
7378 * <li>The Record block object</li>
7379 * <li>The "arg" argument from the load function</li>
7380 * <li>A boolean success indicator</li>
7382 * @param {Object} scope The scope in which to call the callback
7383 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7385 load : function(params, reader, callback, scope, arg){
7386 if(this.fireEvent("beforeload", this, params) !== false){
7388 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7391 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7393 url += "&_dc=" + (new Date().getTime());
7395 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7398 cb : "stcCallback"+transId,
7399 scriptId : "stcScript"+transId,
7403 callback : callback,
7409 window[trans.cb] = function(o){
7410 conn.handleResponse(o, trans);
7413 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7415 if(this.autoAbort !== false){
7419 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7421 var script = document.createElement("script");
7422 script.setAttribute("src", url);
7423 script.setAttribute("type", "text/javascript");
7424 script.setAttribute("id", trans.scriptId);
7425 this.head.appendChild(script);
7429 callback.call(scope||this, null, arg, false);
7434 isLoading : function(){
7435 return this.trans ? true : false;
7439 * Abort the current server request.
7442 if(this.isLoading()){
7443 this.destroyTrans(this.trans);
7448 destroyTrans : function(trans, isLoaded){
7449 this.head.removeChild(document.getElementById(trans.scriptId));
7450 clearTimeout(trans.timeoutId);
7452 window[trans.cb] = undefined;
7454 delete window[trans.cb];
7457 // if hasn't been loaded, wait for load to remove it to prevent script error
7458 window[trans.cb] = function(){
7459 window[trans.cb] = undefined;
7461 delete window[trans.cb];
7468 handleResponse : function(o, trans){
7470 this.destroyTrans(trans, true);
7473 result = trans.reader.readRecords(o);
7475 this.fireEvent("loadexception", this, o, trans.arg, e);
7476 trans.callback.call(trans.scope||window, null, trans.arg, false);
7479 this.fireEvent("load", this, o, trans.arg);
7480 trans.callback.call(trans.scope||window, result, trans.arg, true);
7484 handleFailure : function(trans){
7486 this.destroyTrans(trans, false);
7487 this.fireEvent("loadexception", this, null, trans.arg);
7488 trans.callback.call(trans.scope||window, null, trans.arg, false);
7492 * Ext JS Library 1.1.1
7493 * Copyright(c) 2006-2007, Ext JS, LLC.
7495 * Originally Released Under LGPL - original licence link has changed is not relivant.
7498 * <script type="text/javascript">
7502 * @class Roo.data.JsonReader
7503 * @extends Roo.data.DataReader
7504 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7505 * based on mappings in a provided Roo.data.Record constructor.
7507 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7508 * in the reply previously.
7513 var RecordDef = Roo.data.Record.create([
7514 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7515 {name: 'occupation'} // This field will use "occupation" as the mapping.
7517 var myReader = new Roo.data.JsonReader({
7518 totalProperty: "results", // The property which contains the total dataset size (optional)
7519 root: "rows", // The property which contains an Array of row objects
7520 id: "id" // The property within each row object that provides an ID for the record (optional)
7524 * This would consume a JSON file like this:
7526 { 'results': 2, 'rows': [
7527 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7528 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7531 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7532 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7533 * paged from the remote server.
7534 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7535 * @cfg {String} root name of the property which contains the Array of row objects.
7536 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7538 * Create a new JsonReader
7539 * @param {Object} meta Metadata configuration options
7540 * @param {Object} recordType Either an Array of field definition objects,
7541 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7543 Roo.data.JsonReader = function(meta, recordType){
7546 // set some defaults:
7548 totalProperty: 'total',
7549 successProperty : 'success',
7554 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7556 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7559 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7560 * Used by Store query builder to append _requestMeta to params.
7563 metaFromRemote : false,
7565 * This method is only used by a DataProxy which has retrieved data from a remote server.
7566 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7567 * @return {Object} data A data block which is used by an Roo.data.Store object as
7568 * a cache of Roo.data.Records.
7570 read : function(response){
7571 var json = response.responseText;
7573 var o = /* eval:var:o */ eval("("+json+")");
7575 throw {message: "JsonReader.read: Json object not found"};
7581 this.metaFromRemote = true;
7582 this.meta = o.metaData;
7583 this.recordType = Roo.data.Record.create(o.metaData.fields);
7584 this.onMetaChange(this.meta, this.recordType, o);
7586 return this.readRecords(o);
7589 // private function a store will implement
7590 onMetaChange : function(meta, recordType, o){
7597 simpleAccess: function(obj, subsc) {
7604 getJsonAccessor: function(){
7606 return function(expr) {
7608 return(re.test(expr))
7609 ? new Function("obj", "return obj." + expr)
7619 * Create a data block containing Roo.data.Records from an XML document.
7620 * @param {Object} o An object which contains an Array of row objects in the property specified
7621 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7622 * which contains the total size of the dataset.
7623 * @return {Object} data A data block which is used by an Roo.data.Store object as
7624 * a cache of Roo.data.Records.
7626 readRecords : function(o){
7628 * After any data loads, the raw JSON data is available for further custom processing.
7632 var s = this.meta, Record = this.recordType,
7633 f = Record.prototype.fields, fi = f.items, fl = f.length;
7635 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7637 if(s.totalProperty) {
7638 this.getTotal = this.getJsonAccessor(s.totalProperty);
7640 if(s.successProperty) {
7641 this.getSuccess = this.getJsonAccessor(s.successProperty);
7643 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7645 var g = this.getJsonAccessor(s.id);
7646 this.getId = function(rec) {
7648 return (r === undefined || r === "") ? null : r;
7651 this.getId = function(){return null;};
7654 for(var jj = 0; jj < fl; jj++){
7656 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7657 this.ef[jj] = this.getJsonAccessor(map);
7661 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7662 if(s.totalProperty){
7663 var vt = parseInt(this.getTotal(o), 10);
7668 if(s.successProperty){
7669 var vs = this.getSuccess(o);
7670 if(vs === false || vs === 'false'){
7675 for(var i = 0; i < c; i++){
7678 var id = this.getId(n);
7679 for(var j = 0; j < fl; j++){
7681 var v = this.ef[j](n);
7683 Roo.log('missing convert for ' + f.name);
7687 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7689 var record = new Record(values, id);
7691 records[i] = record;
7697 totalRecords : totalRecords
7702 * Ext JS Library 1.1.1
7703 * Copyright(c) 2006-2007, Ext JS, LLC.
7705 * Originally Released Under LGPL - original licence link has changed is not relivant.
7708 * <script type="text/javascript">
7712 * @class Roo.data.ArrayReader
7713 * @extends Roo.data.DataReader
7714 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7715 * Each element of that Array represents a row of data fields. The
7716 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7717 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7721 var RecordDef = Roo.data.Record.create([
7722 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7723 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7725 var myReader = new Roo.data.ArrayReader({
7726 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7730 * This would consume an Array like this:
7732 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7734 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7736 * Create a new JsonReader
7737 * @param {Object} meta Metadata configuration options.
7738 * @param {Object} recordType Either an Array of field definition objects
7739 * as specified to {@link Roo.data.Record#create},
7740 * or an {@link Roo.data.Record} object
7741 * created using {@link Roo.data.Record#create}.
7743 Roo.data.ArrayReader = function(meta, recordType){
7744 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7747 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7749 * Create a data block containing Roo.data.Records from an XML document.
7750 * @param {Object} o An Array of row objects which represents the dataset.
7751 * @return {Object} data A data block which is used by an Roo.data.Store object as
7752 * a cache of Roo.data.Records.
7754 readRecords : function(o){
7755 var sid = this.meta ? this.meta.id : null;
7756 var recordType = this.recordType, fields = recordType.prototype.fields;
7759 for(var i = 0; i < root.length; i++){
7762 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7763 for(var j = 0, jlen = fields.length; j < jlen; j++){
7764 var f = fields.items[j];
7765 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7766 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7770 var record = new recordType(values, id);
7772 records[records.length] = record;
7776 totalRecords : records.length
7785 * @class Roo.bootstrap.ComboBox
7786 * @extends Roo.bootstrap.TriggerField
7787 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7788 * @cfg {Boolean} append (true|false) default false
7790 * Create a new ComboBox.
7791 * @param {Object} config Configuration options
7793 Roo.bootstrap.ComboBox = function(config){
7794 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7798 * Fires when the dropdown list is expanded
7799 * @param {Roo.bootstrap.ComboBox} combo This combo box
7804 * Fires when the dropdown list is collapsed
7805 * @param {Roo.bootstrap.ComboBox} combo This combo box
7809 * @event beforeselect
7810 * Fires before a list item is selected. Return false to cancel the selection.
7811 * @param {Roo.bootstrap.ComboBox} combo This combo box
7812 * @param {Roo.data.Record} record The data record returned from the underlying store
7813 * @param {Number} index The index of the selected item in the dropdown list
7815 'beforeselect' : true,
7818 * Fires when a list item is selected
7819 * @param {Roo.bootstrap.ComboBox} combo This combo box
7820 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7821 * @param {Number} index The index of the selected item in the dropdown list
7825 * @event beforequery
7826 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7827 * The event object passed has these properties:
7828 * @param {Roo.bootstrap.ComboBox} combo This combo box
7829 * @param {String} query The query
7830 * @param {Boolean} forceAll true to force "all" query
7831 * @param {Boolean} cancel true to cancel the query
7832 * @param {Object} e The query event object
7834 'beforequery': true,
7837 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7838 * @param {Roo.bootstrap.ComboBox} combo This combo box
7843 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7844 * @param {Roo.bootstrap.ComboBox} combo This combo box
7845 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7850 * Fires when the remove value from the combobox array
7851 * @param {Roo.bootstrap.ComboBox} combo This combo box
7858 this.selectedIndex = -1;
7859 if(this.mode == 'local'){
7860 if(config.queryDelay === undefined){
7861 this.queryDelay = 10;
7863 if(config.minChars === undefined){
7869 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7872 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7873 * rendering into an Roo.Editor, defaults to false)
7876 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7877 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7880 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7883 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7884 * the dropdown list (defaults to undefined, with no header element)
7888 * @cfg {String/Roo.Template} tpl The template to use to render the output
7892 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7894 listWidth: undefined,
7896 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7897 * mode = 'remote' or 'text' if mode = 'local')
7899 displayField: undefined,
7901 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7902 * mode = 'remote' or 'value' if mode = 'local').
7903 * Note: use of a valueField requires the user make a selection
7904 * in order for a value to be mapped.
7906 valueField: undefined,
7910 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7911 * field's data value (defaults to the underlying DOM element's name)
7913 hiddenName: undefined,
7915 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7919 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7921 selectedClass: 'active',
7924 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7928 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7929 * anchor positions (defaults to 'tl-bl')
7931 listAlign: 'tl-bl?',
7933 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7937 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7938 * query specified by the allQuery config option (defaults to 'query')
7940 triggerAction: 'query',
7942 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7943 * (defaults to 4, does not apply if editable = false)
7947 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7948 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7952 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7953 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7957 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7958 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7962 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7963 * when editable = true (defaults to false)
7965 selectOnFocus:false,
7967 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7969 queryParam: 'query',
7971 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7972 * when mode = 'remote' (defaults to 'Loading...')
7974 loadingText: 'Loading...',
7976 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7980 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7984 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7985 * traditional select (defaults to true)
7989 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7993 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7997 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7998 * listWidth has a higher value)
8002 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8003 * allow the user to set arbitrary text into the field (defaults to false)
8005 forceSelection:false,
8007 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8008 * if typeAhead = true (defaults to 250)
8010 typeAheadDelay : 250,
8012 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8013 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8015 valueNotFoundText : undefined,
8017 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8022 * @cfg {Boolean} disableClear Disable showing of clear button.
8024 disableClear : false,
8026 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
8028 alwaysQuery : false,
8031 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8045 // element that contains real text value.. (when hidden is used..)
8048 initEvents: function(){
8051 throw "can not find store for combo";
8053 this.store = Roo.factory(this.store, Roo.data);
8057 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8060 if(this.hiddenName){
8062 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8064 this.hiddenField.dom.value =
8065 this.hiddenValue !== undefined ? this.hiddenValue :
8066 this.value !== undefined ? this.value : '';
8068 // prevent input submission
8069 this.el.dom.removeAttribute('name');
8070 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8075 // this.el.dom.setAttribute('autocomplete', 'off');
8078 var cls = 'x-combo-list';
8079 this.list = this.el.select('ul.dropdown-menu',true).first();
8081 //this.list = new Roo.Layer({
8082 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8085 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8086 this.list.setWidth(lw);
8088 this.list.on('mouseover', this.onViewOver, this);
8089 this.list.on('mousemove', this.onViewMove, this);
8091 this.list.on('scroll', this.onViewScroll, this);
8094 this.list.swallowEvent('mousewheel');
8095 this.assetHeight = 0;
8098 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8099 this.assetHeight += this.header.getHeight();
8102 this.innerList = this.list.createChild({cls:cls+'-inner'});
8103 this.innerList.on('mouseover', this.onViewOver, this);
8104 this.innerList.on('mousemove', this.onViewMove, this);
8105 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8107 if(this.allowBlank && !this.pageSize && !this.disableClear){
8108 this.footer = this.list.createChild({cls:cls+'-ft'});
8109 this.pageTb = new Roo.Toolbar(this.footer);
8113 this.footer = this.list.createChild({cls:cls+'-ft'});
8114 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8115 {pageSize: this.pageSize});
8119 if (this.pageTb && this.allowBlank && !this.disableClear) {
8121 this.pageTb.add(new Roo.Toolbar.Fill(), {
8122 cls: 'x-btn-icon x-btn-clear',
8128 _this.onSelect(false, -1);
8133 this.assetHeight += this.footer.getHeight();
8138 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8141 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8142 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8144 //this.view.wrapEl.setDisplayed(false);
8145 this.view.on('click', this.onViewClick, this);
8149 this.store.on('beforeload', this.onBeforeLoad, this);
8150 this.store.on('load', this.onLoad, this);
8151 this.store.on('loadexception', this.onLoadException, this);
8154 this.resizer = new Roo.Resizable(this.list, {
8155 pinned:true, handles:'se'
8157 this.resizer.on('resize', function(r, w, h){
8158 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8160 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8161 this.restrictHeight();
8163 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8167 this.editable = true;
8168 this.setEditable(false);
8173 if (typeof(this.events.add.listeners) != 'undefined') {
8175 this.addicon = this.wrap.createChild(
8176 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8178 this.addicon.on('click', function(e) {
8179 this.fireEvent('add', this);
8182 if (typeof(this.events.edit.listeners) != 'undefined') {
8184 this.editicon = this.wrap.createChild(
8185 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8187 this.editicon.setStyle('margin-left', '40px');
8189 this.editicon.on('click', function(e) {
8191 // we fire even if inothing is selected..
8192 this.fireEvent('edit', this, this.lastData );
8198 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8200 this.inKeyMode = true;
8204 "down" : function(e){
8205 if(!this.isExpanded()){
8206 this.onTriggerClick();
8208 this.inKeyMode = true;
8213 "enter" : function(e){
8218 "esc" : function(e){
8222 "tab" : function(e){
8225 if(this.fireEvent("specialkey", this, e)){
8226 this.onViewClick(false);
8234 doRelay : function(foo, bar, hname){
8235 if(hname == 'down' || this.scope.isExpanded()){
8236 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8245 this.queryDelay = Math.max(this.queryDelay || 10,
8246 this.mode == 'local' ? 10 : 250);
8249 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8252 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8254 if(this.editable !== false){
8255 this.inputEl().on("keyup", this.onKeyUp, this);
8257 if(this.forceSelection){
8258 this.on('blur', this.doForce, this);
8262 this.choices = this.el.select('ul.select2-choices', true).first();
8263 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8267 onDestroy : function(){
8269 this.view.setStore(null);
8270 this.view.el.removeAllListeners();
8271 this.view.el.remove();
8272 this.view.purgeListeners();
8275 this.list.dom.innerHTML = '';
8278 this.store.un('beforeload', this.onBeforeLoad, this);
8279 this.store.un('load', this.onLoad, this);
8280 this.store.un('loadexception', this.onLoadException, this);
8282 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8286 fireKey : function(e){
8287 if(e.isNavKeyPress() && !this.list.isVisible()){
8288 this.fireEvent("specialkey", this, e);
8293 onResize: function(w, h){
8294 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8296 // if(typeof w != 'number'){
8297 // // we do not handle it!?!?
8300 // var tw = this.trigger.getWidth();
8301 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8302 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8304 // this.inputEl().setWidth( this.adjustWidth('input', x));
8306 // //this.trigger.setStyle('left', x+'px');
8308 // if(this.list && this.listWidth === undefined){
8309 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8310 // this.list.setWidth(lw);
8311 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8319 * Allow or prevent the user from directly editing the field text. If false is passed,
8320 * the user will only be able to select from the items defined in the dropdown list. This method
8321 * is the runtime equivalent of setting the 'editable' config option at config time.
8322 * @param {Boolean} value True to allow the user to directly edit the field text
8324 setEditable : function(value){
8325 if(value == this.editable){
8328 this.editable = value;
8330 this.inputEl().dom.setAttribute('readOnly', true);
8331 this.inputEl().on('mousedown', this.onTriggerClick, this);
8332 this.inputEl().addClass('x-combo-noedit');
8334 this.inputEl().dom.setAttribute('readOnly', false);
8335 this.inputEl().un('mousedown', this.onTriggerClick, this);
8336 this.inputEl().removeClass('x-combo-noedit');
8342 onBeforeLoad : function(combo,opts){
8347 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8349 this.restrictHeight();
8350 this.selectedIndex = -1;
8354 onLoad : function(){
8356 this.hasQuery = false;
8362 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8363 this.loading.hide();
8366 if(this.store.getCount() > 0){
8368 this.restrictHeight();
8369 if(this.lastQuery == this.allQuery){
8371 this.inputEl().dom.select();
8373 if(!this.selectByValue(this.value, true)){
8374 this.select(0, true);
8378 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8379 this.taTask.delay(this.typeAheadDelay);
8383 this.onEmptyResults();
8389 onLoadException : function()
8391 this.hasQuery = false;
8393 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8394 this.loading.hide();
8398 Roo.log(this.store.reader.jsonData);
8399 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8401 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8407 onTypeAhead : function(){
8408 if(this.store.getCount() > 0){
8409 var r = this.store.getAt(0);
8410 var newValue = r.data[this.displayField];
8411 var len = newValue.length;
8412 var selStart = this.getRawValue().length;
8414 if(selStart != len){
8415 this.setRawValue(newValue);
8416 this.selectText(selStart, newValue.length);
8422 onSelect : function(record, index){
8424 if(this.fireEvent('beforeselect', this, record, index) !== false){
8426 this.setFromData(index > -1 ? record.data : false);
8429 this.fireEvent('select', this, record, index);
8434 * Returns the currently selected field value or empty string if no value is set.
8435 * @return {String} value The selected value
8437 getValue : function(){
8440 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8443 if(this.valueField){
8444 return typeof this.value != 'undefined' ? this.value : '';
8446 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8451 * Clears any text/value currently set in the field
8453 clearValue : function(){
8454 if(this.hiddenField){
8455 this.hiddenField.dom.value = '';
8458 this.setRawValue('');
8459 this.lastSelectionText = '';
8464 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8465 * will be displayed in the field. If the value does not match the data value of an existing item,
8466 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8467 * Otherwise the field will be blank (although the value will still be set).
8468 * @param {String} value The value to match
8470 setValue : function(v){
8477 if(this.valueField){
8478 var r = this.findRecord(this.valueField, v);
8480 text = r.data[this.displayField];
8481 }else if(this.valueNotFoundText !== undefined){
8482 text = this.valueNotFoundText;
8485 this.lastSelectionText = text;
8486 if(this.hiddenField){
8487 this.hiddenField.dom.value = v;
8489 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8493 * @property {Object} the last set data for the element
8498 * Sets the value of the field based on a object which is related to the record format for the store.
8499 * @param {Object} value the value to set as. or false on reset?
8501 setFromData : function(o){
8508 var dv = ''; // display value
8509 var vv = ''; // value value..
8511 if (this.displayField) {
8512 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8514 // this is an error condition!!!
8515 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8518 if(this.valueField){
8519 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8522 if(this.hiddenField){
8523 this.hiddenField.dom.value = vv;
8525 this.lastSelectionText = dv;
8526 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8530 // no hidden field.. - we store the value in 'value', but still display
8531 // display field!!!!
8532 this.lastSelectionText = dv;
8533 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8540 // overridden so that last data is reset..
8541 this.setValue(this.originalValue);
8542 this.clearInvalid();
8543 this.lastData = false;
8545 this.view.clearSelections();
8549 findRecord : function(prop, value){
8551 if(this.store.getCount() > 0){
8552 this.store.each(function(r){
8553 if(r.data[prop] == value){
8565 // returns hidden if it's set..
8566 if (!this.rendered) {return ''};
8567 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8571 onViewMove : function(e, t){
8572 this.inKeyMode = false;
8576 onViewOver : function(e, t){
8577 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8580 var item = this.view.findItemFromChild(t);
8582 var index = this.view.indexOf(item);
8583 this.select(index, false);
8588 onViewClick : function(doFocus)
8590 var index = this.view.getSelectedIndexes()[0];
8591 var r = this.store.getAt(index);
8593 this.onSelect(r, index);
8595 if(doFocus !== false && !this.blockFocus){
8596 this.inputEl().focus();
8601 restrictHeight : function(){
8602 //this.innerList.dom.style.height = '';
8603 //var inner = this.innerList.dom;
8604 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8605 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8606 //this.list.beginUpdate();
8607 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8608 this.list.alignTo(this.inputEl(), this.listAlign);
8609 //this.list.endUpdate();
8613 onEmptyResults : function(){
8618 * Returns true if the dropdown list is expanded, else false.
8620 isExpanded : function(){
8621 return this.list.isVisible();
8625 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8626 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8627 * @param {String} value The data value of the item to select
8628 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8629 * selected item if it is not currently in view (defaults to true)
8630 * @return {Boolean} True if the value matched an item in the list, else false
8632 selectByValue : function(v, scrollIntoView){
8633 if(v !== undefined && v !== null){
8634 var r = this.findRecord(this.valueField || this.displayField, v);
8636 this.select(this.store.indexOf(r), scrollIntoView);
8644 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8645 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8646 * @param {Number} index The zero-based index of the list item to select
8647 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8648 * selected item if it is not currently in view (defaults to true)
8650 select : function(index, scrollIntoView){
8651 this.selectedIndex = index;
8652 this.view.select(index);
8653 if(scrollIntoView !== false){
8654 var el = this.view.getNode(index);
8656 //this.innerList.scrollChildIntoView(el, false);
8663 selectNext : function(){
8664 var ct = this.store.getCount();
8666 if(this.selectedIndex == -1){
8668 }else if(this.selectedIndex < ct-1){
8669 this.select(this.selectedIndex+1);
8675 selectPrev : function(){
8676 var ct = this.store.getCount();
8678 if(this.selectedIndex == -1){
8680 }else if(this.selectedIndex != 0){
8681 this.select(this.selectedIndex-1);
8687 onKeyUp : function(e){
8688 if(this.editable !== false && !e.isSpecialKey()){
8689 this.lastKey = e.getKey();
8690 this.dqTask.delay(this.queryDelay);
8695 validateBlur : function(){
8696 return !this.list || !this.list.isVisible();
8700 initQuery : function(){
8701 this.doQuery(this.getRawValue());
8705 doForce : function(){
8706 if(this.el.dom.value.length > 0){
8708 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8714 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8715 * query allowing the query action to be canceled if needed.
8716 * @param {String} query The SQL query to execute
8717 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8718 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8719 * saved in the current store (defaults to false)
8721 doQuery : function(q, forceAll){
8723 if(q === undefined || q === null){
8732 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8737 forceAll = qe.forceAll;
8738 if(forceAll === true || (q.length >= this.minChars)){
8740 this.hasQuery = true;
8742 if(this.lastQuery != q || this.alwaysQuery){
8744 if(this.mode == 'local'){
8745 this.selectedIndex = -1;
8747 this.store.clearFilter();
8749 this.store.filter(this.displayField, q);
8753 this.store.baseParams[this.queryParam] = q;
8755 var options = {params : this.getParams(q)};
8759 options.params.start = this.page * this.pageSize;
8762 this.store.load(options);
8766 this.selectedIndex = -1;
8771 this.loadNext = false;
8775 getParams : function(q){
8777 //p[this.queryParam] = q;
8781 p.limit = this.pageSize;
8787 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8789 collapse : function(){
8790 if(!this.isExpanded()){
8795 Roo.get(document).un('mousedown', this.collapseIf, this);
8796 Roo.get(document).un('mousewheel', this.collapseIf, this);
8797 if (!this.editable) {
8798 Roo.get(document).un('keydown', this.listKeyPress, this);
8800 this.fireEvent('collapse', this);
8804 collapseIf : function(e){
8805 var in_combo = e.within(this.el);
8806 var in_list = e.within(this.list);
8808 if (in_combo || in_list) {
8809 //e.stopPropagation();
8818 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8820 expand : function(){
8822 if(this.isExpanded() || !this.hasFocus){
8826 this.list.alignTo(this.inputEl(), this.listAlign);
8828 Roo.get(document).on('mousedown', this.collapseIf, this);
8829 Roo.get(document).on('mousewheel', this.collapseIf, this);
8830 if (!this.editable) {
8831 Roo.get(document).on('keydown', this.listKeyPress, this);
8834 this.fireEvent('expand', this);
8838 // Implements the default empty TriggerField.onTriggerClick function
8839 onTriggerClick : function()
8841 Roo.log('trigger click');
8848 this.loadNext = false;
8850 if(this.isExpanded()){
8852 if (!this.blockFocus) {
8853 this.inputEl().focus();
8857 this.hasFocus = true;
8858 if(this.triggerAction == 'all') {
8859 this.doQuery(this.allQuery, true);
8861 this.doQuery(this.getRawValue());
8863 if (!this.blockFocus) {
8864 this.inputEl().focus();
8868 listKeyPress : function(e)
8870 //Roo.log('listkeypress');
8871 // scroll to first matching element based on key pres..
8872 if (e.isSpecialKey()) {
8875 var k = String.fromCharCode(e.getKey()).toUpperCase();
8878 var csel = this.view.getSelectedNodes();
8879 var cselitem = false;
8881 var ix = this.view.indexOf(csel[0]);
8882 cselitem = this.store.getAt(ix);
8883 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8889 this.store.each(function(v) {
8891 // start at existing selection.
8892 if (cselitem.id == v.id) {
8898 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8899 match = this.store.indexOf(v);
8905 if (match === false) {
8906 return true; // no more action?
8909 this.view.select(match);
8910 var sn = Roo.get(this.view.getSelectedNodes()[0])
8911 //sn.scrollIntoView(sn.dom.parentNode, false);
8914 onViewScroll : function(e, t){
8916 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8920 this.hasQuery = true;
8922 this.loading = this.list.select('.loading', true).first();
8924 if(this.loading === null){
8925 this.list.createChild({
8927 cls: 'loading select2-more-results select2-active',
8928 html: 'Loading more results...'
8931 this.loading = this.list.select('.loading', true).first();
8933 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8935 this.loading.hide();
8938 this.loading.show();
8943 this.loadNext = true;
8945 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8950 addItem : function(o)
8952 var dv = ''; // display value
8954 if (this.displayField) {
8955 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8957 // this is an error condition!!!
8958 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8965 var choice = this.choices.createChild({
8967 cls: 'select2-search-choice',
8976 cls: 'select2-search-choice-close',
8981 }, this.searchField);
8983 var close = choice.select('a.select2-search-choice-close', true).first()
8985 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8992 this.inputEl().dom.value = '';
8996 onRemoveItem : function(e, _self, o)
8998 Roo.log('remove item');
8999 var index = this.item.indexOf(o.data) * 1;
9002 Roo.log('not this item?!');
9006 this.item.splice(index, 1);
9011 this.fireEvent('remove', this);
9015 syncValue : function()
9017 if(!this.item.length){
9024 Roo.each(this.item, function(i){
9025 if(_this.valueField){
9026 value.push(i[_this.valueField]);
9033 this.value = value.join(',');
9035 if(this.hiddenField){
9036 this.hiddenField.dom.value = this.value;
9040 clearItem : function()
9048 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9058 * @cfg {Boolean} grow
9062 * @cfg {Number} growMin
9066 * @cfg {Number} growMax
9076 * Ext JS Library 1.1.1
9077 * Copyright(c) 2006-2007, Ext JS, LLC.
9079 * Originally Released Under LGPL - original licence link has changed is not relivant.
9082 * <script type="text/javascript">
9087 * @extends Roo.util.Observable
9088 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9089 * This class also supports single and multi selection modes. <br>
9090 * Create a data model bound view:
9092 var store = new Roo.data.Store(...);
9094 var view = new Roo.View({
9096 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9099 selectedClass: "ydataview-selected",
9103 // listen for node click?
9104 view.on("click", function(vw, index, node, e){
9105 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9109 dataModel.load("foobar.xml");
9111 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9113 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9114 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9116 * Note: old style constructor is still suported (container, template, config)
9120 * @param {Object} config The config object
9123 Roo.View = function(config, depreciated_tpl, depreciated_config){
9125 if (typeof(depreciated_tpl) == 'undefined') {
9126 // new way.. - universal constructor.
9127 Roo.apply(this, config);
9128 this.el = Roo.get(this.el);
9131 this.el = Roo.get(config);
9132 this.tpl = depreciated_tpl;
9133 Roo.apply(this, depreciated_config);
9135 this.wrapEl = this.el.wrap().wrap();
9136 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9139 if(typeof(this.tpl) == "string"){
9140 this.tpl = new Roo.Template(this.tpl);
9142 // support xtype ctors..
9143 this.tpl = new Roo.factory(this.tpl, Roo);
9155 * @event beforeclick
9156 * Fires before a click is processed. Returns false to cancel the default action.
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 "beforeclick" : true,
9165 * Fires when a template node is clicked.
9166 * @param {Roo.View} this
9167 * @param {Number} index The index of the target node
9168 * @param {HTMLElement} node The target node
9169 * @param {Roo.EventObject} e The raw event object
9174 * Fires when a template node is double clicked.
9175 * @param {Roo.View} this
9176 * @param {Number} index The index of the target node
9177 * @param {HTMLElement} node The target node
9178 * @param {Roo.EventObject} e The raw event object
9182 * @event contextmenu
9183 * Fires when a template node is right clicked.
9184 * @param {Roo.View} this
9185 * @param {Number} index The index of the target node
9186 * @param {HTMLElement} node The target node
9187 * @param {Roo.EventObject} e The raw event object
9189 "contextmenu" : true,
9191 * @event selectionchange
9192 * Fires when the selected nodes change.
9193 * @param {Roo.View} this
9194 * @param {Array} selections Array of the selected nodes
9196 "selectionchange" : true,
9199 * @event beforeselect
9200 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9201 * @param {Roo.View} this
9202 * @param {HTMLElement} node The node to be selected
9203 * @param {Array} selections Array of currently selected nodes
9205 "beforeselect" : true,
9207 * @event preparedata
9208 * Fires on every row to render, to allow you to change the data.
9209 * @param {Roo.View} this
9210 * @param {Object} data to be rendered (change this)
9212 "preparedata" : true
9220 "click": this.onClick,
9221 "dblclick": this.onDblClick,
9222 "contextmenu": this.onContextMenu,
9226 this.selections = [];
9228 this.cmp = new Roo.CompositeElementLite([]);
9230 this.store = Roo.factory(this.store, Roo.data);
9231 this.setStore(this.store, true);
9234 if ( this.footer && this.footer.xtype) {
9236 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9238 this.footer.dataSource = this.store
9239 this.footer.container = fctr;
9240 this.footer = Roo.factory(this.footer, Roo);
9241 fctr.insertFirst(this.el);
9243 // this is a bit insane - as the paging toolbar seems to detach the el..
9244 // dom.parentNode.parentNode.parentNode
9245 // they get detached?
9249 Roo.View.superclass.constructor.call(this);
9254 Roo.extend(Roo.View, Roo.util.Observable, {
9257 * @cfg {Roo.data.Store} store Data store to load data from.
9262 * @cfg {String|Roo.Element} el The container element.
9267 * @cfg {String|Roo.Template} tpl The template used by this View
9271 * @cfg {String} dataName the named area of the template to use as the data area
9272 * Works with domtemplates roo-name="name"
9276 * @cfg {String} selectedClass The css class to add to selected nodes
9278 selectedClass : "x-view-selected",
9280 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9285 * @cfg {String} text to display on mask (default Loading)
9289 * @cfg {Boolean} multiSelect Allow multiple selection
9291 multiSelect : false,
9293 * @cfg {Boolean} singleSelect Allow single selection
9295 singleSelect: false,
9298 * @cfg {Boolean} toggleSelect - selecting
9300 toggleSelect : false,
9303 * Returns the element this view is bound to.
9304 * @return {Roo.Element}
9313 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9315 refresh : function(){
9319 // if we are using something like 'domtemplate', then
9320 // the what gets used is:
9321 // t.applySubtemplate(NAME, data, wrapping data..)
9322 // the outer template then get' applied with
9323 // the store 'extra data'
9324 // and the body get's added to the
9325 // roo-name="data" node?
9326 // <span class='roo-tpl-{name}'></span> ?????
9330 this.clearSelections();
9333 var records = this.store.getRange();
9334 if(records.length < 1) {
9336 // is this valid?? = should it render a template??
9338 this.el.update(this.emptyText);
9342 if (this.dataName) {
9343 this.el.update(t.apply(this.store.meta)); //????
9344 el = this.el.child('.roo-tpl-' + this.dataName);
9347 for(var i = 0, len = records.length; i < len; i++){
9348 var data = this.prepareData(records[i].data, i, records[i]);
9349 this.fireEvent("preparedata", this, data, i, records[i]);
9350 html[html.length] = Roo.util.Format.trim(
9352 t.applySubtemplate(this.dataName, data, this.store.meta) :
9359 el.update(html.join(""));
9360 this.nodes = el.dom.childNodes;
9361 this.updateIndexes(0);
9366 * Function to override to reformat the data that is sent to
9367 * the template for each node.
9368 * DEPRICATED - use the preparedata event handler.
9369 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9370 * a JSON object for an UpdateManager bound view).
9372 prepareData : function(data, index, record)
9374 this.fireEvent("preparedata", this, data, index, record);
9378 onUpdate : function(ds, record){
9379 Roo.log('on update');
9380 this.clearSelections();
9381 var index = this.store.indexOf(record);
9382 var n = this.nodes[index];
9383 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9384 n.parentNode.removeChild(n);
9385 this.updateIndexes(index, index);
9391 onAdd : function(ds, records, index)
9393 Roo.log(['on Add', ds, records, index] );
9394 this.clearSelections();
9395 if(this.nodes.length == 0){
9399 var n = this.nodes[index];
9400 for(var i = 0, len = records.length; i < len; i++){
9401 var d = this.prepareData(records[i].data, i, records[i]);
9403 this.tpl.insertBefore(n, d);
9406 this.tpl.append(this.el, d);
9409 this.updateIndexes(index);
9412 onRemove : function(ds, record, index){
9413 Roo.log('onRemove');
9414 this.clearSelections();
9415 var el = this.dataName ?
9416 this.el.child('.roo-tpl-' + this.dataName) :
9419 el.dom.removeChild(this.nodes[index]);
9420 this.updateIndexes(index);
9424 * Refresh an individual node.
9425 * @param {Number} index
9427 refreshNode : function(index){
9428 this.onUpdate(this.store, this.store.getAt(index));
9431 updateIndexes : function(startIndex, endIndex){
9432 var ns = this.nodes;
9433 startIndex = startIndex || 0;
9434 endIndex = endIndex || ns.length - 1;
9435 for(var i = startIndex; i <= endIndex; i++){
9436 ns[i].nodeIndex = i;
9441 * Changes the data store this view uses and refresh the view.
9442 * @param {Store} store
9444 setStore : function(store, initial){
9445 if(!initial && this.store){
9446 this.store.un("datachanged", this.refresh);
9447 this.store.un("add", this.onAdd);
9448 this.store.un("remove", this.onRemove);
9449 this.store.un("update", this.onUpdate);
9450 this.store.un("clear", this.refresh);
9451 this.store.un("beforeload", this.onBeforeLoad);
9452 this.store.un("load", this.onLoad);
9453 this.store.un("loadexception", this.onLoad);
9457 store.on("datachanged", this.refresh, this);
9458 store.on("add", this.onAdd, this);
9459 store.on("remove", this.onRemove, this);
9460 store.on("update", this.onUpdate, this);
9461 store.on("clear", this.refresh, this);
9462 store.on("beforeload", this.onBeforeLoad, this);
9463 store.on("load", this.onLoad, this);
9464 store.on("loadexception", this.onLoad, this);
9472 * onbeforeLoad - masks the loading area.
9475 onBeforeLoad : function(store,opts)
9477 Roo.log('onBeforeLoad');
9481 this.el.mask(this.mask ? this.mask : "Loading" );
9483 onLoad : function ()
9490 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9491 * @param {HTMLElement} node
9492 * @return {HTMLElement} The template node
9494 findItemFromChild : function(node){
9495 var el = this.dataName ?
9496 this.el.child('.roo-tpl-' + this.dataName,true) :
9499 if(!node || node.parentNode == el){
9502 var p = node.parentNode;
9503 while(p && p != el){
9504 if(p.parentNode == el){
9513 onClick : function(e){
9514 var item = this.findItemFromChild(e.getTarget());
9516 var index = this.indexOf(item);
9517 if(this.onItemClick(item, index, e) !== false){
9518 this.fireEvent("click", this, index, item, e);
9521 this.clearSelections();
9526 onContextMenu : function(e){
9527 var item = this.findItemFromChild(e.getTarget());
9529 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9534 onDblClick : function(e){
9535 var item = this.findItemFromChild(e.getTarget());
9537 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9541 onItemClick : function(item, index, e)
9543 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9546 if (this.toggleSelect) {
9547 var m = this.isSelected(item) ? 'unselect' : 'select';
9550 _t[m](item, true, false);
9553 if(this.multiSelect || this.singleSelect){
9554 if(this.multiSelect && e.shiftKey && this.lastSelection){
9555 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9557 this.select(item, this.multiSelect && e.ctrlKey);
9558 this.lastSelection = item;
9566 * Get the number of selected nodes.
9569 getSelectionCount : function(){
9570 return this.selections.length;
9574 * Get the currently selected nodes.
9575 * @return {Array} An array of HTMLElements
9577 getSelectedNodes : function(){
9578 return this.selections;
9582 * Get the indexes of the selected nodes.
9585 getSelectedIndexes : function(){
9586 var indexes = [], s = this.selections;
9587 for(var i = 0, len = s.length; i < len; i++){
9588 indexes.push(s[i].nodeIndex);
9594 * Clear all selections
9595 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9597 clearSelections : function(suppressEvent){
9598 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9599 this.cmp.elements = this.selections;
9600 this.cmp.removeClass(this.selectedClass);
9601 this.selections = [];
9603 this.fireEvent("selectionchange", this, this.selections);
9609 * Returns true if the passed node is selected
9610 * @param {HTMLElement/Number} node The node or node index
9613 isSelected : function(node){
9614 var s = this.selections;
9618 node = this.getNode(node);
9619 return s.indexOf(node) !== -1;
9624 * @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
9625 * @param {Boolean} keepExisting (optional) true to keep existing selections
9626 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9628 select : function(nodeInfo, keepExisting, suppressEvent){
9629 if(nodeInfo instanceof Array){
9631 this.clearSelections(true);
9633 for(var i = 0, len = nodeInfo.length; i < len; i++){
9634 this.select(nodeInfo[i], true, true);
9638 var node = this.getNode(nodeInfo);
9639 if(!node || this.isSelected(node)){
9640 return; // already selected.
9643 this.clearSelections(true);
9645 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9646 Roo.fly(node).addClass(this.selectedClass);
9647 this.selections.push(node);
9649 this.fireEvent("selectionchange", this, this.selections);
9657 * @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
9658 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9659 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9661 unselect : function(nodeInfo, keepExisting, suppressEvent)
9663 if(nodeInfo instanceof Array){
9664 Roo.each(this.selections, function(s) {
9665 this.unselect(s, nodeInfo);
9669 var node = this.getNode(nodeInfo);
9670 if(!node || !this.isSelected(node)){
9671 Roo.log("not selected");
9672 return; // not selected.
9676 Roo.each(this.selections, function(s) {
9678 Roo.fly(node).removeClass(this.selectedClass);
9685 this.selections= ns;
9686 this.fireEvent("selectionchange", this, this.selections);
9690 * Gets a template node.
9691 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9692 * @return {HTMLElement} The node or null if it wasn't found
9694 getNode : function(nodeInfo){
9695 if(typeof nodeInfo == "string"){
9696 return document.getElementById(nodeInfo);
9697 }else if(typeof nodeInfo == "number"){
9698 return this.nodes[nodeInfo];
9704 * Gets a range template nodes.
9705 * @param {Number} startIndex
9706 * @param {Number} endIndex
9707 * @return {Array} An array of nodes
9709 getNodes : function(start, end){
9710 var ns = this.nodes;
9712 end = typeof end == "undefined" ? ns.length - 1 : end;
9715 for(var i = start; i <= end; i++){
9719 for(var i = start; i >= end; i--){
9727 * Finds the index of the passed node
9728 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9729 * @return {Number} The index of the node or -1
9731 indexOf : function(node){
9732 node = this.getNode(node);
9733 if(typeof node.nodeIndex == "number"){
9734 return node.nodeIndex;
9736 var ns = this.nodes;
9737 for(var i = 0, len = ns.length; i < len; i++){
9748 * based on jquery fullcalendar
9752 Roo.bootstrap = Roo.bootstrap || {};
9754 * @class Roo.bootstrap.Calendar
9755 * @extends Roo.bootstrap.Component
9756 * Bootstrap Calendar class
9757 * @cfg {Boolean} loadMask (true|false) default false
9760 * Create a new Container
9761 * @param {Object} config The config object
9766 Roo.bootstrap.Calendar = function(config){
9767 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9771 * Fires when a date is selected
9772 * @param {DatePicker} this
9773 * @param {Date} date The selected date
9777 * @event monthchange
9778 * Fires when the displayed month changes
9779 * @param {DatePicker} this
9780 * @param {Date} date The selected month
9782 'monthchange': true,
9785 * Fires when mouse over an event
9786 * @param {Calendar} this
9787 * @param {event} Event
9792 * Fires when the mouse leaves an
9793 * @param {Calendar} this
9799 * Fires when the mouse click an
9800 * @param {Calendar} this
9809 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9812 * @cfg {Number} startDay
9813 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9819 getAutoCreate : function(){
9822 var fc_button = function(name, corner, style, content ) {
9823 return Roo.apply({},{
9825 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9827 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9830 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9838 style : 'width:100%',
9845 cls : 'fc-header-left',
9847 fc_button('prev', 'left', 'arrow', '‹' ),
9848 fc_button('next', 'right', 'arrow', '›' ),
9849 { tag: 'span', cls: 'fc-header-space' },
9850 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9858 cls : 'fc-header-center',
9862 cls: 'fc-header-title',
9865 html : 'month / year'
9873 cls : 'fc-header-right',
9875 /* fc_button('month', 'left', '', 'month' ),
9876 fc_button('week', '', '', 'week' ),
9877 fc_button('day', 'right', '', 'day' )
9889 var cal_heads = function() {
9891 // fixme - handle this.
9893 for (var i =0; i < Date.dayNames.length; i++) {
9894 var d = Date.dayNames[i];
9897 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9898 html : d.substring(0,3)
9902 ret[0].cls += ' fc-first';
9903 ret[6].cls += ' fc-last';
9906 var cal_cell = function(n) {
9909 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9914 cls: 'fc-day-number',
9918 cls: 'fc-day-content',
9922 style: 'position: relative;' // height: 17px;
9934 var cal_rows = function() {
9937 for (var r = 0; r < 6; r++) {
9944 for (var i =0; i < Date.dayNames.length; i++) {
9945 var d = Date.dayNames[i];
9946 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9949 row.cn[0].cls+=' fc-first';
9950 row.cn[0].cn[0].style = 'min-height:90px';
9951 row.cn[6].cls+=' fc-last';
9955 ret[0].cls += ' fc-first';
9956 ret[4].cls += ' fc-prev-last';
9957 ret[5].cls += ' fc-last';
9964 cls: 'fc-border-separate',
9965 style : 'width:100%',
9973 cls : 'fc-first fc-last',
9992 style : "position: relative;",
9995 cls : 'fc-view fc-view-month fc-grid',
9996 style : 'position: relative',
9997 unselectable : 'on',
10000 cls : 'fc-event-container',
10001 style : 'position:absolute;z-index:8;top:0;left:0;'
10019 initEvents : function()
10022 throw "can not find store for calendar";
10028 style: "text-align:center",
10032 style: "background-color:white;width:50%;margin:250 auto",
10036 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10047 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10049 var size = this.el.select('.fc-content', true).first().getSize();
10050 this.maskEl.setSize(size.width, size.height);
10051 this.maskEl.enableDisplayMode("block");
10052 if(!this.loadMask){
10053 this.maskEl.hide();
10056 this.store = Roo.factory(this.store, Roo.data);
10057 this.store.on('load', this.onLoad, this);
10058 this.store.on('beforeload', this.onBeforeLoad, this);
10062 this.cells = this.el.select('.fc-day',true);
10063 //Roo.log(this.cells);
10064 this.textNodes = this.el.query('.fc-day-number');
10065 this.cells.addClassOnOver('fc-state-hover');
10067 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10068 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10069 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10070 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10072 this.on('monthchange', this.onMonthChange, this);
10074 this.update(new Date().clearTime());
10077 resize : function() {
10078 var sz = this.el.getSize();
10080 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10081 this.el.select('.fc-day-content div',true).setHeight(34);
10086 showPrevMonth : function(e){
10087 this.update(this.activeDate.add("mo", -1));
10089 showToday : function(e){
10090 this.update(new Date().clearTime());
10093 showNextMonth : function(e){
10094 this.update(this.activeDate.add("mo", 1));
10098 showPrevYear : function(){
10099 this.update(this.activeDate.add("y", -1));
10103 showNextYear : function(){
10104 this.update(this.activeDate.add("y", 1));
10109 update : function(date)
10111 var vd = this.activeDate;
10112 this.activeDate = date;
10113 // if(vd && this.el){
10114 // var t = date.getTime();
10115 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10116 // Roo.log('using add remove');
10118 // this.fireEvent('monthchange', this, date);
10120 // this.cells.removeClass("fc-state-highlight");
10121 // this.cells.each(function(c){
10122 // if(c.dateValue == t){
10123 // c.addClass("fc-state-highlight");
10124 // setTimeout(function(){
10125 // try{c.dom.firstChild.focus();}catch(e){}
10135 var days = date.getDaysInMonth();
10137 var firstOfMonth = date.getFirstDateOfMonth();
10138 var startingPos = firstOfMonth.getDay()-this.startDay;
10140 if(startingPos < this.startDay){
10144 var pm = date.add(Date.MONTH, -1);
10145 var prevStart = pm.getDaysInMonth()-startingPos;
10147 this.cells = this.el.select('.fc-day',true);
10148 this.textNodes = this.el.query('.fc-day-number');
10149 this.cells.addClassOnOver('fc-state-hover');
10151 var cells = this.cells.elements;
10152 var textEls = this.textNodes;
10154 Roo.each(cells, function(cell){
10155 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10158 days += startingPos;
10160 // convert everything to numbers so it's fast
10161 var day = 86400000;
10162 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10165 //Roo.log(prevStart);
10167 var today = new Date().clearTime().getTime();
10168 var sel = date.clearTime().getTime();
10169 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10170 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10171 var ddMatch = this.disabledDatesRE;
10172 var ddText = this.disabledDatesText;
10173 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10174 var ddaysText = this.disabledDaysText;
10175 var format = this.format;
10177 var setCellClass = function(cal, cell){
10179 //Roo.log('set Cell Class');
10181 var t = d.getTime();
10185 cell.dateValue = t;
10187 cell.className += " fc-today";
10188 cell.className += " fc-state-highlight";
10189 cell.title = cal.todayText;
10192 // disable highlight in other month..
10193 //cell.className += " fc-state-highlight";
10198 cell.className = " fc-state-disabled";
10199 cell.title = cal.minText;
10203 cell.className = " fc-state-disabled";
10204 cell.title = cal.maxText;
10208 if(ddays.indexOf(d.getDay()) != -1){
10209 cell.title = ddaysText;
10210 cell.className = " fc-state-disabled";
10213 if(ddMatch && format){
10214 var fvalue = d.dateFormat(format);
10215 if(ddMatch.test(fvalue)){
10216 cell.title = ddText.replace("%0", fvalue);
10217 cell.className = " fc-state-disabled";
10221 if (!cell.initialClassName) {
10222 cell.initialClassName = cell.dom.className;
10225 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10230 for(; i < startingPos; i++) {
10231 textEls[i].innerHTML = (++prevStart);
10232 d.setDate(d.getDate()+1);
10234 cells[i].className = "fc-past fc-other-month";
10235 setCellClass(this, cells[i]);
10240 for(; i < days; i++){
10241 intDay = i - startingPos + 1;
10242 textEls[i].innerHTML = (intDay);
10243 d.setDate(d.getDate()+1);
10245 cells[i].className = ''; // "x-date-active";
10246 setCellClass(this, cells[i]);
10250 for(; i < 42; i++) {
10251 textEls[i].innerHTML = (++extraDays);
10252 d.setDate(d.getDate()+1);
10254 cells[i].className = "fc-future fc-other-month";
10255 setCellClass(this, cells[i]);
10258 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10260 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10262 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10263 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10265 if(totalRows != 6){
10266 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10267 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10270 this.fireEvent('monthchange', this, date);
10274 if(!this.internalRender){
10275 var main = this.el.dom.firstChild;
10276 var w = main.offsetWidth;
10277 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10278 Roo.fly(main).setWidth(w);
10279 this.internalRender = true;
10280 // opera does not respect the auto grow header center column
10281 // then, after it gets a width opera refuses to recalculate
10282 // without a second pass
10283 if(Roo.isOpera && !this.secondPass){
10284 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10285 this.secondPass = true;
10286 this.update.defer(10, this, [date]);
10293 findCell : function(dt) {
10294 dt = dt.clearTime().getTime();
10296 this.cells.each(function(c){
10297 //Roo.log("check " +c.dateValue + '?=' + dt);
10298 if(c.dateValue == dt){
10308 findCells : function(ev) {
10309 var s = ev.start.clone().clearTime().getTime();
10311 var e= ev.end.clone().clearTime().getTime();
10314 this.cells.each(function(c){
10315 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10317 if(c.dateValue > e){
10320 if(c.dateValue < s){
10329 findBestRow: function(cells)
10333 for (var i =0 ; i < cells.length;i++) {
10334 ret = Math.max(cells[i].rows || 0,ret);
10341 addItem : function(ev)
10343 // look for vertical location slot in
10344 var cells = this.findCells(ev);
10346 ev.row = this.findBestRow(cells);
10348 // work out the location.
10352 for(var i =0; i < cells.length; i++) {
10360 if (crow.start.getY() == cells[i].getY()) {
10362 crow.end = cells[i];
10378 for (var i = 0; i < cells.length;i++) {
10379 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10383 this.calevents.push(ev);
10386 clearEvents: function() {
10388 if(!this.calevents){
10392 Roo.each(this.cells.elements, function(c){
10396 Roo.each(this.calevents, function(e) {
10397 Roo.each(e.els, function(el) {
10398 el.un('mouseenter' ,this.onEventEnter, this);
10399 el.un('mouseleave' ,this.onEventLeave, this);
10406 renderEvents: function()
10408 // first make sure there is enough space..
10410 this.cells.each(function(c) {
10412 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10415 for (var e = 0; e < this.calevents.length; e++) {
10416 var ev = this.calevents[e];
10417 var cells = ev.cells;
10418 var rows = ev.rows;
10420 for(var i =0; i < rows.length; i++) {
10423 // how many rows should it span..
10426 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10427 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10429 unselectable : "on",
10432 cls: 'fc-event-inner',
10436 // cls: 'fc-event-time',
10437 // html : cells.length > 1 ? '' : ev.time
10441 cls: 'fc-event-title',
10442 html : String.format('{0}', ev.title)
10449 cls: 'ui-resizable-handle ui-resizable-e',
10450 html : '  '
10456 cfg.cls += ' fc-event-start';
10458 if ((i+1) == rows.length) {
10459 cfg.cls += ' fc-event-end';
10462 var ctr = this.el.select('.fc-event-container',true).first();
10463 var cg = ctr.createChild(cfg);
10465 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10466 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10467 cg.on('click', this.onEventClick, this, ev);
10471 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10472 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10474 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10475 cg.setWidth(ebox.right - sbox.x -2);
10483 onEventEnter: function (e, el,event,d) {
10484 this.fireEvent('evententer', this, el, event);
10487 onEventLeave: function (e, el,event,d) {
10488 this.fireEvent('eventleave', this, el, event);
10491 onEventClick: function (e, el,event,d) {
10492 this.fireEvent('eventclick', this, el, event);
10495 onMonthChange: function () {
10499 onLoad: function ()
10501 this.calevents = [];
10504 if(this.store.getCount() > 0){
10505 this.store.data.each(function(d){
10508 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10509 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10510 time : d.data.start_time,
10511 title : d.data.title,
10512 description : d.data.description,
10513 venue : d.data.venue
10518 this.renderEvents();
10521 this.maskEl.hide();
10525 onBeforeLoad: function()
10527 this.clearEvents();
10530 this.maskEl.show();
10544 * @class Roo.bootstrap.Popover
10545 * @extends Roo.bootstrap.Component
10546 * Bootstrap Popover class
10547 * @cfg {String} html contents of the popover (or false to use children..)
10548 * @cfg {String} title of popover (or false to hide)
10549 * @cfg {String} placement how it is placed
10550 * @cfg {String} trigger click || hover (or false to trigger manually)
10551 * @cfg {String} over what (parent or false to trigger manually.)
10554 * Create a new Popover
10555 * @param {Object} config The config object
10558 Roo.bootstrap.Popover = function(config){
10559 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10562 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10564 title: 'Fill in a title',
10567 placement : 'right',
10568 trigger : 'hover', // hover
10572 can_build_overlaid : false,
10574 getChildContainer : function()
10576 return this.el.select('.popover-content',true).first();
10579 getAutoCreate : function(){
10580 Roo.log('make popover?');
10582 cls : 'popover roo-dynamic',
10583 style: 'display:block',
10589 cls : 'popover-inner',
10593 cls: 'popover-title',
10597 cls : 'popover-content',
10608 setTitle: function(str)
10610 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10612 setContent: function(str)
10614 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10616 // as it get's added to the bottom of the page.
10617 onRender : function(ct, position)
10619 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10621 var cfg = Roo.apply({}, this.getAutoCreate());
10625 cfg.cls += ' ' + this.cls;
10628 cfg.style = this.style;
10630 Roo.log("adding to ")
10631 this.el = Roo.get(document.body).createChild(cfg, position);
10637 initEvents : function()
10639 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10640 this.el.enableDisplayMode('block');
10642 if (this.over === false) {
10645 if (this.triggers === false) {
10648 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10649 var triggers = this.trigger ? this.trigger.split(' ') : [];
10650 Roo.each(triggers, function(trigger) {
10652 if (trigger == 'click') {
10653 on_el.on('click', this.toggle, this);
10654 } else if (trigger != 'manual') {
10655 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10656 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10658 on_el.on(eventIn ,this.enter, this);
10659 on_el.on(eventOut, this.leave, this);
10670 toggle : function () {
10671 this.hoverState == 'in' ? this.leave() : this.enter();
10674 enter : function () {
10677 clearTimeout(this.timeout);
10679 this.hoverState = 'in'
10681 if (!this.delay || !this.delay.show) {
10686 this.timeout = setTimeout(function () {
10687 if (_t.hoverState == 'in') {
10690 }, this.delay.show)
10692 leave : function() {
10693 clearTimeout(this.timeout);
10695 this.hoverState = 'out'
10697 if (!this.delay || !this.delay.hide) {
10702 this.timeout = setTimeout(function () {
10703 if (_t.hoverState == 'out') {
10706 }, this.delay.hide)
10709 show : function (on_el)
10712 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10715 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10716 if (this.html !== false) {
10717 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10719 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10720 if (!this.title.length) {
10721 this.el.select('.popover-title',true).hide();
10724 var placement = typeof this.placement == 'function' ?
10725 this.placement.call(this, this.el, on_el) :
10728 var autoToken = /\s?auto?\s?/i;
10729 var autoPlace = autoToken.test(placement);
10731 placement = placement.replace(autoToken, '') || 'top';
10735 //this.el.setXY([0,0]);
10737 this.el.dom.style.display='block';
10738 this.el.addClass(placement);
10740 //this.el.appendTo(on_el);
10742 var p = this.getPosition();
10743 var box = this.el.getBox();
10748 var align = Roo.bootstrap.Popover.alignment[placement]
10749 this.el.alignTo(on_el, align[0],align[1]);
10750 //var arrow = this.el.select('.arrow',true).first();
10751 //arrow.set(align[2],
10753 this.el.addClass('in');
10754 this.hoverState = null;
10756 if (this.el.hasClass('fade')) {
10763 this.el.setXY([0,0]);
10764 this.el.removeClass('in');
10771 Roo.bootstrap.Popover.alignment = {
10772 'left' : ['r-l', [-10,0], 'right'],
10773 'right' : ['l-r', [10,0], 'left'],
10774 'bottom' : ['t-b', [0,10], 'top'],
10775 'top' : [ 'b-t', [0,-10], 'bottom']
10786 * @class Roo.bootstrap.Progress
10787 * @extends Roo.bootstrap.Component
10788 * Bootstrap Progress class
10789 * @cfg {Boolean} striped striped of the progress bar
10790 * @cfg {Boolean} active animated of the progress bar
10794 * Create a new Progress
10795 * @param {Object} config The config object
10798 Roo.bootstrap.Progress = function(config){
10799 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10802 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10807 getAutoCreate : function(){
10815 cfg.cls += ' progress-striped';
10819 cfg.cls += ' active';
10838 * @class Roo.bootstrap.ProgressBar
10839 * @extends Roo.bootstrap.Component
10840 * Bootstrap ProgressBar class
10841 * @cfg {Number} aria_valuenow aria-value now
10842 * @cfg {Number} aria_valuemin aria-value min
10843 * @cfg {Number} aria_valuemax aria-value max
10844 * @cfg {String} label label for the progress bar
10845 * @cfg {String} panel (success | info | warning | danger )
10846 * @cfg {String} role role of the progress bar
10847 * @cfg {String} sr_only text
10851 * Create a new ProgressBar
10852 * @param {Object} config The config object
10855 Roo.bootstrap.ProgressBar = function(config){
10856 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10859 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10863 aria_valuemax : 100,
10869 getAutoCreate : function()
10874 cls: 'progress-bar',
10875 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10887 cfg.role = this.role;
10890 if(this.aria_valuenow){
10891 cfg['aria-valuenow'] = this.aria_valuenow;
10894 if(this.aria_valuemin){
10895 cfg['aria-valuemin'] = this.aria_valuemin;
10898 if(this.aria_valuemax){
10899 cfg['aria-valuemax'] = this.aria_valuemax;
10902 if(this.label && !this.sr_only){
10903 cfg.html = this.label;
10907 cfg.cls += ' progress-bar-' + this.panel;
10913 update : function(aria_valuenow)
10915 this.aria_valuenow = aria_valuenow;
10917 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10932 * @class Roo.bootstrap.TabPanel
10933 * @extends Roo.bootstrap.Component
10934 * Bootstrap TabPanel class
10935 * @cfg {Boolean} active panel active
10936 * @cfg {String} html panel content
10937 * @cfg {String} tabId tab relate id
10941 * Create a new TabPanel
10942 * @param {Object} config The config object
10945 Roo.bootstrap.TabPanel = function(config){
10946 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10949 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10955 getAutoCreate : function(){
10959 html: this.html || ''
10963 cfg.cls += ' active';
10967 cfg.tabId = this.tabId;
10985 * @class Roo.bootstrap.DateField
10986 * @extends Roo.bootstrap.Input
10987 * Bootstrap DateField class
10988 * @cfg {Number} weekStart default 0
10989 * @cfg {Number} weekStart default 0
10990 * @cfg {Number} viewMode default empty, (months|years)
10991 * @cfg {Number} minViewMode default empty, (months|years)
10992 * @cfg {Number} startDate default -Infinity
10993 * @cfg {Number} endDate default Infinity
10994 * @cfg {Boolean} todayHighlight default false
10995 * @cfg {Boolean} todayBtn default false
10996 * @cfg {Boolean} calendarWeeks default false
10997 * @cfg {Object} daysOfWeekDisabled default empty
10999 * @cfg {Boolean} keyboardNavigation default true
11000 * @cfg {String} language default en
11003 * Create a new DateField
11004 * @param {Object} config The config object
11007 Roo.bootstrap.DateField = function(config){
11008 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11012 * Fires when this field show.
11013 * @param {Roo.bootstrap.DateField} this
11014 * @param {Mixed} date The date value
11019 * Fires when this field hide.
11020 * @param {Roo.bootstrap.DateField} this
11021 * @param {Mixed} date The date value
11026 * Fires when select a date.
11027 * @param {Roo.bootstrap.DateField} this
11028 * @param {Mixed} date The date value
11034 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
11037 * @cfg {String} format
11038 * The default date format string which can be overriden for localization support. The format must be
11039 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11043 * @cfg {String} altFormats
11044 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11045 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11047 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11055 todayHighlight : false,
11061 keyboardNavigation: true,
11063 calendarWeeks: false,
11065 startDate: -Infinity,
11069 daysOfWeekDisabled: [],
11073 UTCDate: function()
11075 return new Date(Date.UTC.apply(Date, arguments));
11078 UTCToday: function()
11080 var today = new Date();
11081 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11084 getDate: function() {
11085 var d = this.getUTCDate();
11086 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11089 getUTCDate: function() {
11093 setDate: function(d) {
11094 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11097 setUTCDate: function(d) {
11099 this.setValue(this.formatDate(this.date));
11102 onRender: function(ct, position)
11105 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11107 this.language = this.language || 'en';
11108 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11109 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11111 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11112 this.format = this.format || 'm/d/y';
11113 this.isInline = false;
11114 this.isInput = true;
11115 this.component = this.el.select('.add-on', true).first() || false;
11116 this.component = (this.component && this.component.length === 0) ? false : this.component;
11117 this.hasInput = this.component && this.inputEL().length;
11119 if (typeof(this.minViewMode === 'string')) {
11120 switch (this.minViewMode) {
11122 this.minViewMode = 1;
11125 this.minViewMode = 2;
11128 this.minViewMode = 0;
11133 if (typeof(this.viewMode === 'string')) {
11134 switch (this.viewMode) {
11147 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11149 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11151 this.picker().on('mousedown', this.onMousedown, this);
11152 this.picker().on('click', this.onClick, this);
11154 this.picker().addClass('datepicker-dropdown');
11156 this.startViewMode = this.viewMode;
11159 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11160 if(!this.calendarWeeks){
11165 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11166 v.attr('colspan', function(i, val){
11167 return parseInt(val) + 1;
11172 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11174 this.setStartDate(this.startDate);
11175 this.setEndDate(this.endDate);
11177 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11184 if(this.isInline) {
11189 picker : function()
11191 return this.el.select('.datepicker', true).first();
11194 fillDow: function()
11196 var dowCnt = this.weekStart;
11205 if(this.calendarWeeks){
11213 while (dowCnt < this.weekStart + 7) {
11217 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11221 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11224 fillMonths: function()
11227 var months = this.picker().select('>.datepicker-months td', true).first();
11229 months.dom.innerHTML = '';
11235 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11238 months.createChild(month);
11243 update: function(){
11245 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11247 if (this.date < this.startDate) {
11248 this.viewDate = new Date(this.startDate);
11249 } else if (this.date > this.endDate) {
11250 this.viewDate = new Date(this.endDate);
11252 this.viewDate = new Date(this.date);
11259 var d = new Date(this.viewDate),
11260 year = d.getUTCFullYear(),
11261 month = d.getUTCMonth(),
11262 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11263 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11264 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11265 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11266 currentDate = this.date && this.date.valueOf(),
11267 today = this.UTCToday();
11269 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11271 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11273 // this.picker.select('>tfoot th.today').
11274 // .text(dates[this.language].today)
11275 // .toggle(this.todayBtn !== false);
11277 this.updateNavArrows();
11280 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11282 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11284 prevMonth.setUTCDate(day);
11286 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11288 var nextMonth = new Date(prevMonth);
11290 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11292 nextMonth = nextMonth.valueOf();
11294 var fillMonths = false;
11296 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11298 while(prevMonth.valueOf() < nextMonth) {
11301 if (prevMonth.getUTCDay() === this.weekStart) {
11303 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11311 if(this.calendarWeeks){
11312 // ISO 8601: First week contains first thursday.
11313 // ISO also states week starts on Monday, but we can be more abstract here.
11315 // Start of current week: based on weekstart/current date
11316 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11317 // Thursday of this week
11318 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11319 // First Thursday of year, year from thursday
11320 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11321 // Calendar week: ms between thursdays, div ms per day, div 7 days
11322 calWeek = (th - yth) / 864e5 / 7 + 1;
11324 fillMonths.cn.push({
11332 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11334 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11337 if (this.todayHighlight &&
11338 prevMonth.getUTCFullYear() == today.getFullYear() &&
11339 prevMonth.getUTCMonth() == today.getMonth() &&
11340 prevMonth.getUTCDate() == today.getDate()) {
11341 clsName += ' today';
11344 if (currentDate && prevMonth.valueOf() === currentDate) {
11345 clsName += ' active';
11348 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11349 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11350 clsName += ' disabled';
11353 fillMonths.cn.push({
11355 cls: 'day ' + clsName,
11356 html: prevMonth.getDate()
11359 prevMonth.setDate(prevMonth.getDate()+1);
11362 var currentYear = this.date && this.date.getUTCFullYear();
11363 var currentMonth = this.date && this.date.getUTCMonth();
11365 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11367 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11368 v.removeClass('active');
11370 if(currentYear === year && k === currentMonth){
11371 v.addClass('active');
11374 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11375 v.addClass('disabled');
11381 year = parseInt(year/10, 10) * 10;
11383 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11385 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11388 for (var i = -1; i < 11; i++) {
11389 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11391 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11399 showMode: function(dir) {
11401 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11403 Roo.each(this.picker().select('>div',true).elements, function(v){
11404 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11407 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11412 if(this.isInline) return;
11414 this.picker().removeClass(['bottom', 'top']);
11416 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11418 * place to the top of element!
11422 this.picker().addClass('top');
11423 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11428 this.picker().addClass('bottom');
11430 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11433 parseDate : function(value){
11434 if(!value || value instanceof Date){
11437 var v = Date.parseDate(value, this.format);
11438 if (!v && this.useIso) {
11439 v = Date.parseDate(value, 'Y-m-d');
11441 if(!v && this.altFormats){
11442 if(!this.altFormatsArray){
11443 this.altFormatsArray = this.altFormats.split("|");
11445 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11446 v = Date.parseDate(value, this.altFormatsArray[i]);
11452 formatDate : function(date, fmt){
11453 return (!date || !(date instanceof Date)) ?
11454 date : date.dateFormat(fmt || this.format);
11457 onFocus : function()
11459 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11463 onBlur : function()
11465 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11471 this.picker().show();
11475 this.fireEvent('show', this, this.date);
11480 if(this.isInline) return;
11481 this.picker().hide();
11482 this.viewMode = this.startViewMode;
11485 this.fireEvent('hide', this, this.date);
11489 onMousedown: function(e){
11490 e.stopPropagation();
11491 e.preventDefault();
11494 keyup: function(e){
11495 Roo.bootstrap.DateField.superclass.keyup.call(this);
11500 setValue: function(v){
11501 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11503 this.fireEvent('select', this, this.date);
11507 fireKey: function(e){
11508 if (!this.picker().isVisible()){
11509 if (e.keyCode == 27) // allow escape to hide and re-show picker
11513 var dateChanged = false,
11515 newDate, newViewDate;
11519 e.preventDefault();
11523 if (!this.keyboardNavigation) break;
11524 dir = e.keyCode == 37 ? -1 : 1;
11527 newDate = this.moveYear(this.date, dir);
11528 newViewDate = this.moveYear(this.viewDate, dir);
11529 } else if (e.shiftKey){
11530 newDate = this.moveMonth(this.date, dir);
11531 newViewDate = this.moveMonth(this.viewDate, dir);
11533 newDate = new Date(this.date);
11534 newDate.setUTCDate(this.date.getUTCDate() + dir);
11535 newViewDate = new Date(this.viewDate);
11536 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11538 if (this.dateWithinRange(newDate)){
11539 this.date = newDate;
11540 this.viewDate = newViewDate;
11541 this.setValue(this.formatDate(this.date));
11543 e.preventDefault();
11544 dateChanged = true;
11549 if (!this.keyboardNavigation) break;
11550 dir = e.keyCode == 38 ? -1 : 1;
11552 newDate = this.moveYear(this.date, dir);
11553 newViewDate = this.moveYear(this.viewDate, dir);
11554 } else if (e.shiftKey){
11555 newDate = this.moveMonth(this.date, dir);
11556 newViewDate = this.moveMonth(this.viewDate, dir);
11558 newDate = new Date(this.date);
11559 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11560 newViewDate = new Date(this.viewDate);
11561 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11563 if (this.dateWithinRange(newDate)){
11564 this.date = newDate;
11565 this.viewDate = newViewDate;
11566 this.setValue(this.formatDate(this.date));
11568 e.preventDefault();
11569 dateChanged = true;
11573 this.setValue(this.formatDate(this.date));
11575 e.preventDefault();
11578 this.setValue(this.formatDate(this.date));
11585 onClick: function(e) {
11586 e.stopPropagation();
11587 e.preventDefault();
11589 var target = e.getTarget();
11591 if(target.nodeName.toLowerCase() === 'i'){
11592 target = Roo.get(target).dom.parentNode;
11595 var nodeName = target.nodeName;
11596 var className = target.className;
11597 var html = target.innerHTML;
11599 switch(nodeName.toLowerCase()) {
11601 switch(className) {
11607 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11608 switch(this.viewMode){
11610 this.viewDate = this.moveMonth(this.viewDate, dir);
11614 this.viewDate = this.moveYear(this.viewDate, dir);
11620 var date = new Date();
11621 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11623 this.setValue(this.formatDate(this.date));
11629 if (className.indexOf('disabled') === -1) {
11630 this.viewDate.setUTCDate(1);
11631 if (className.indexOf('month') !== -1) {
11632 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11634 var year = parseInt(html, 10) || 0;
11635 this.viewDate.setUTCFullYear(year);
11644 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11645 var day = parseInt(html, 10) || 1;
11646 var year = this.viewDate.getUTCFullYear(),
11647 month = this.viewDate.getUTCMonth();
11649 if (className.indexOf('old') !== -1) {
11656 } else if (className.indexOf('new') !== -1) {
11664 this.date = this.UTCDate(year, month, day,0,0,0,0);
11665 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11667 this.setValue(this.formatDate(this.date));
11674 setStartDate: function(startDate){
11675 this.startDate = startDate || -Infinity;
11676 if (this.startDate !== -Infinity) {
11677 this.startDate = this.parseDate(this.startDate);
11680 this.updateNavArrows();
11683 setEndDate: function(endDate){
11684 this.endDate = endDate || Infinity;
11685 if (this.endDate !== Infinity) {
11686 this.endDate = this.parseDate(this.endDate);
11689 this.updateNavArrows();
11692 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11693 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11694 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11695 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11697 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11698 return parseInt(d, 10);
11701 this.updateNavArrows();
11704 updateNavArrows: function() {
11705 var d = new Date(this.viewDate),
11706 year = d.getUTCFullYear(),
11707 month = d.getUTCMonth();
11709 Roo.each(this.picker().select('.prev', true).elements, function(v){
11711 switch (this.viewMode) {
11714 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11720 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11727 Roo.each(this.picker().select('.next', true).elements, function(v){
11729 switch (this.viewMode) {
11732 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11738 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11746 moveMonth: function(date, dir){
11747 if (!dir) return date;
11748 var new_date = new Date(date.valueOf()),
11749 day = new_date.getUTCDate(),
11750 month = new_date.getUTCMonth(),
11751 mag = Math.abs(dir),
11753 dir = dir > 0 ? 1 : -1;
11756 // If going back one month, make sure month is not current month
11757 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11759 return new_date.getUTCMonth() == month;
11761 // If going forward one month, make sure month is as expected
11762 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11764 return new_date.getUTCMonth() != new_month;
11766 new_month = month + dir;
11767 new_date.setUTCMonth(new_month);
11768 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11769 if (new_month < 0 || new_month > 11)
11770 new_month = (new_month + 12) % 12;
11772 // For magnitudes >1, move one month at a time...
11773 for (var i=0; i<mag; i++)
11774 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11775 new_date = this.moveMonth(new_date, dir);
11776 // ...then reset the day, keeping it in the new month
11777 new_month = new_date.getUTCMonth();
11778 new_date.setUTCDate(day);
11780 return new_month != new_date.getUTCMonth();
11783 // Common date-resetting loop -- if date is beyond end of month, make it
11786 new_date.setUTCDate(--day);
11787 new_date.setUTCMonth(new_month);
11792 moveYear: function(date, dir){
11793 return this.moveMonth(date, dir*12);
11796 dateWithinRange: function(date){
11797 return date >= this.startDate && date <= this.endDate;
11801 remove: function() {
11802 this.picker().remove();
11807 Roo.apply(Roo.bootstrap.DateField, {
11818 html: '<i class="icon-arrow-left"/>'
11828 html: '<i class="icon-arrow-right"/>'
11870 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11871 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11872 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11873 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11874 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11887 navFnc: 'FullYear',
11892 navFnc: 'FullYear',
11897 Roo.apply(Roo.bootstrap.DateField, {
11901 cls: 'datepicker dropdown-menu',
11905 cls: 'datepicker-days',
11909 cls: 'table-condensed',
11911 Roo.bootstrap.DateField.head,
11915 Roo.bootstrap.DateField.footer
11922 cls: 'datepicker-months',
11926 cls: 'table-condensed',
11928 Roo.bootstrap.DateField.head,
11929 Roo.bootstrap.DateField.content,
11930 Roo.bootstrap.DateField.footer
11937 cls: 'datepicker-years',
11941 cls: 'table-condensed',
11943 Roo.bootstrap.DateField.head,
11944 Roo.bootstrap.DateField.content,
11945 Roo.bootstrap.DateField.footer
11964 * @class Roo.bootstrap.TimeField
11965 * @extends Roo.bootstrap.Input
11966 * Bootstrap DateField class
11970 * Create a new TimeField
11971 * @param {Object} config The config object
11974 Roo.bootstrap.TimeField = function(config){
11975 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11979 * Fires when this field show.
11980 * @param {Roo.bootstrap.DateField} this
11981 * @param {Mixed} date The date value
11986 * Fires when this field hide.
11987 * @param {Roo.bootstrap.DateField} this
11988 * @param {Mixed} date The date value
11993 * Fires when select a date.
11994 * @param {Roo.bootstrap.DateField} this
11995 * @param {Mixed} date The date value
12001 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
12004 * @cfg {String} format
12005 * The default time format string which can be overriden for localization support. The format must be
12006 * valid according to {@link Date#parseDate} (defaults to 'H:i').
12010 onRender: function(ct, position)
12013 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12015 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12017 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12019 this.pop = this.picker().select('>.datepicker-time',true).first();
12020 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
12022 this.picker().on('mousedown', this.onMousedown, this);
12023 this.picker().on('click', this.onClick, this);
12025 this.picker().addClass('datepicker-dropdown');
12030 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12031 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12032 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12033 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12034 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12035 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12039 fireKey: function(e){
12040 if (!this.picker().isVisible()){
12041 if (e.keyCode == 27) // allow escape to hide and re-show picker
12046 e.preventDefault();
12054 this.onTogglePeriod();
12057 this.onIncrementMinutes();
12060 this.onDecrementMinutes();
12069 onClick: function(e) {
12070 e.stopPropagation();
12071 e.preventDefault();
12074 picker : function()
12076 return this.el.select('.datepicker', true).first();
12079 fillTime: function()
12081 var time = this.pop.select('tbody', true).first();
12083 time.dom.innerHTML = '';
12098 cls: 'hours-up glyphicon glyphicon-chevron-up'
12118 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12139 cls: 'timepicker-hour',
12154 cls: 'timepicker-minute',
12169 cls: 'btn btn-primary period',
12191 cls: 'hours-down glyphicon glyphicon-chevron-down'
12211 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12229 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12236 var hours = this.time.getHours();
12237 var minutes = this.time.getMinutes();
12250 hours = hours - 12;
12254 hours = '0' + hours;
12258 minutes = '0' + minutes;
12261 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12262 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12263 this.pop.select('button', true).first().dom.innerHTML = period;
12269 this.picker().removeClass(['bottom', 'top']);
12271 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12273 * place to the top of element!
12277 this.picker().addClass('top');
12278 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12283 this.picker().addClass('bottom');
12285 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12288 onFocus : function()
12290 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12294 onBlur : function()
12296 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12302 this.picker().show();
12307 this.fireEvent('show', this, this.date);
12312 this.picker().hide();
12315 this.fireEvent('hide', this, this.date);
12318 setTime : function()
12321 this.setValue(this.time.format(this.format));
12323 this.fireEvent('select', this, this.date);
12328 onMousedown: function(e){
12329 e.stopPropagation();
12330 e.preventDefault();
12333 onIncrementHours: function()
12335 Roo.log('onIncrementHours');
12336 this.time = this.time.add(Date.HOUR, 1);
12341 onDecrementHours: function()
12343 Roo.log('onDecrementHours');
12344 this.time = this.time.add(Date.HOUR, -1);
12348 onIncrementMinutes: function()
12350 Roo.log('onIncrementMinutes');
12351 this.time = this.time.add(Date.MINUTE, 1);
12355 onDecrementMinutes: function()
12357 Roo.log('onDecrementMinutes');
12358 this.time = this.time.add(Date.MINUTE, -1);
12362 onTogglePeriod: function()
12364 Roo.log('onTogglePeriod');
12365 this.time = this.time.add(Date.HOUR, 12);
12372 Roo.apply(Roo.bootstrap.TimeField, {
12402 cls: 'btn btn-info ok',
12414 Roo.apply(Roo.bootstrap.TimeField, {
12418 cls: 'datepicker dropdown-menu',
12422 cls: 'datepicker-time',
12426 cls: 'table-condensed',
12428 Roo.bootstrap.TimeField.content,
12429 Roo.bootstrap.TimeField.footer
12448 * @class Roo.bootstrap.CheckBox
12449 * @extends Roo.bootstrap.Input
12450 * Bootstrap CheckBox class
12452 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12453 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12454 * @cfg {String} boxLabel The text that appears beside the checkbox
12455 * @cfg {Boolean} checked initnal the element
12458 * Create a new CheckBox
12459 * @param {Object} config The config object
12462 Roo.bootstrap.CheckBox = function(config){
12463 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12468 * Fires when the element is checked or unchecked.
12469 * @param {Roo.bootstrap.CheckBox} this This input
12470 * @param {Boolean} checked The new checked value
12476 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12478 inputType: 'checkbox',
12484 getAutoCreate : function()
12486 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12492 cfg.cls = 'form-group' //input-group
12497 type : this.inputType,
12498 value : (!this.checked) ? this.valueOff : this.inputValue,
12500 placeholder : this.placeholder || ''
12504 if (this.disabled) {
12505 input.disabled=true;
12509 input.checked = this.checked;
12513 input.name = this.name;
12517 input.cls += ' input-' + this.size;
12521 ['xs','sm','md','lg'].map(function(size){
12522 if (settings[size]) {
12523 cfg.cls += ' col-' + size + '-' + settings[size];
12527 var inputblock = input;
12529 if (this.before || this.after) {
12532 cls : 'input-group',
12536 inputblock.cn.push({
12538 cls : 'input-group-addon',
12542 inputblock.cn.push(input);
12544 inputblock.cn.push({
12546 cls : 'input-group-addon',
12553 if (align ==='left' && this.fieldLabel.length) {
12554 Roo.log("left and has label");
12560 cls : 'control-label col-md-' + this.labelWidth,
12561 html : this.fieldLabel
12565 cls : "col-md-" + (12 - this.labelWidth),
12572 } else if ( this.fieldLabel.length) {
12577 tag: this.boxLabel ? 'span' : 'label',
12579 cls: 'control-label box-input-label',
12580 //cls : 'input-group-addon',
12581 html : this.fieldLabel
12591 Roo.log(" no label && no align");
12606 html: this.boxLabel
12615 * return the real input element.
12617 inputEl: function ()
12619 return this.el.select('input.form-box',true).first();
12622 initEvents : function()
12624 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12626 this.inputEl().on('click', this.onClick, this);
12630 onClick : function()
12632 this.setChecked(!this.checked);
12635 setChecked : function(state,suppressEvent)
12637 this.checked = state;
12639 this.inputEl().dom.checked = state;
12641 if(suppressEvent !== true){
12642 this.fireEvent('check', this, state);
12645 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12649 setValue : function(v,suppressEvent)
12651 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12665 * @class Roo.bootstrap.Radio
12666 * @extends Roo.bootstrap.CheckBox
12667 * Bootstrap Radio class
12670 * Create a new Radio
12671 * @param {Object} config The config object
12674 Roo.bootstrap.Radio = function(config){
12675 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12679 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12681 inputType: 'radio',
12685 getAutoCreate : function()
12687 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12693 cfg.cls = 'form-group' //input-group
12698 type : this.inputType,
12699 value : (!this.checked) ? this.valueOff : this.inputValue,
12701 placeholder : this.placeholder || ''
12705 if (this.disabled) {
12706 input.disabled=true;
12710 input.checked = this.checked;
12714 input.name = this.name;
12718 input.cls += ' input-' + this.size;
12722 ['xs','sm','md','lg'].map(function(size){
12723 if (settings[size]) {
12724 cfg.cls += ' col-' + size + '-' + settings[size];
12728 var inputblock = input;
12730 if (this.before || this.after) {
12733 cls : 'input-group',
12737 inputblock.cn.push({
12739 cls : 'input-group-addon',
12743 inputblock.cn.push(input);
12745 inputblock.cn.push({
12747 cls : 'input-group-addon',
12754 if (align ==='left' && this.fieldLabel.length) {
12755 Roo.log("left and has label");
12761 cls : 'control-label col-md-' + this.labelWidth,
12762 html : this.fieldLabel
12766 cls : "col-md-" + (12 - this.labelWidth),
12773 } else if ( this.fieldLabel.length) {
12780 cls: 'control-label box-input-label',
12781 //cls : 'input-group-addon',
12782 html : this.fieldLabel
12792 Roo.log(" no label && no align");
12807 html: this.boxLabel
12815 onClick : function()
12817 this.setChecked(true);
12820 setChecked : function(state,suppressEvent)
12823 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12824 v.dom.checked = false;
12828 this.checked = state;
12829 this.inputEl().dom.checked = state;
12831 if(suppressEvent !== true){
12832 this.fireEvent('check', this, state);
12835 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12839 getGroupValue : function()
12842 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12843 if(v.dom.checked == true){
12844 value = v.dom.value;
12852 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12853 * @return {Mixed} value The field value
12855 getValue : function(){
12856 return this.getGroupValue();
12862 //<script type="text/javascript">
12865 * Based Ext JS Library 1.1.1
12866 * Copyright(c) 2006-2007, Ext JS, LLC.
12872 * @class Roo.HtmlEditorCore
12873 * @extends Roo.Component
12874 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12876 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12879 Roo.HtmlEditorCore = function(config){
12882 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12885 * @event initialize
12886 * Fires when the editor is fully initialized (including the iframe)
12887 * @param {Roo.HtmlEditorCore} this
12892 * Fires when the editor is first receives the focus. Any insertion must wait
12893 * until after this event.
12894 * @param {Roo.HtmlEditorCore} this
12898 * @event beforesync
12899 * Fires before the textarea is updated with content from the editor iframe. Return false
12900 * to cancel the sync.
12901 * @param {Roo.HtmlEditorCore} this
12902 * @param {String} html
12906 * @event beforepush
12907 * Fires before the iframe editor is updated with content from the textarea. Return false
12908 * to cancel the push.
12909 * @param {Roo.HtmlEditorCore} this
12910 * @param {String} html
12915 * Fires when the textarea is updated with content from the editor iframe.
12916 * @param {Roo.HtmlEditorCore} this
12917 * @param {String} html
12922 * Fires when the iframe editor is updated with content from the textarea.
12923 * @param {Roo.HtmlEditorCore} this
12924 * @param {String} html
12929 * @event editorevent
12930 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12931 * @param {Roo.HtmlEditorCore} this
12939 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12943 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12949 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12954 * @cfg {Number} height (in pixels)
12958 * @cfg {Number} width (in pixels)
12963 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12966 stylesheets: false,
12971 // private properties
12972 validationEvent : false,
12974 initialized : false,
12976 sourceEditMode : false,
12977 onFocus : Roo.emptyFn,
12979 hideMode:'offsets',
12987 * Protected method that will not generally be called directly. It
12988 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12989 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12991 getDocMarkup : function(){
12994 Roo.log(this.stylesheets);
12996 // inherit styels from page...??
12997 if (this.stylesheets === false) {
12999 Roo.get(document.head).select('style').each(function(node) {
13000 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13003 Roo.get(document.head).select('link').each(function(node) {
13004 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13007 } else if (!this.stylesheets.length) {
13009 st = '<style type="text/css">' +
13010 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13013 Roo.each(this.stylesheets, function(s) {
13014 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13019 st += '<style type="text/css">' +
13020 'IMG { cursor: pointer } ' +
13024 return '<html><head>' + st +
13025 //<style type="text/css">' +
13026 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13028 ' </head><body class="roo-htmleditor-body"></body></html>';
13032 onRender : function(ct, position)
13035 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13036 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13039 this.el.dom.style.border = '0 none';
13040 this.el.dom.setAttribute('tabIndex', -1);
13041 this.el.addClass('x-hidden hide');
13045 if(Roo.isIE){ // fix IE 1px bogus margin
13046 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13050 this.frameId = Roo.id();
13054 var iframe = this.owner.wrap.createChild({
13056 cls: 'form-control', // bootstrap..
13058 name: this.frameId,
13059 frameBorder : 'no',
13060 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13065 this.iframe = iframe.dom;
13067 this.assignDocWin();
13069 this.doc.designMode = 'on';
13072 this.doc.write(this.getDocMarkup());
13076 var task = { // must defer to wait for browser to be ready
13078 //console.log("run task?" + this.doc.readyState);
13079 this.assignDocWin();
13080 if(this.doc.body || this.doc.readyState == 'complete'){
13082 this.doc.designMode="on";
13086 Roo.TaskMgr.stop(task);
13087 this.initEditor.defer(10, this);
13094 Roo.TaskMgr.start(task);
13101 onResize : function(w, h)
13103 Roo.log('resize: ' +w + ',' + h );
13104 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13108 if(typeof w == 'number'){
13110 this.iframe.style.width = w + 'px';
13112 if(typeof h == 'number'){
13114 this.iframe.style.height = h + 'px';
13116 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13123 * Toggles the editor between standard and source edit mode.
13124 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13126 toggleSourceEdit : function(sourceEditMode){
13128 this.sourceEditMode = sourceEditMode === true;
13130 if(this.sourceEditMode){
13132 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13135 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13136 //this.iframe.className = '';
13139 //this.setSize(this.owner.wrap.getSize());
13140 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13147 * Protected method that will not generally be called directly. If you need/want
13148 * custom HTML cleanup, this is the method you should override.
13149 * @param {String} html The HTML to be cleaned
13150 * return {String} The cleaned HTML
13152 cleanHtml : function(html){
13153 html = String(html);
13154 if(html.length > 5){
13155 if(Roo.isSafari){ // strip safari nonsense
13156 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13159 if(html == ' '){
13166 * HTML Editor -> Textarea
13167 * Protected method that will not generally be called directly. Syncs the contents
13168 * of the editor iframe with the textarea.
13170 syncValue : function(){
13171 if(this.initialized){
13172 var bd = (this.doc.body || this.doc.documentElement);
13173 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13174 var html = bd.innerHTML;
13176 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13177 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13179 html = '<div style="'+m[0]+'">' + html + '</div>';
13182 html = this.cleanHtml(html);
13183 // fix up the special chars.. normaly like back quotes in word...
13184 // however we do not want to do this with chinese..
13185 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13186 var cc = b.charCodeAt();
13188 (cc >= 0x4E00 && cc < 0xA000 ) ||
13189 (cc >= 0x3400 && cc < 0x4E00 ) ||
13190 (cc >= 0xf900 && cc < 0xfb00 )
13196 if(this.owner.fireEvent('beforesync', this, html) !== false){
13197 this.el.dom.value = html;
13198 this.owner.fireEvent('sync', this, html);
13204 * Protected method that will not generally be called directly. Pushes the value of the textarea
13205 * into the iframe editor.
13207 pushValue : function(){
13208 if(this.initialized){
13209 var v = this.el.dom.value.trim();
13211 // if(v.length < 1){
13215 if(this.owner.fireEvent('beforepush', this, v) !== false){
13216 var d = (this.doc.body || this.doc.documentElement);
13218 this.cleanUpPaste();
13219 this.el.dom.value = d.innerHTML;
13220 this.owner.fireEvent('push', this, v);
13226 deferFocus : function(){
13227 this.focus.defer(10, this);
13231 focus : function(){
13232 if(this.win && !this.sourceEditMode){
13239 assignDocWin: function()
13241 var iframe = this.iframe;
13244 this.doc = iframe.contentWindow.document;
13245 this.win = iframe.contentWindow;
13247 if (!Roo.get(this.frameId)) {
13250 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13251 this.win = Roo.get(this.frameId).dom.contentWindow;
13256 initEditor : function(){
13257 //console.log("INIT EDITOR");
13258 this.assignDocWin();
13262 this.doc.designMode="on";
13264 this.doc.write(this.getDocMarkup());
13267 var dbody = (this.doc.body || this.doc.documentElement);
13268 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13269 // this copies styles from the containing element into thsi one..
13270 // not sure why we need all of this..
13271 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13272 ss['background-attachment'] = 'fixed'; // w3c
13273 dbody.bgProperties = 'fixed'; // ie
13274 Roo.DomHelper.applyStyles(dbody, ss);
13275 Roo.EventManager.on(this.doc, {
13276 //'mousedown': this.onEditorEvent,
13277 'mouseup': this.onEditorEvent,
13278 'dblclick': this.onEditorEvent,
13279 'click': this.onEditorEvent,
13280 'keyup': this.onEditorEvent,
13285 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13287 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13288 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13290 this.initialized = true;
13292 this.owner.fireEvent('initialize', this);
13297 onDestroy : function(){
13303 //for (var i =0; i < this.toolbars.length;i++) {
13304 // // fixme - ask toolbars for heights?
13305 // this.toolbars[i].onDestroy();
13308 //this.wrap.dom.innerHTML = '';
13309 //this.wrap.remove();
13314 onFirstFocus : function(){
13316 this.assignDocWin();
13319 this.activated = true;
13322 if(Roo.isGecko){ // prevent silly gecko errors
13324 var s = this.win.getSelection();
13325 if(!s.focusNode || s.focusNode.nodeType != 3){
13326 var r = s.getRangeAt(0);
13327 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13332 this.execCmd('useCSS', true);
13333 this.execCmd('styleWithCSS', false);
13336 this.owner.fireEvent('activate', this);
13340 adjustFont: function(btn){
13341 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13342 //if(Roo.isSafari){ // safari
13345 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13346 if(Roo.isSafari){ // safari
13347 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13348 v = (v < 10) ? 10 : v;
13349 v = (v > 48) ? 48 : v;
13350 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13355 v = Math.max(1, v+adjust);
13357 this.execCmd('FontSize', v );
13360 onEditorEvent : function(e){
13361 this.owner.fireEvent('editorevent', this, e);
13362 // this.updateToolbar();
13363 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13366 insertTag : function(tg)
13368 // could be a bit smarter... -> wrap the current selected tRoo..
13369 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13371 range = this.createRange(this.getSelection());
13372 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13373 wrappingNode.appendChild(range.extractContents());
13374 range.insertNode(wrappingNode);
13381 this.execCmd("formatblock", tg);
13385 insertText : function(txt)
13389 var range = this.createRange();
13390 range.deleteContents();
13391 //alert(Sender.getAttribute('label'));
13393 range.insertNode(this.doc.createTextNode(txt));
13399 * Executes a Midas editor command on the editor document and performs necessary focus and
13400 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13401 * @param {String} cmd The Midas command
13402 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13404 relayCmd : function(cmd, value){
13406 this.execCmd(cmd, value);
13407 this.owner.fireEvent('editorevent', this);
13408 //this.updateToolbar();
13409 this.owner.deferFocus();
13413 * Executes a Midas editor command directly on the editor document.
13414 * For visual commands, you should use {@link #relayCmd} instead.
13415 * <b>This should only be called after the editor is initialized.</b>
13416 * @param {String} cmd The Midas command
13417 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13419 execCmd : function(cmd, value){
13420 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13427 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13429 * @param {String} text | dom node..
13431 insertAtCursor : function(text)
13436 if(!this.activated){
13442 var r = this.doc.selection.createRange();
13453 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13457 // from jquery ui (MIT licenced)
13459 var win = this.win;
13461 if (win.getSelection && win.getSelection().getRangeAt) {
13462 range = win.getSelection().getRangeAt(0);
13463 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13464 range.insertNode(node);
13465 } else if (win.document.selection && win.document.selection.createRange) {
13466 // no firefox support
13467 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13468 win.document.selection.createRange().pasteHTML(txt);
13470 // no firefox support
13471 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13472 this.execCmd('InsertHTML', txt);
13481 mozKeyPress : function(e){
13483 var c = e.getCharCode(), cmd;
13486 c = String.fromCharCode(c).toLowerCase();
13500 this.cleanUpPaste.defer(100, this);
13508 e.preventDefault();
13516 fixKeys : function(){ // load time branching for fastest keydown performance
13518 return function(e){
13519 var k = e.getKey(), r;
13522 r = this.doc.selection.createRange();
13525 r.pasteHTML('    ');
13532 r = this.doc.selection.createRange();
13534 var target = r.parentElement();
13535 if(!target || target.tagName.toLowerCase() != 'li'){
13537 r.pasteHTML('<br />');
13543 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13544 this.cleanUpPaste.defer(100, this);
13550 }else if(Roo.isOpera){
13551 return function(e){
13552 var k = e.getKey();
13556 this.execCmd('InsertHTML','    ');
13559 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13560 this.cleanUpPaste.defer(100, this);
13565 }else if(Roo.isSafari){
13566 return function(e){
13567 var k = e.getKey();
13571 this.execCmd('InsertText','\t');
13575 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13576 this.cleanUpPaste.defer(100, this);
13584 getAllAncestors: function()
13586 var p = this.getSelectedNode();
13589 a.push(p); // push blank onto stack..
13590 p = this.getParentElement();
13594 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13598 a.push(this.doc.body);
13602 lastSelNode : false,
13605 getSelection : function()
13607 this.assignDocWin();
13608 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13611 getSelectedNode: function()
13613 // this may only work on Gecko!!!
13615 // should we cache this!!!!
13620 var range = this.createRange(this.getSelection()).cloneRange();
13623 var parent = range.parentElement();
13625 var testRange = range.duplicate();
13626 testRange.moveToElementText(parent);
13627 if (testRange.inRange(range)) {
13630 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13633 parent = parent.parentElement;
13638 // is ancestor a text element.
13639 var ac = range.commonAncestorContainer;
13640 if (ac.nodeType == 3) {
13641 ac = ac.parentNode;
13644 var ar = ac.childNodes;
13647 var other_nodes = [];
13648 var has_other_nodes = false;
13649 for (var i=0;i<ar.length;i++) {
13650 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13653 // fullly contained node.
13655 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13660 // probably selected..
13661 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13662 other_nodes.push(ar[i]);
13666 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13671 has_other_nodes = true;
13673 if (!nodes.length && other_nodes.length) {
13674 nodes= other_nodes;
13676 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13682 createRange: function(sel)
13684 // this has strange effects when using with
13685 // top toolbar - not sure if it's a great idea.
13686 //this.editor.contentWindow.focus();
13687 if (typeof sel != "undefined") {
13689 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13691 return this.doc.createRange();
13694 return this.doc.createRange();
13697 getParentElement: function()
13700 this.assignDocWin();
13701 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13703 var range = this.createRange(sel);
13706 var p = range.commonAncestorContainer;
13707 while (p.nodeType == 3) { // text node
13718 * Range intersection.. the hard stuff...
13722 * [ -- selected range --- ]
13726 * if end is before start or hits it. fail.
13727 * if start is after end or hits it fail.
13729 * if either hits (but other is outside. - then it's not
13735 // @see http://www.thismuchiknow.co.uk/?p=64.
13736 rangeIntersectsNode : function(range, node)
13738 var nodeRange = node.ownerDocument.createRange();
13740 nodeRange.selectNode(node);
13742 nodeRange.selectNodeContents(node);
13745 var rangeStartRange = range.cloneRange();
13746 rangeStartRange.collapse(true);
13748 var rangeEndRange = range.cloneRange();
13749 rangeEndRange.collapse(false);
13751 var nodeStartRange = nodeRange.cloneRange();
13752 nodeStartRange.collapse(true);
13754 var nodeEndRange = nodeRange.cloneRange();
13755 nodeEndRange.collapse(false);
13757 return rangeStartRange.compareBoundaryPoints(
13758 Range.START_TO_START, nodeEndRange) == -1 &&
13759 rangeEndRange.compareBoundaryPoints(
13760 Range.START_TO_START, nodeStartRange) == 1;
13764 rangeCompareNode : function(range, node)
13766 var nodeRange = node.ownerDocument.createRange();
13768 nodeRange.selectNode(node);
13770 nodeRange.selectNodeContents(node);
13774 range.collapse(true);
13776 nodeRange.collapse(true);
13778 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13779 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13781 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13783 var nodeIsBefore = ss == 1;
13784 var nodeIsAfter = ee == -1;
13786 if (nodeIsBefore && nodeIsAfter)
13788 if (!nodeIsBefore && nodeIsAfter)
13789 return 1; //right trailed.
13791 if (nodeIsBefore && !nodeIsAfter)
13792 return 2; // left trailed.
13797 // private? - in a new class?
13798 cleanUpPaste : function()
13800 // cleans up the whole document..
13801 Roo.log('cleanuppaste');
13803 this.cleanUpChildren(this.doc.body);
13804 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13805 if (clean != this.doc.body.innerHTML) {
13806 this.doc.body.innerHTML = clean;
13811 cleanWordChars : function(input) {// change the chars to hex code
13812 var he = Roo.HtmlEditorCore;
13814 var output = input;
13815 Roo.each(he.swapCodes, function(sw) {
13816 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13818 output = output.replace(swapper, sw[1]);
13825 cleanUpChildren : function (n)
13827 if (!n.childNodes.length) {
13830 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13831 this.cleanUpChild(n.childNodes[i]);
13838 cleanUpChild : function (node)
13841 //console.log(node);
13842 if (node.nodeName == "#text") {
13843 // clean up silly Windows -- stuff?
13846 if (node.nodeName == "#comment") {
13847 node.parentNode.removeChild(node);
13848 // clean up silly Windows -- stuff?
13852 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13854 node.parentNode.removeChild(node);
13859 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13861 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13862 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13864 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13865 // remove_keep_children = true;
13868 if (remove_keep_children) {
13869 this.cleanUpChildren(node);
13870 // inserts everything just before this node...
13871 while (node.childNodes.length) {
13872 var cn = node.childNodes[0];
13873 node.removeChild(cn);
13874 node.parentNode.insertBefore(cn, node);
13876 node.parentNode.removeChild(node);
13880 if (!node.attributes || !node.attributes.length) {
13881 this.cleanUpChildren(node);
13885 function cleanAttr(n,v)
13888 if (v.match(/^\./) || v.match(/^\//)) {
13891 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13894 if (v.match(/^#/)) {
13897 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13898 node.removeAttribute(n);
13902 function cleanStyle(n,v)
13904 if (v.match(/expression/)) { //XSS?? should we even bother..
13905 node.removeAttribute(n);
13908 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13909 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13912 var parts = v.split(/;/);
13915 Roo.each(parts, function(p) {
13916 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13920 var l = p.split(':').shift().replace(/\s+/g,'');
13921 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13923 if ( cblack.indexOf(l) > -1) {
13924 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13925 //node.removeAttribute(n);
13929 // only allow 'c whitelisted system attributes'
13930 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13931 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13932 //node.removeAttribute(n);
13942 if (clean.length) {
13943 node.setAttribute(n, clean.join(';'));
13945 node.removeAttribute(n);
13951 for (var i = node.attributes.length-1; i > -1 ; i--) {
13952 var a = node.attributes[i];
13955 if (a.name.toLowerCase().substr(0,2)=='on') {
13956 node.removeAttribute(a.name);
13959 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13960 node.removeAttribute(a.name);
13963 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13964 cleanAttr(a.name,a.value); // fixme..
13967 if (a.name == 'style') {
13968 cleanStyle(a.name,a.value);
13971 /// clean up MS crap..
13972 // tecnically this should be a list of valid class'es..
13975 if (a.name == 'class') {
13976 if (a.value.match(/^Mso/)) {
13977 node.className = '';
13980 if (a.value.match(/body/)) {
13981 node.className = '';
13992 this.cleanUpChildren(node);
13998 // hide stuff that is not compatible
14012 * @event specialkey
14016 * @cfg {String} fieldClass @hide
14019 * @cfg {String} focusClass @hide
14022 * @cfg {String} autoCreate @hide
14025 * @cfg {String} inputType @hide
14028 * @cfg {String} invalidClass @hide
14031 * @cfg {String} invalidText @hide
14034 * @cfg {String} msgFx @hide
14037 * @cfg {String} validateOnBlur @hide
14041 Roo.HtmlEditorCore.white = [
14042 'area', 'br', 'img', 'input', 'hr', 'wbr',
14044 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14045 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14046 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14047 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14048 'table', 'ul', 'xmp',
14050 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14053 'dir', 'menu', 'ol', 'ul', 'dl',
14059 Roo.HtmlEditorCore.black = [
14060 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14062 'base', 'basefont', 'bgsound', 'blink', 'body',
14063 'frame', 'frameset', 'head', 'html', 'ilayer',
14064 'iframe', 'layer', 'link', 'meta', 'object',
14065 'script', 'style' ,'title', 'xml' // clean later..
14067 Roo.HtmlEditorCore.clean = [
14068 'script', 'style', 'title', 'xml'
14070 Roo.HtmlEditorCore.remove = [
14075 Roo.HtmlEditorCore.ablack = [
14079 Roo.HtmlEditorCore.aclean = [
14080 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14084 Roo.HtmlEditorCore.pwhite= [
14085 'http', 'https', 'mailto'
14088 // white listed style attributes.
14089 Roo.HtmlEditorCore.cwhite= [
14090 // 'text-align', /// default is to allow most things..
14096 // black listed style attributes.
14097 Roo.HtmlEditorCore.cblack= [
14098 // 'font-size' -- this can be set by the project
14102 Roo.HtmlEditorCore.swapCodes =[
14121 * @class Roo.bootstrap.HtmlEditor
14122 * @extends Roo.bootstrap.TextArea
14123 * Bootstrap HtmlEditor class
14126 * Create a new HtmlEditor
14127 * @param {Object} config The config object
14130 Roo.bootstrap.HtmlEditor = function(config){
14131 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14132 if (!this.toolbars) {
14133 this.toolbars = [];
14135 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14138 * @event initialize
14139 * Fires when the editor is fully initialized (including the iframe)
14140 * @param {HtmlEditor} this
14145 * Fires when the editor is first receives the focus. Any insertion must wait
14146 * until after this event.
14147 * @param {HtmlEditor} this
14151 * @event beforesync
14152 * Fires before the textarea is updated with content from the editor iframe. Return false
14153 * to cancel the sync.
14154 * @param {HtmlEditor} this
14155 * @param {String} html
14159 * @event beforepush
14160 * Fires before the iframe editor is updated with content from the textarea. Return false
14161 * to cancel the push.
14162 * @param {HtmlEditor} this
14163 * @param {String} html
14168 * Fires when the textarea is updated with content from the editor iframe.
14169 * @param {HtmlEditor} this
14170 * @param {String} html
14175 * Fires when the iframe editor is updated with content from the textarea.
14176 * @param {HtmlEditor} this
14177 * @param {String} html
14181 * @event editmodechange
14182 * Fires when the editor switches edit modes
14183 * @param {HtmlEditor} this
14184 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14186 editmodechange: true,
14188 * @event editorevent
14189 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14190 * @param {HtmlEditor} this
14194 * @event firstfocus
14195 * Fires when on first focus - needed by toolbars..
14196 * @param {HtmlEditor} this
14201 * Auto save the htmlEditor value as a file into Events
14202 * @param {HtmlEditor} this
14206 * @event savedpreview
14207 * preview the saved version of htmlEditor
14208 * @param {HtmlEditor} this
14215 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14219 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14224 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14229 * @cfg {Number} height (in pixels)
14233 * @cfg {Number} width (in pixels)
14238 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14241 stylesheets: false,
14246 // private properties
14247 validationEvent : false,
14249 initialized : false,
14252 onFocus : Roo.emptyFn,
14254 hideMode:'offsets',
14257 tbContainer : false,
14259 toolbarContainer :function() {
14260 return this.wrap.select('.x-html-editor-tb',true).first();
14264 * Protected method that will not generally be called directly. It
14265 * is called when the editor creates its toolbar. Override this method if you need to
14266 * add custom toolbar buttons.
14267 * @param {HtmlEditor} editor
14269 createToolbar : function(){
14271 Roo.log("create toolbars");
14273 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14274 this.toolbars[0].render(this.toolbarContainer());
14278 // if (!editor.toolbars || !editor.toolbars.length) {
14279 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14282 // for (var i =0 ; i < editor.toolbars.length;i++) {
14283 // editor.toolbars[i] = Roo.factory(
14284 // typeof(editor.toolbars[i]) == 'string' ?
14285 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14286 // Roo.bootstrap.HtmlEditor);
14287 // editor.toolbars[i].init(editor);
14293 onRender : function(ct, position)
14295 // Roo.log("Call onRender: " + this.xtype);
14297 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14299 this.wrap = this.inputEl().wrap({
14300 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14303 this.editorcore.onRender(ct, position);
14305 if (this.resizable) {
14306 this.resizeEl = new Roo.Resizable(this.wrap, {
14310 minHeight : this.height,
14311 height: this.height,
14312 handles : this.resizable,
14315 resize : function(r, w, h) {
14316 _t.onResize(w,h); // -something
14322 this.createToolbar(this);
14325 if(!this.width && this.resizable){
14326 this.setSize(this.wrap.getSize());
14328 if (this.resizeEl) {
14329 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14330 // should trigger onReize..
14336 onResize : function(w, h)
14338 Roo.log('resize: ' +w + ',' + h );
14339 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14343 if(this.inputEl() ){
14344 if(typeof w == 'number'){
14345 var aw = w - this.wrap.getFrameWidth('lr');
14346 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14349 if(typeof h == 'number'){
14350 var tbh = -11; // fixme it needs to tool bar size!
14351 for (var i =0; i < this.toolbars.length;i++) {
14352 // fixme - ask toolbars for heights?
14353 tbh += this.toolbars[i].el.getHeight();
14354 //if (this.toolbars[i].footer) {
14355 // tbh += this.toolbars[i].footer.el.getHeight();
14363 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14364 ah -= 5; // knock a few pixes off for look..
14365 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14369 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14370 this.editorcore.onResize(ew,eh);
14375 * Toggles the editor between standard and source edit mode.
14376 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14378 toggleSourceEdit : function(sourceEditMode)
14380 this.editorcore.toggleSourceEdit(sourceEditMode);
14382 if(this.editorcore.sourceEditMode){
14383 Roo.log('editor - showing textarea');
14386 // Roo.log(this.syncValue());
14388 this.inputEl().removeClass('hide');
14389 this.inputEl().dom.removeAttribute('tabIndex');
14390 this.inputEl().focus();
14392 Roo.log('editor - hiding textarea');
14394 // Roo.log(this.pushValue());
14397 this.inputEl().addClass('hide');
14398 this.inputEl().dom.setAttribute('tabIndex', -1);
14399 //this.deferFocus();
14402 if(this.resizable){
14403 this.setSize(this.wrap.getSize());
14406 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14409 // private (for BoxComponent)
14410 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14412 // private (for BoxComponent)
14413 getResizeEl : function(){
14417 // private (for BoxComponent)
14418 getPositionEl : function(){
14423 initEvents : function(){
14424 this.originalValue = this.getValue();
14428 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14431 // markInvalid : Roo.emptyFn,
14433 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14436 // clearInvalid : Roo.emptyFn,
14438 setValue : function(v){
14439 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14440 this.editorcore.pushValue();
14445 deferFocus : function(){
14446 this.focus.defer(10, this);
14450 focus : function(){
14451 this.editorcore.focus();
14457 onDestroy : function(){
14463 for (var i =0; i < this.toolbars.length;i++) {
14464 // fixme - ask toolbars for heights?
14465 this.toolbars[i].onDestroy();
14468 this.wrap.dom.innerHTML = '';
14469 this.wrap.remove();
14474 onFirstFocus : function(){
14475 //Roo.log("onFirstFocus");
14476 this.editorcore.onFirstFocus();
14477 for (var i =0; i < this.toolbars.length;i++) {
14478 this.toolbars[i].onFirstFocus();
14484 syncValue : function()
14486 this.editorcore.syncValue();
14489 pushValue : function()
14491 this.editorcore.pushValue();
14495 // hide stuff that is not compatible
14509 * @event specialkey
14513 * @cfg {String} fieldClass @hide
14516 * @cfg {String} focusClass @hide
14519 * @cfg {String} autoCreate @hide
14522 * @cfg {String} inputType @hide
14525 * @cfg {String} invalidClass @hide
14528 * @cfg {String} invalidText @hide
14531 * @cfg {String} msgFx @hide
14534 * @cfg {String} validateOnBlur @hide
14545 * @class Roo.bootstrap.HtmlEditorToolbar1
14550 new Roo.bootstrap.HtmlEditor({
14553 new Roo.bootstrap.HtmlEditorToolbar1({
14554 disable : { fonts: 1 , format: 1, ..., ... , ...],
14560 * @cfg {Object} disable List of elements to disable..
14561 * @cfg {Array} btns List of additional buttons.
14565 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14568 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14571 Roo.apply(this, config);
14573 // default disabled, based on 'good practice'..
14574 this.disable = this.disable || {};
14575 Roo.applyIf(this.disable, {
14578 specialElements : true
14580 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14582 this.editor = config.editor;
14583 this.editorcore = config.editor.editorcore;
14585 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14587 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14588 // dont call parent... till later.
14590 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14596 editorcore : false,
14601 "h1","h2","h3","h4","h5","h6",
14603 "abbr", "acronym", "address", "cite", "samp", "var",
14607 onRender : function(ct, position)
14609 // Roo.log("Call onRender: " + this.xtype);
14611 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14613 this.el.dom.style.marginBottom = '0';
14615 var editorcore = this.editorcore;
14616 var editor= this.editor;
14619 var btn = function(id,cmd , toggle, handler){
14621 var event = toggle ? 'toggle' : 'click';
14626 xns: Roo.bootstrap,
14629 enableToggle:toggle !== false,
14631 pressed : toggle ? false : null,
14634 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14635 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14644 xns: Roo.bootstrap,
14645 glyphicon : 'font',
14649 xns: Roo.bootstrap,
14653 Roo.each(this.formats, function(f) {
14654 style.menu.items.push({
14656 xns: Roo.bootstrap,
14657 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14662 editorcore.insertTag(this.tagname);
14669 children.push(style);
14672 btn('bold',false,true);
14673 btn('italic',false,true);
14674 btn('align-left', 'justifyleft',true);
14675 btn('align-center', 'justifycenter',true);
14676 btn('align-right' , 'justifyright',true);
14677 btn('link', false, false, function(btn) {
14678 //Roo.log("create link?");
14679 var url = prompt(this.createLinkText, this.defaultLinkValue);
14680 if(url && url != 'http:/'+'/'){
14681 this.editorcore.relayCmd('createlink', url);
14684 btn('list','insertunorderedlist',true);
14685 btn('pencil', false,true, function(btn){
14688 this.toggleSourceEdit(btn.pressed);
14694 xns: Roo.bootstrap,
14699 xns: Roo.bootstrap,
14704 cog.menu.items.push({
14706 xns: Roo.bootstrap,
14707 html : Clean styles,
14712 editorcore.insertTag(this.tagname);
14721 this.xtype = 'Navbar';
14723 for(var i=0;i< children.length;i++) {
14725 this.buttons.add(this.addxtypeChild(children[i]));
14729 editor.on('editorevent', this.updateToolbar, this);
14731 onBtnClick : function(id)
14733 this.editorcore.relayCmd(id);
14734 this.editorcore.focus();
14738 * Protected method that will not generally be called directly. It triggers
14739 * a toolbar update by reading the markup state of the current selection in the editor.
14741 updateToolbar: function(){
14743 if(!this.editorcore.activated){
14744 this.editor.onFirstFocus(); // is this neeed?
14748 var btns = this.buttons;
14749 var doc = this.editorcore.doc;
14750 btns.get('bold').setActive(doc.queryCommandState('bold'));
14751 btns.get('italic').setActive(doc.queryCommandState('italic'));
14752 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14754 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14755 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14756 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14758 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14759 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14762 var ans = this.editorcore.getAllAncestors();
14763 if (this.formatCombo) {
14766 var store = this.formatCombo.store;
14767 this.formatCombo.setValue("");
14768 for (var i =0; i < ans.length;i++) {
14769 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14771 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14779 // hides menus... - so this cant be on a menu...
14780 Roo.bootstrap.MenuMgr.hideAll();
14782 Roo.bootstrap.MenuMgr.hideAll();
14783 //this.editorsyncValue();
14785 onFirstFocus: function() {
14786 this.buttons.each(function(item){
14790 toggleSourceEdit : function(sourceEditMode){
14793 if(sourceEditMode){
14794 Roo.log("disabling buttons");
14795 this.buttons.each( function(item){
14796 if(item.cmd != 'pencil'){
14802 Roo.log("enabling buttons");
14803 if(this.editorcore.initialized){
14804 this.buttons.each( function(item){
14810 Roo.log("calling toggole on editor");
14811 // tell the editor that it's been pressed..
14812 this.editor.toggleSourceEdit(sourceEditMode);
14822 * @class Roo.bootstrap.Table.AbstractSelectionModel
14823 * @extends Roo.util.Observable
14824 * Abstract base class for grid SelectionModels. It provides the interface that should be
14825 * implemented by descendant classes. This class should not be directly instantiated.
14828 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14829 this.locked = false;
14830 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14834 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14835 /** @ignore Called by the grid automatically. Do not call directly. */
14836 init : function(grid){
14842 * Locks the selections.
14845 this.locked = true;
14849 * Unlocks the selections.
14851 unlock : function(){
14852 this.locked = false;
14856 * Returns true if the selections are locked.
14857 * @return {Boolean}
14859 isLocked : function(){
14860 return this.locked;
14864 * @class Roo.bootstrap.Table.ColumnModel
14865 * @extends Roo.util.Observable
14866 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14867 * the columns in the table.
14870 * @param {Object} config An Array of column config objects. See this class's
14871 * config objects for details.
14873 Roo.bootstrap.Table.ColumnModel = function(config){
14875 * The config passed into the constructor
14877 this.config = config;
14880 // if no id, create one
14881 // if the column does not have a dataIndex mapping,
14882 // map it to the order it is in the config
14883 for(var i = 0, len = config.length; i < len; i++){
14885 if(typeof c.dataIndex == "undefined"){
14888 if(typeof c.renderer == "string"){
14889 c.renderer = Roo.util.Format[c.renderer];
14891 if(typeof c.id == "undefined"){
14894 // if(c.editor && c.editor.xtype){
14895 // c.editor = Roo.factory(c.editor, Roo.grid);
14897 // if(c.editor && c.editor.isFormField){
14898 // c.editor = new Roo.grid.GridEditor(c.editor);
14901 this.lookup[c.id] = c;
14905 * The width of columns which have no width specified (defaults to 100)
14908 this.defaultWidth = 100;
14911 * Default sortable of columns which have no sortable specified (defaults to false)
14914 this.defaultSortable = false;
14918 * @event widthchange
14919 * Fires when the width of a column changes.
14920 * @param {ColumnModel} this
14921 * @param {Number} columnIndex The column index
14922 * @param {Number} newWidth The new width
14924 "widthchange": true,
14926 * @event headerchange
14927 * Fires when the text of a header changes.
14928 * @param {ColumnModel} this
14929 * @param {Number} columnIndex The column index
14930 * @param {Number} newText The new header text
14932 "headerchange": true,
14934 * @event hiddenchange
14935 * Fires when a column is hidden or "unhidden".
14936 * @param {ColumnModel} this
14937 * @param {Number} columnIndex The column index
14938 * @param {Boolean} hidden true if hidden, false otherwise
14940 "hiddenchange": true,
14942 * @event columnmoved
14943 * Fires when a column is moved.
14944 * @param {ColumnModel} this
14945 * @param {Number} oldIndex
14946 * @param {Number} newIndex
14948 "columnmoved" : true,
14950 * @event columlockchange
14951 * Fires when a column's locked state is changed
14952 * @param {ColumnModel} this
14953 * @param {Number} colIndex
14954 * @param {Boolean} locked true if locked
14956 "columnlockchange" : true
14958 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14960 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14962 * @cfg {String} header The header text to display in the Grid view.
14965 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14966 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14967 * specified, the column's index is used as an index into the Record's data Array.
14970 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14971 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14974 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14975 * Defaults to the value of the {@link #defaultSortable} property.
14976 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14979 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14982 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14985 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14988 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14991 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14992 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14993 * default renderer uses the raw data value.
14996 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
15000 * Returns the id of the column at the specified index.
15001 * @param {Number} index The column index
15002 * @return {String} the id
15004 getColumnId : function(index){
15005 return this.config[index].id;
15009 * Returns the column for a specified id.
15010 * @param {String} id The column id
15011 * @return {Object} the column
15013 getColumnById : function(id){
15014 return this.lookup[id];
15019 * Returns the column for a specified dataIndex.
15020 * @param {String} dataIndex The column dataIndex
15021 * @return {Object|Boolean} the column or false if not found
15023 getColumnByDataIndex: function(dataIndex){
15024 var index = this.findColumnIndex(dataIndex);
15025 return index > -1 ? this.config[index] : false;
15029 * Returns the index for a specified column id.
15030 * @param {String} id The column id
15031 * @return {Number} the index, or -1 if not found
15033 getIndexById : function(id){
15034 for(var i = 0, len = this.config.length; i < len; i++){
15035 if(this.config[i].id == id){
15043 * Returns the index for a specified column dataIndex.
15044 * @param {String} dataIndex The column dataIndex
15045 * @return {Number} the index, or -1 if not found
15048 findColumnIndex : function(dataIndex){
15049 for(var i = 0, len = this.config.length; i < len; i++){
15050 if(this.config[i].dataIndex == dataIndex){
15058 moveColumn : function(oldIndex, newIndex){
15059 var c = this.config[oldIndex];
15060 this.config.splice(oldIndex, 1);
15061 this.config.splice(newIndex, 0, c);
15062 this.dataMap = null;
15063 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15066 isLocked : function(colIndex){
15067 return this.config[colIndex].locked === true;
15070 setLocked : function(colIndex, value, suppressEvent){
15071 if(this.isLocked(colIndex) == value){
15074 this.config[colIndex].locked = value;
15075 if(!suppressEvent){
15076 this.fireEvent("columnlockchange", this, colIndex, value);
15080 getTotalLockedWidth : function(){
15081 var totalWidth = 0;
15082 for(var i = 0; i < this.config.length; i++){
15083 if(this.isLocked(i) && !this.isHidden(i)){
15084 this.totalWidth += this.getColumnWidth(i);
15090 getLockedCount : function(){
15091 for(var i = 0, len = this.config.length; i < len; i++){
15092 if(!this.isLocked(i)){
15099 * Returns the number of columns.
15102 getColumnCount : function(visibleOnly){
15103 if(visibleOnly === true){
15105 for(var i = 0, len = this.config.length; i < len; i++){
15106 if(!this.isHidden(i)){
15112 return this.config.length;
15116 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15117 * @param {Function} fn
15118 * @param {Object} scope (optional)
15119 * @return {Array} result
15121 getColumnsBy : function(fn, scope){
15123 for(var i = 0, len = this.config.length; i < len; i++){
15124 var c = this.config[i];
15125 if(fn.call(scope||this, c, i) === true){
15133 * Returns true if the specified column is sortable.
15134 * @param {Number} col The column index
15135 * @return {Boolean}
15137 isSortable : function(col){
15138 if(typeof this.config[col].sortable == "undefined"){
15139 return this.defaultSortable;
15141 return this.config[col].sortable;
15145 * Returns the rendering (formatting) function defined for the column.
15146 * @param {Number} col The column index.
15147 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15149 getRenderer : function(col){
15150 if(!this.config[col].renderer){
15151 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15153 return this.config[col].renderer;
15157 * Sets the rendering (formatting) function for a column.
15158 * @param {Number} col The column index
15159 * @param {Function} fn The function to use to process the cell's raw data
15160 * to return HTML markup for the grid view. The render function is called with
15161 * the following parameters:<ul>
15162 * <li>Data value.</li>
15163 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15164 * <li>css A CSS style string to apply to the table cell.</li>
15165 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15166 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15167 * <li>Row index</li>
15168 * <li>Column index</li>
15169 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15171 setRenderer : function(col, fn){
15172 this.config[col].renderer = fn;
15176 * Returns the width for the specified column.
15177 * @param {Number} col The column index
15180 getColumnWidth : function(col){
15181 return this.config[col].width * 1 || this.defaultWidth;
15185 * Sets the width for a column.
15186 * @param {Number} col The column index
15187 * @param {Number} width The new width
15189 setColumnWidth : function(col, width, suppressEvent){
15190 this.config[col].width = width;
15191 this.totalWidth = null;
15192 if(!suppressEvent){
15193 this.fireEvent("widthchange", this, col, width);
15198 * Returns the total width of all columns.
15199 * @param {Boolean} includeHidden True to include hidden column widths
15202 getTotalWidth : function(includeHidden){
15203 if(!this.totalWidth){
15204 this.totalWidth = 0;
15205 for(var i = 0, len = this.config.length; i < len; i++){
15206 if(includeHidden || !this.isHidden(i)){
15207 this.totalWidth += this.getColumnWidth(i);
15211 return this.totalWidth;
15215 * Returns the header for the specified column.
15216 * @param {Number} col The column index
15219 getColumnHeader : function(col){
15220 return this.config[col].header;
15224 * Sets the header for a column.
15225 * @param {Number} col The column index
15226 * @param {String} header The new header
15228 setColumnHeader : function(col, header){
15229 this.config[col].header = header;
15230 this.fireEvent("headerchange", this, col, header);
15234 * Returns the tooltip for the specified column.
15235 * @param {Number} col The column index
15238 getColumnTooltip : function(col){
15239 return this.config[col].tooltip;
15242 * Sets the tooltip for a column.
15243 * @param {Number} col The column index
15244 * @param {String} tooltip The new tooltip
15246 setColumnTooltip : function(col, tooltip){
15247 this.config[col].tooltip = tooltip;
15251 * Returns the dataIndex for the specified column.
15252 * @param {Number} col The column index
15255 getDataIndex : function(col){
15256 return this.config[col].dataIndex;
15260 * Sets the dataIndex for a column.
15261 * @param {Number} col The column index
15262 * @param {Number} dataIndex The new dataIndex
15264 setDataIndex : function(col, dataIndex){
15265 this.config[col].dataIndex = dataIndex;
15271 * Returns true if the cell is editable.
15272 * @param {Number} colIndex The column index
15273 * @param {Number} rowIndex The row index
15274 * @return {Boolean}
15276 isCellEditable : function(colIndex, rowIndex){
15277 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15281 * Returns the editor defined for the cell/column.
15282 * return false or null to disable editing.
15283 * @param {Number} colIndex The column index
15284 * @param {Number} rowIndex The row index
15287 getCellEditor : function(colIndex, rowIndex){
15288 return this.config[colIndex].editor;
15292 * Sets if a column is editable.
15293 * @param {Number} col The column index
15294 * @param {Boolean} editable True if the column is editable
15296 setEditable : function(col, editable){
15297 this.config[col].editable = editable;
15302 * Returns true if the column is hidden.
15303 * @param {Number} colIndex The column index
15304 * @return {Boolean}
15306 isHidden : function(colIndex){
15307 return this.config[colIndex].hidden;
15312 * Returns true if the column width cannot be changed
15314 isFixed : function(colIndex){
15315 return this.config[colIndex].fixed;
15319 * Returns true if the column can be resized
15320 * @return {Boolean}
15322 isResizable : function(colIndex){
15323 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15326 * Sets if a column is hidden.
15327 * @param {Number} colIndex The column index
15328 * @param {Boolean} hidden True if the column is hidden
15330 setHidden : function(colIndex, hidden){
15331 this.config[colIndex].hidden = hidden;
15332 this.totalWidth = null;
15333 this.fireEvent("hiddenchange", this, colIndex, hidden);
15337 * Sets the editor for a column.
15338 * @param {Number} col The column index
15339 * @param {Object} editor The editor object
15341 setEditor : function(col, editor){
15342 this.config[col].editor = editor;
15346 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15347 if(typeof value == "string" && value.length < 1){
15353 // Alias for backwards compatibility
15354 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15357 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15358 * @class Roo.bootstrap.Table.RowSelectionModel
15359 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15360 * It supports multiple selections and keyboard selection/navigation.
15362 * @param {Object} config
15365 Roo.bootstrap.Table.RowSelectionModel = function(config){
15366 Roo.apply(this, config);
15367 this.selections = new Roo.util.MixedCollection(false, function(o){
15372 this.lastActive = false;
15376 * @event selectionchange
15377 * Fires when the selection changes
15378 * @param {SelectionModel} this
15380 "selectionchange" : true,
15382 * @event afterselectionchange
15383 * Fires after the selection changes (eg. by key press or clicking)
15384 * @param {SelectionModel} this
15386 "afterselectionchange" : true,
15388 * @event beforerowselect
15389 * Fires when a row is selected being selected, return false to cancel.
15390 * @param {SelectionModel} this
15391 * @param {Number} rowIndex The selected index
15392 * @param {Boolean} keepExisting False if other selections will be cleared
15394 "beforerowselect" : true,
15397 * Fires when a row is selected.
15398 * @param {SelectionModel} this
15399 * @param {Number} rowIndex The selected index
15400 * @param {Roo.data.Record} r The record
15402 "rowselect" : true,
15404 * @event rowdeselect
15405 * Fires when a row is deselected.
15406 * @param {SelectionModel} this
15407 * @param {Number} rowIndex The selected index
15409 "rowdeselect" : true
15411 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15412 this.locked = false;
15415 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15417 * @cfg {Boolean} singleSelect
15418 * True to allow selection of only one row at a time (defaults to false)
15420 singleSelect : false,
15423 initEvents : function(){
15425 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15426 this.grid.on("mousedown", this.handleMouseDown, this);
15427 }else{ // allow click to work like normal
15428 this.grid.on("rowclick", this.handleDragableRowClick, this);
15431 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15432 "up" : function(e){
15434 this.selectPrevious(e.shiftKey);
15435 }else if(this.last !== false && this.lastActive !== false){
15436 var last = this.last;
15437 this.selectRange(this.last, this.lastActive-1);
15438 this.grid.getView().focusRow(this.lastActive);
15439 if(last !== false){
15443 this.selectFirstRow();
15445 this.fireEvent("afterselectionchange", this);
15447 "down" : function(e){
15449 this.selectNext(e.shiftKey);
15450 }else if(this.last !== false && this.lastActive !== false){
15451 var last = this.last;
15452 this.selectRange(this.last, this.lastActive+1);
15453 this.grid.getView().focusRow(this.lastActive);
15454 if(last !== false){
15458 this.selectFirstRow();
15460 this.fireEvent("afterselectionchange", this);
15465 var view = this.grid.view;
15466 view.on("refresh", this.onRefresh, this);
15467 view.on("rowupdated", this.onRowUpdated, this);
15468 view.on("rowremoved", this.onRemove, this);
15472 onRefresh : function(){
15473 var ds = this.grid.dataSource, i, v = this.grid.view;
15474 var s = this.selections;
15475 s.each(function(r){
15476 if((i = ds.indexOfId(r.id)) != -1){
15485 onRemove : function(v, index, r){
15486 this.selections.remove(r);
15490 onRowUpdated : function(v, index, r){
15491 if(this.isSelected(r)){
15492 v.onRowSelect(index);
15498 * @param {Array} records The records to select
15499 * @param {Boolean} keepExisting (optional) True to keep existing selections
15501 selectRecords : function(records, keepExisting){
15503 this.clearSelections();
15505 var ds = this.grid.dataSource;
15506 for(var i = 0, len = records.length; i < len; i++){
15507 this.selectRow(ds.indexOf(records[i]), true);
15512 * Gets the number of selected rows.
15515 getCount : function(){
15516 return this.selections.length;
15520 * Selects the first row in the grid.
15522 selectFirstRow : function(){
15527 * Select the last row.
15528 * @param {Boolean} keepExisting (optional) True to keep existing selections
15530 selectLastRow : function(keepExisting){
15531 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15535 * Selects the row immediately following the last selected row.
15536 * @param {Boolean} keepExisting (optional) True to keep existing selections
15538 selectNext : function(keepExisting){
15539 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15540 this.selectRow(this.last+1, keepExisting);
15541 this.grid.getView().focusRow(this.last);
15546 * Selects the row that precedes the last selected row.
15547 * @param {Boolean} keepExisting (optional) True to keep existing selections
15549 selectPrevious : function(keepExisting){
15551 this.selectRow(this.last-1, keepExisting);
15552 this.grid.getView().focusRow(this.last);
15557 * Returns the selected records
15558 * @return {Array} Array of selected records
15560 getSelections : function(){
15561 return [].concat(this.selections.items);
15565 * Returns the first selected record.
15568 getSelected : function(){
15569 return this.selections.itemAt(0);
15574 * Clears all selections.
15576 clearSelections : function(fast){
15577 if(this.locked) return;
15579 var ds = this.grid.dataSource;
15580 var s = this.selections;
15581 s.each(function(r){
15582 this.deselectRow(ds.indexOfId(r.id));
15586 this.selections.clear();
15593 * Selects all rows.
15595 selectAll : function(){
15596 if(this.locked) return;
15597 this.selections.clear();
15598 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15599 this.selectRow(i, true);
15604 * Returns True if there is a selection.
15605 * @return {Boolean}
15607 hasSelection : function(){
15608 return this.selections.length > 0;
15612 * Returns True if the specified row is selected.
15613 * @param {Number/Record} record The record or index of the record to check
15614 * @return {Boolean}
15616 isSelected : function(index){
15617 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15618 return (r && this.selections.key(r.id) ? true : false);
15622 * Returns True if the specified record id is selected.
15623 * @param {String} id The id of record to check
15624 * @return {Boolean}
15626 isIdSelected : function(id){
15627 return (this.selections.key(id) ? true : false);
15631 handleMouseDown : function(e, t){
15632 var view = this.grid.getView(), rowIndex;
15633 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15636 if(e.shiftKey && this.last !== false){
15637 var last = this.last;
15638 this.selectRange(last, rowIndex, e.ctrlKey);
15639 this.last = last; // reset the last
15640 view.focusRow(rowIndex);
15642 var isSelected = this.isSelected(rowIndex);
15643 if(e.button !== 0 && isSelected){
15644 view.focusRow(rowIndex);
15645 }else if(e.ctrlKey && isSelected){
15646 this.deselectRow(rowIndex);
15647 }else if(!isSelected){
15648 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15649 view.focusRow(rowIndex);
15652 this.fireEvent("afterselectionchange", this);
15655 handleDragableRowClick : function(grid, rowIndex, e)
15657 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15658 this.selectRow(rowIndex, false);
15659 grid.view.focusRow(rowIndex);
15660 this.fireEvent("afterselectionchange", this);
15665 * Selects multiple rows.
15666 * @param {Array} rows Array of the indexes of the row to select
15667 * @param {Boolean} keepExisting (optional) True to keep existing selections
15669 selectRows : function(rows, keepExisting){
15671 this.clearSelections();
15673 for(var i = 0, len = rows.length; i < len; i++){
15674 this.selectRow(rows[i], true);
15679 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15680 * @param {Number} startRow The index of the first row in the range
15681 * @param {Number} endRow The index of the last row in the range
15682 * @param {Boolean} keepExisting (optional) True to retain existing selections
15684 selectRange : function(startRow, endRow, keepExisting){
15685 if(this.locked) return;
15687 this.clearSelections();
15689 if(startRow <= endRow){
15690 for(var i = startRow; i <= endRow; i++){
15691 this.selectRow(i, true);
15694 for(var i = startRow; i >= endRow; i--){
15695 this.selectRow(i, true);
15701 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15702 * @param {Number} startRow The index of the first row in the range
15703 * @param {Number} endRow The index of the last row in the range
15705 deselectRange : function(startRow, endRow, preventViewNotify){
15706 if(this.locked) return;
15707 for(var i = startRow; i <= endRow; i++){
15708 this.deselectRow(i, preventViewNotify);
15714 * @param {Number} row The index of the row to select
15715 * @param {Boolean} keepExisting (optional) True to keep existing selections
15717 selectRow : function(index, keepExisting, preventViewNotify){
15718 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15719 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15720 if(!keepExisting || this.singleSelect){
15721 this.clearSelections();
15723 var r = this.grid.dataSource.getAt(index);
15724 this.selections.add(r);
15725 this.last = this.lastActive = index;
15726 if(!preventViewNotify){
15727 this.grid.getView().onRowSelect(index);
15729 this.fireEvent("rowselect", this, index, r);
15730 this.fireEvent("selectionchange", this);
15736 * @param {Number} row The index of the row to deselect
15738 deselectRow : function(index, preventViewNotify){
15739 if(this.locked) return;
15740 if(this.last == index){
15743 if(this.lastActive == index){
15744 this.lastActive = false;
15746 var r = this.grid.dataSource.getAt(index);
15747 this.selections.remove(r);
15748 if(!preventViewNotify){
15749 this.grid.getView().onRowDeselect(index);
15751 this.fireEvent("rowdeselect", this, index);
15752 this.fireEvent("selectionchange", this);
15756 restoreLast : function(){
15758 this.last = this._last;
15763 acceptsNav : function(row, col, cm){
15764 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15768 onEditorKey : function(field, e){
15769 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15774 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15776 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15778 }else if(k == e.ENTER && !e.ctrlKey){
15782 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15784 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15786 }else if(k == e.ESC){
15790 g.startEditing(newCell[0], newCell[1]);
15801 * @class Roo.bootstrap.MessageBar
15802 * @extends Roo.bootstrap.Component
15803 * Bootstrap MessageBar class
15804 * @cfg {String} html contents of the MessageBar
15805 * @cfg {String} weight (info | success | warning | danger) default info
15806 * @cfg {String} beforeClass insert the bar before the given class
15807 * @cfg {Boolean} closable (true | false) default false
15808 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15811 * Create a new Element
15812 * @param {Object} config The config object
15815 Roo.bootstrap.MessageBar = function(config){
15816 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15819 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15825 beforeClass: 'bootstrap-sticky-wrap',
15827 getAutoCreate : function(){
15831 cls: 'alert alert-dismissable alert-' + this.weight,
15836 html: this.html || ''
15842 cfg.cls += ' alert-messages-fixed';
15856 onRender : function(ct, position)
15858 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15861 var cfg = Roo.apply({}, this.getAutoCreate());
15865 cfg.cls += ' ' + this.cls;
15868 cfg.style = this.style;
15870 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15872 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15875 this.el.select('>button.close').on('click', this.hide, this);
15881 if (!this.rendered) {
15887 this.fireEvent('show', this);
15893 if (!this.rendered) {
15899 this.fireEvent('hide', this);
15902 update : function()
15904 // var e = this.el.dom.firstChild;
15906 // if(this.closable){
15907 // e = e.nextSibling;
15910 // e.data = this.html || '';
15912 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';