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){
3023 renderBody : function()
3033 renderFooter : function()
3045 Roo.log('ds onload');
3050 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3051 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3053 var sortable = e.attr('')
3055 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3056 e.addClass('glyphicon', 'glyphicon-arrow-up');
3059 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3060 e.addClass('glyphicon', 'glyphicon-arrow-down');
3064 var tbody = this.el.select('tbody', true).first();
3068 if(this.store.getCount() > 0){
3069 this.store.data.each(function(d){
3075 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3076 var renderer = cm.getRenderer(i);
3077 var config = cm.config[i];
3081 if(typeof(renderer) !== 'undefined'){
3082 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3085 if(typeof(value) === 'object'){
3095 html: (typeof(value) === 'object') ? '' : value
3098 if(typeof(config.width) != 'undefined'){
3099 td.width = config.width;
3106 tbody.createChild(row);
3114 Roo.each(renders, function(r){
3115 _this.renderColumn(r);
3119 // if(this.loadMask){
3120 // this.maskEl.hide();
3124 onBeforeLoad : function()
3126 Roo.log('ds onBeforeLoad');
3130 // if(this.loadMask){
3131 // this.maskEl.show();
3137 this.el.select('tbody', true).first().dom.innerHTML = '';
3140 getSelectionModel : function(){
3142 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3144 return this.selModel;
3147 renderColumn : function(r)
3150 r.cfg.render(Roo.get(r.id));
3153 Roo.each(r.cfg.cn, function(c){
3158 _this.renderColumn(child);
3175 * @class Roo.bootstrap.TableCell
3176 * @extends Roo.bootstrap.Component
3177 * Bootstrap TableCell class
3178 * @cfg {String} html cell contain text
3179 * @cfg {String} cls cell class
3180 * @cfg {String} tag cell tag (td|th) default td
3181 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3182 * @cfg {String} align Aligns the content in a cell
3183 * @cfg {String} axis Categorizes cells
3184 * @cfg {String} bgcolor Specifies the background color of a cell
3185 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3186 * @cfg {Number} colspan Specifies the number of columns a cell should span
3187 * @cfg {String} headers Specifies one or more header cells a cell is related to
3188 * @cfg {Number} height Sets the height of a cell
3189 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3190 * @cfg {Number} rowspan Sets the number of rows a cell should span
3191 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3192 * @cfg {String} valign Vertical aligns the content in a cell
3193 * @cfg {Number} width Specifies the width of a cell
3196 * Create a new TableCell
3197 * @param {Object} config The config object
3200 Roo.bootstrap.TableCell = function(config){
3201 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3204 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3224 getAutoCreate : function(){
3225 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3245 cfg.align=this.align
3251 cfg.bgcolor=this.bgcolor
3254 cfg.charoff=this.charoff
3257 cfg.colspan=this.colspan
3260 cfg.headers=this.headers
3263 cfg.height=this.height
3266 cfg.nowrap=this.nowrap
3269 cfg.rowspan=this.rowspan
3272 cfg.scope=this.scope
3275 cfg.valign=this.valign
3278 cfg.width=this.width
3297 * @class Roo.bootstrap.TableRow
3298 * @extends Roo.bootstrap.Component
3299 * Bootstrap TableRow class
3300 * @cfg {String} cls row class
3301 * @cfg {String} align Aligns the content in a table row
3302 * @cfg {String} bgcolor Specifies a background color for a table row
3303 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3304 * @cfg {String} valign Vertical aligns the content in a table row
3307 * Create a new TableRow
3308 * @param {Object} config The config object
3311 Roo.bootstrap.TableRow = function(config){
3312 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3315 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3323 getAutoCreate : function(){
3324 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3334 cfg.align = this.align;
3337 cfg.bgcolor = this.bgcolor;
3340 cfg.charoff = this.charoff;
3343 cfg.valign = this.valign;
3361 * @class Roo.bootstrap.TableBody
3362 * @extends Roo.bootstrap.Component
3363 * Bootstrap TableBody class
3364 * @cfg {String} cls element class
3365 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3366 * @cfg {String} align Aligns the content inside the element
3367 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3368 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3371 * Create a new TableBody
3372 * @param {Object} config The config object
3375 Roo.bootstrap.TableBody = function(config){
3376 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3379 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3387 getAutoCreate : function(){
3388 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3402 cfg.align = this.align;
3405 cfg.charoff = this.charoff;
3408 cfg.valign = this.valign;
3415 // initEvents : function()
3422 // this.store = Roo.factory(this.store, Roo.data);
3423 // this.store.on('load', this.onLoad, this);
3425 // this.store.load();
3429 // onLoad: function ()
3431 // this.fireEvent('load', this);
3441 * Ext JS Library 1.1.1
3442 * Copyright(c) 2006-2007, Ext JS, LLC.
3444 * Originally Released Under LGPL - original licence link has changed is not relivant.
3447 * <script type="text/javascript">
3450 // as we use this in bootstrap.
3451 Roo.namespace('Roo.form');
3453 * @class Roo.form.Action
3454 * Internal Class used to handle form actions
3456 * @param {Roo.form.BasicForm} el The form element or its id
3457 * @param {Object} config Configuration options
3462 // define the action interface
3463 Roo.form.Action = function(form, options){
3465 this.options = options || {};
3468 * Client Validation Failed
3471 Roo.form.Action.CLIENT_INVALID = 'client';
3473 * Server Validation Failed
3476 Roo.form.Action.SERVER_INVALID = 'server';
3478 * Connect to Server Failed
3481 Roo.form.Action.CONNECT_FAILURE = 'connect';
3483 * Reading Data from Server Failed
3486 Roo.form.Action.LOAD_FAILURE = 'load';
3488 Roo.form.Action.prototype = {
3490 failureType : undefined,
3491 response : undefined,
3495 run : function(options){
3500 success : function(response){
3505 handleResponse : function(response){
3509 // default connection failure
3510 failure : function(response){
3512 this.response = response;
3513 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3514 this.form.afterAction(this, false);
3517 processResponse : function(response){
3518 this.response = response;
3519 if(!response.responseText){
3522 this.result = this.handleResponse(response);
3526 // utility functions used internally
3527 getUrl : function(appendParams){
3528 var url = this.options.url || this.form.url || this.form.el.dom.action;
3530 var p = this.getParams();
3532 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3538 getMethod : function(){
3539 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3542 getParams : function(){
3543 var bp = this.form.baseParams;
3544 var p = this.options.params;
3546 if(typeof p == "object"){
3547 p = Roo.urlEncode(Roo.applyIf(p, bp));
3548 }else if(typeof p == 'string' && bp){
3549 p += '&' + Roo.urlEncode(bp);
3552 p = Roo.urlEncode(bp);
3557 createCallback : function(){
3559 success: this.success,
3560 failure: this.failure,
3562 timeout: (this.form.timeout*1000),
3563 upload: this.form.fileUpload ? this.success : undefined
3568 Roo.form.Action.Submit = function(form, options){
3569 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3572 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3575 haveProgress : false,
3576 uploadComplete : false,
3578 // uploadProgress indicator.
3579 uploadProgress : function()
3581 if (!this.form.progressUrl) {
3585 if (!this.haveProgress) {
3586 Roo.MessageBox.progress("Uploading", "Uploading");
3588 if (this.uploadComplete) {
3589 Roo.MessageBox.hide();
3593 this.haveProgress = true;
3595 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3597 var c = new Roo.data.Connection();
3599 url : this.form.progressUrl,
3604 success : function(req){
3605 //console.log(data);
3609 rdata = Roo.decode(req.responseText)
3611 Roo.log("Invalid data from server..");
3615 if (!rdata || !rdata.success) {
3617 Roo.MessageBox.alert(Roo.encode(rdata));
3620 var data = rdata.data;
3622 if (this.uploadComplete) {
3623 Roo.MessageBox.hide();
3628 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3629 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3632 this.uploadProgress.defer(2000,this);
3635 failure: function(data) {
3636 Roo.log('progress url failed ');
3647 // run get Values on the form, so it syncs any secondary forms.
3648 this.form.getValues();
3650 var o = this.options;
3651 var method = this.getMethod();
3652 var isPost = method == 'POST';
3653 if(o.clientValidation === false || this.form.isValid()){
3655 if (this.form.progressUrl) {
3656 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3657 (new Date() * 1) + '' + Math.random());
3662 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3663 form:this.form.el.dom,
3664 url:this.getUrl(!isPost),
3666 params:isPost ? this.getParams() : null,
3667 isUpload: this.form.fileUpload
3670 this.uploadProgress();
3672 }else if (o.clientValidation !== false){ // client validation failed
3673 this.failureType = Roo.form.Action.CLIENT_INVALID;
3674 this.form.afterAction(this, false);
3678 success : function(response)
3680 this.uploadComplete= true;
3681 if (this.haveProgress) {
3682 Roo.MessageBox.hide();
3686 var result = this.processResponse(response);
3687 if(result === true || result.success){
3688 this.form.afterAction(this, true);
3692 this.form.markInvalid(result.errors);
3693 this.failureType = Roo.form.Action.SERVER_INVALID;
3695 this.form.afterAction(this, false);
3697 failure : function(response)
3699 this.uploadComplete= true;
3700 if (this.haveProgress) {
3701 Roo.MessageBox.hide();
3704 this.response = response;
3705 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3706 this.form.afterAction(this, false);
3709 handleResponse : function(response){
3710 if(this.form.errorReader){
3711 var rs = this.form.errorReader.read(response);
3714 for(var i = 0, len = rs.records.length; i < len; i++) {
3715 var r = rs.records[i];
3719 if(errors.length < 1){
3723 success : rs.success,
3729 ret = Roo.decode(response.responseText);
3733 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3743 Roo.form.Action.Load = function(form, options){
3744 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3745 this.reader = this.form.reader;
3748 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3753 Roo.Ajax.request(Roo.apply(
3754 this.createCallback(), {
3755 method:this.getMethod(),
3756 url:this.getUrl(false),
3757 params:this.getParams()
3761 success : function(response){
3763 var result = this.processResponse(response);
3764 if(result === true || !result.success || !result.data){
3765 this.failureType = Roo.form.Action.LOAD_FAILURE;
3766 this.form.afterAction(this, false);
3769 this.form.clearInvalid();
3770 this.form.setValues(result.data);
3771 this.form.afterAction(this, true);
3774 handleResponse : function(response){
3775 if(this.form.reader){
3776 var rs = this.form.reader.read(response);
3777 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3779 success : rs.success,
3783 return Roo.decode(response.responseText);
3787 Roo.form.Action.ACTION_TYPES = {
3788 'load' : Roo.form.Action.Load,
3789 'submit' : Roo.form.Action.Submit
3798 * @class Roo.bootstrap.Form
3799 * @extends Roo.bootstrap.Component
3800 * Bootstrap Form class
3801 * @cfg {String} method GET | POST (default POST)
3802 * @cfg {String} labelAlign top | left (default top)
3803 * @cfg {String} align left | right - for navbars
3808 * @param {Object} config The config object
3812 Roo.bootstrap.Form = function(config){
3813 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3816 * @event clientvalidation
3817 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3818 * @param {Form} this
3819 * @param {Boolean} valid true if the form has passed client-side validation
3821 clientvalidation: true,
3823 * @event beforeaction
3824 * Fires before any action is performed. Return false to cancel the action.
3825 * @param {Form} this
3826 * @param {Action} action The action to be performed
3830 * @event actionfailed
3831 * Fires when an action fails.
3832 * @param {Form} this
3833 * @param {Action} action The action that failed
3835 actionfailed : true,
3837 * @event actioncomplete
3838 * Fires when an action is completed.
3839 * @param {Form} this
3840 * @param {Action} action The action that completed
3842 actioncomplete : true
3847 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3850 * @cfg {String} method
3851 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3856 * The URL to use for form actions if one isn't supplied in the action options.
3859 * @cfg {Boolean} fileUpload
3860 * Set to true if this form is a file upload.
3864 * @cfg {Object} baseParams
3865 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3869 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3873 * @cfg {Sting} align (left|right) for navbar forms
3878 activeAction : null,
3881 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3882 * element by passing it or its id or mask the form itself by passing in true.
3885 waitMsgTarget : false,
3890 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3891 * element by passing it or its id or mask the form itself by passing in true.
3895 getAutoCreate : function(){
3899 method : this.method || 'POST',
3900 id : this.id || Roo.id(),
3903 if (this.parent().xtype.match(/^Nav/)) {
3904 cfg.cls = 'navbar-form navbar-' + this.align;
3908 if (this.labelAlign == 'left' ) {
3909 cfg.cls += ' form-horizontal';
3915 initEvents : function()
3917 this.el.on('submit', this.onSubmit, this);
3922 onSubmit : function(e){
3927 * Returns true if client-side validation on the form is successful.
3930 isValid : function(){
3931 var items = this.getItems();
3933 items.each(function(f){
3942 * Returns true if any fields in this form have changed since their original load.
3945 isDirty : function(){
3947 var items = this.getItems();
3948 items.each(function(f){
3958 * Performs a predefined action (submit or load) or custom actions you define on this form.
3959 * @param {String} actionName The name of the action type
3960 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3961 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3962 * accept other config options):
3964 Property Type Description
3965 ---------------- --------------- ----------------------------------------------------------------------------------
3966 url String The url for the action (defaults to the form's url)
3967 method String The form method to use (defaults to the form's method, or POST if not defined)
3968 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3969 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3970 validate the form on the client (defaults to false)
3972 * @return {BasicForm} this
3974 doAction : function(action, options){
3975 if(typeof action == 'string'){
3976 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3978 if(this.fireEvent('beforeaction', this, action) !== false){
3979 this.beforeAction(action);
3980 action.run.defer(100, action);
3986 beforeAction : function(action){
3987 var o = action.options;
3989 // not really supported yet.. ??
3991 //if(this.waitMsgTarget === true){
3992 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3993 //}else if(this.waitMsgTarget){
3994 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3995 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3997 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4003 afterAction : function(action, success){
4004 this.activeAction = null;
4005 var o = action.options;
4007 //if(this.waitMsgTarget === true){
4009 //}else if(this.waitMsgTarget){
4010 // this.waitMsgTarget.unmask();
4012 // Roo.MessageBox.updateProgress(1);
4013 // Roo.MessageBox.hide();
4020 Roo.callback(o.success, o.scope, [this, action]);
4021 this.fireEvent('actioncomplete', this, action);
4025 // failure condition..
4026 // we have a scenario where updates need confirming.
4027 // eg. if a locking scenario exists..
4028 // we look for { errors : { needs_confirm : true }} in the response.
4030 (typeof(action.result) != 'undefined') &&
4031 (typeof(action.result.errors) != 'undefined') &&
4032 (typeof(action.result.errors.needs_confirm) != 'undefined')
4035 Roo.log("not supported yet");
4038 Roo.MessageBox.confirm(
4039 "Change requires confirmation",
4040 action.result.errorMsg,
4045 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4055 Roo.callback(o.failure, o.scope, [this, action]);
4056 // show an error message if no failed handler is set..
4057 if (!this.hasListener('actionfailed')) {
4058 Roo.log("need to add dialog support");
4060 Roo.MessageBox.alert("Error",
4061 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4062 action.result.errorMsg :
4063 "Saving Failed, please check your entries or try again"
4068 this.fireEvent('actionfailed', this, action);
4073 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4074 * @param {String} id The value to search for
4077 findField : function(id){
4078 var items = this.getItems();
4079 var field = items.get(id);
4081 items.each(function(f){
4082 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4089 return field || null;
4092 * Mark fields in this form invalid in bulk.
4093 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4094 * @return {BasicForm} this
4096 markInvalid : function(errors){
4097 if(errors instanceof Array){
4098 for(var i = 0, len = errors.length; i < len; i++){
4099 var fieldError = errors[i];
4100 var f = this.findField(fieldError.id);
4102 f.markInvalid(fieldError.msg);
4108 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4109 field.markInvalid(errors[id]);
4113 //Roo.each(this.childForms || [], function (f) {
4114 // f.markInvalid(errors);
4121 * Set values for fields in this form in bulk.
4122 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4123 * @return {BasicForm} this
4125 setValues : function(values){
4126 if(values instanceof Array){ // array of objects
4127 for(var i = 0, len = values.length; i < len; i++){
4129 var f = this.findField(v.id);
4131 f.setValue(v.value);
4132 if(this.trackResetOnLoad){
4133 f.originalValue = f.getValue();
4137 }else{ // object hash
4140 if(typeof values[id] != 'function' && (field = this.findField(id))){
4142 if (field.setFromData &&
4144 field.displayField &&
4145 // combos' with local stores can
4146 // be queried via setValue()
4147 // to set their value..
4148 (field.store && !field.store.isLocal)
4152 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4153 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4154 field.setFromData(sd);
4157 field.setValue(values[id]);
4161 if(this.trackResetOnLoad){
4162 field.originalValue = field.getValue();
4168 //Roo.each(this.childForms || [], function (f) {
4169 // f.setValues(values);
4176 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4177 * they are returned as an array.
4178 * @param {Boolean} asString
4181 getValues : function(asString){
4182 //if (this.childForms) {
4183 // copy values from the child forms
4184 // Roo.each(this.childForms, function (f) {
4185 // this.setValues(f.getValues());
4191 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4192 if(asString === true){
4195 return Roo.urlDecode(fs);
4199 * Returns the fields in this form as an object with key/value pairs.
4200 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4203 getFieldValues : function(with_hidden)
4205 var items = this.getItems();
4207 items.each(function(f){
4211 var v = f.getValue();
4212 if (f.inputType =='radio') {
4213 if (typeof(ret[f.getName()]) == 'undefined') {
4214 ret[f.getName()] = ''; // empty..
4217 if (!f.el.dom.checked) {
4225 // not sure if this supported any more..
4226 if ((typeof(v) == 'object') && f.getRawValue) {
4227 v = f.getRawValue() ; // dates..
4229 // combo boxes where name != hiddenName...
4230 if (f.name != f.getName()) {
4231 ret[f.name] = f.getRawValue();
4233 ret[f.getName()] = v;
4240 * Clears all invalid messages in this form.
4241 * @return {BasicForm} this
4243 clearInvalid : function(){
4244 var items = this.getItems();
4246 items.each(function(f){
4257 * @return {BasicForm} this
4260 var items = this.getItems();
4261 items.each(function(f){
4265 Roo.each(this.childForms || [], function (f) {
4272 getItems : function()
4274 var r=new Roo.util.MixedCollection(false, function(o){
4275 return o.id || (o.id = Roo.id());
4277 var iter = function(el) {
4284 Roo.each(el.items,function(e) {
4303 * Ext JS Library 1.1.1
4304 * Copyright(c) 2006-2007, Ext JS, LLC.
4306 * Originally Released Under LGPL - original licence link has changed is not relivant.
4309 * <script type="text/javascript">
4312 * @class Roo.form.VTypes
4313 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4316 Roo.form.VTypes = function(){
4317 // closure these in so they are only created once.
4318 var alpha = /^[a-zA-Z_]+$/;
4319 var alphanum = /^[a-zA-Z0-9_]+$/;
4320 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4321 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4323 // All these messages and functions are configurable
4326 * The function used to validate email addresses
4327 * @param {String} value The email address
4329 'email' : function(v){
4330 return email.test(v);
4333 * The error text to display when the email validation function returns false
4336 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4338 * The keystroke filter mask to be applied on email input
4341 'emailMask' : /[a-z0-9_\.\-@]/i,
4344 * The function used to validate URLs
4345 * @param {String} value The URL
4347 'url' : function(v){
4351 * The error text to display when the url validation function returns false
4354 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4357 * The function used to validate alpha values
4358 * @param {String} value The value
4360 'alpha' : function(v){
4361 return alpha.test(v);
4364 * The error text to display when the alpha validation function returns false
4367 'alphaText' : 'This field should only contain letters and _',
4369 * The keystroke filter mask to be applied on alpha input
4372 'alphaMask' : /[a-z_]/i,
4375 * The function used to validate alphanumeric values
4376 * @param {String} value The value
4378 'alphanum' : function(v){
4379 return alphanum.test(v);
4382 * The error text to display when the alphanumeric validation function returns false
4385 'alphanumText' : 'This field should only contain letters, numbers and _',
4387 * The keystroke filter mask to be applied on alphanumeric input
4390 'alphanumMask' : /[a-z0-9_]/i
4400 * @class Roo.bootstrap.Input
4401 * @extends Roo.bootstrap.Component
4402 * Bootstrap Input class
4403 * @cfg {Boolean} disabled is it disabled
4404 * @cfg {String} fieldLabel - the label associated
4405 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4406 * @cfg {String} name name of the input
4407 * @cfg {string} fieldLabel - the label associated
4408 * @cfg {string} inputType - input / file submit ...
4409 * @cfg {string} placeholder - placeholder to put in text.
4410 * @cfg {string} before - input group add on before
4411 * @cfg {string} after - input group add on after
4412 * @cfg {string} size - (lg|sm) or leave empty..
4413 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4414 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4415 * @cfg {Number} md colspan out of 12 for computer-sized screens
4416 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4417 * @cfg {string} value default value of the input
4418 * @cfg {Number} labelWidth set the width of label (0-12)
4419 * @cfg {String} labelAlign (top|left)
4420 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4424 * Create a new Input
4425 * @param {Object} config The config object
4428 Roo.bootstrap.Input = function(config){
4429 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4434 * Fires when this field receives input focus.
4435 * @param {Roo.form.Field} this
4440 * Fires when this field loses input focus.
4441 * @param {Roo.form.Field} this
4446 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4447 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4448 * @param {Roo.form.Field} this
4449 * @param {Roo.EventObject} e The event object
4454 * Fires just before the field blurs if the field value has changed.
4455 * @param {Roo.form.Field} this
4456 * @param {Mixed} newValue The new value
4457 * @param {Mixed} oldValue The original value
4462 * Fires after the field has been marked as invalid.
4463 * @param {Roo.form.Field} this
4464 * @param {String} msg The validation message
4469 * Fires after the field has been validated with no errors.
4470 * @param {Roo.form.Field} this
4475 * Fires after the key up
4476 * @param {Roo.form.Field} this
4477 * @param {Roo.EventObject} e The event Object
4483 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4485 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4486 automatic validation (defaults to "keyup").
4488 validationEvent : "keyup",
4490 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4492 validateOnBlur : true,
4494 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4496 validationDelay : 250,
4498 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4500 focusClass : "x-form-focus", // not needed???
4504 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4506 invalidClass : "has-error",
4509 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4511 selectOnFocus : false,
4514 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4518 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4523 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4525 disableKeyFilter : false,
4528 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4532 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4536 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4538 blankText : "This field is required",
4541 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4545 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4547 maxLength : Number.MAX_VALUE,
4549 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4551 minLengthText : "The minimum length for this field is {0}",
4553 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4555 maxLengthText : "The maximum length for this field is {0}",
4559 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4560 * If available, this function will be called only after the basic validators all return true, and will be passed the
4561 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4565 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4566 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4567 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4571 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4594 parentLabelAlign : function()
4597 while (parent.parent()) {
4598 parent = parent.parent();
4599 if (typeof(parent.labelAlign) !='undefined') {
4600 return parent.labelAlign;
4607 getAutoCreate : function(){
4609 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4615 if(this.inputType != 'hidden'){
4616 cfg.cls = 'form-group' //input-group
4622 type : this.inputType,
4624 cls : 'form-control',
4625 placeholder : this.placeholder || ''
4629 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4630 input.maxLength = this.maxLength;
4633 if (this.disabled) {
4634 input.disabled=true;
4637 if (this.readOnly) {
4638 input.readonly=true;
4642 input.name = this.name;
4645 input.cls += ' input-' + this.size;
4648 ['xs','sm','md','lg'].map(function(size){
4649 if (settings[size]) {
4650 cfg.cls += ' col-' + size + '-' + settings[size];
4654 var inputblock = input;
4656 if (this.before || this.after) {
4659 cls : 'input-group',
4663 inputblock.cn.push({
4665 cls : 'input-group-addon',
4669 inputblock.cn.push(input);
4671 inputblock.cn.push({
4673 cls : 'input-group-addon',
4680 if (align ==='left' && this.fieldLabel.length) {
4681 Roo.log("left and has label");
4687 cls : 'control-label col-sm-' + this.labelWidth,
4688 html : this.fieldLabel
4692 cls : "col-sm-" + (12 - this.labelWidth),
4699 } else if ( this.fieldLabel.length) {
4705 //cls : 'input-group-addon',
4706 html : this.fieldLabel
4716 Roo.log(" no label && no align");
4725 Roo.log('input-parentType: ' + this.parentType);
4727 if (this.parentType === 'Navbar' && this.parent().bar) {
4728 cfg.cls += ' navbar-form';
4736 * return the real input element.
4738 inputEl: function ()
4740 return this.el.select('input.form-control',true).first();
4742 setDisabled : function(v)
4744 var i = this.inputEl().dom;
4746 i.removeAttribute('disabled');
4750 i.setAttribute('disabled','true');
4752 initEvents : function()
4755 this.inputEl().on("keydown" , this.fireKey, this);
4756 this.inputEl().on("focus", this.onFocus, this);
4757 this.inputEl().on("blur", this.onBlur, this);
4759 this.inputEl().relayEvent('keyup', this);
4761 // reference to original value for reset
4762 this.originalValue = this.getValue();
4763 //Roo.form.TextField.superclass.initEvents.call(this);
4764 if(this.validationEvent == 'keyup'){
4765 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4766 this.inputEl().on('keyup', this.filterValidation, this);
4768 else if(this.validationEvent !== false){
4769 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4772 if(this.selectOnFocus){
4773 this.on("focus", this.preFocus, this);
4776 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4777 this.inputEl().on("keypress", this.filterKeys, this);
4780 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4781 this.el.on("click", this.autoSize, this);
4784 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4785 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4789 filterValidation : function(e){
4790 if(!e.isNavKeyPress()){
4791 this.validationTask.delay(this.validationDelay);
4795 * Validates the field value
4796 * @return {Boolean} True if the value is valid, else false
4798 validate : function(){
4799 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4800 if(this.disabled || this.validateValue(this.getRawValue())){
4801 this.clearInvalid();
4809 * Validates a value according to the field's validation rules and marks the field as invalid
4810 * if the validation fails
4811 * @param {Mixed} value The value to validate
4812 * @return {Boolean} True if the value is valid, else false
4814 validateValue : function(value){
4815 if(value.length < 1) { // if it's blank
4816 if(this.allowBlank){
4817 this.clearInvalid();
4820 this.markInvalid(this.blankText);
4824 if(value.length < this.minLength){
4825 this.markInvalid(String.format(this.minLengthText, this.minLength));
4828 if(value.length > this.maxLength){
4829 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4833 var vt = Roo.form.VTypes;
4834 if(!vt[this.vtype](value, this)){
4835 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4839 if(typeof this.validator == "function"){
4840 var msg = this.validator(value);
4842 this.markInvalid(msg);
4846 if(this.regex && !this.regex.test(value)){
4847 this.markInvalid(this.regexText);
4856 fireKey : function(e){
4857 //Roo.log('field ' + e.getKey());
4858 if(e.isNavKeyPress()){
4859 this.fireEvent("specialkey", this, e);
4862 focus : function (selectText){
4864 this.inputEl().focus();
4865 if(selectText === true){
4866 this.inputEl().dom.select();
4872 onFocus : function(){
4873 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4874 // this.el.addClass(this.focusClass);
4877 this.hasFocus = true;
4878 this.startValue = this.getValue();
4879 this.fireEvent("focus", this);
4883 beforeBlur : Roo.emptyFn,
4887 onBlur : function(){
4889 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4890 //this.el.removeClass(this.focusClass);
4892 this.hasFocus = false;
4893 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4896 var v = this.getValue();
4897 if(String(v) !== String(this.startValue)){
4898 this.fireEvent('change', this, v, this.startValue);
4900 this.fireEvent("blur", this);
4904 * Resets the current field value to the originally loaded value and clears any validation messages
4907 this.setValue(this.originalValue);
4908 this.clearInvalid();
4911 * Returns the name of the field
4912 * @return {Mixed} name The name field
4914 getName: function(){
4918 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4919 * @return {Mixed} value The field value
4921 getValue : function(){
4922 return this.inputEl().getValue();
4925 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4926 * @return {Mixed} value The field value
4928 getRawValue : function(){
4929 var v = this.inputEl().getValue();
4935 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4936 * @param {Mixed} value The value to set
4938 setRawValue : function(v){
4939 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4942 selectText : function(start, end){
4943 var v = this.getRawValue();
4945 start = start === undefined ? 0 : start;
4946 end = end === undefined ? v.length : end;
4947 var d = this.inputEl().dom;
4948 if(d.setSelectionRange){
4949 d.setSelectionRange(start, end);
4950 }else if(d.createTextRange){
4951 var range = d.createTextRange();
4952 range.moveStart("character", start);
4953 range.moveEnd("character", v.length-end);
4960 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4961 * @param {Mixed} value The value to set
4963 setValue : function(v){
4966 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4972 processValue : function(value){
4973 if(this.stripCharsRe){
4974 var newValue = value.replace(this.stripCharsRe, '');
4975 if(newValue !== value){
4976 this.setRawValue(newValue);
4983 preFocus : function(){
4985 if(this.selectOnFocus){
4986 this.inputEl().dom.select();
4989 filterKeys : function(e){
4991 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4994 var c = e.getCharCode(), cc = String.fromCharCode(c);
4995 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4998 if(!this.maskRe.test(cc)){
5003 * Clear any invalid styles/messages for this field
5005 clearInvalid : function(){
5007 if(!this.el || this.preventMark){ // not rendered
5010 this.el.removeClass(this.invalidClass);
5012 switch(this.msgTarget){
5014 this.el.dom.qtip = '';
5017 this.el.dom.title = '';
5021 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5026 this.errorIcon.dom.qtip = '';
5027 this.errorIcon.hide();
5028 this.un('resize', this.alignErrorIcon, this);
5032 var t = Roo.getDom(this.msgTarget);
5034 t.style.display = 'none';
5038 this.fireEvent('valid', this);
5041 * Mark this field as invalid
5042 * @param {String} msg The validation message
5044 markInvalid : function(msg){
5045 if(!this.el || this.preventMark){ // not rendered
5048 this.el.addClass(this.invalidClass);
5050 msg = msg || this.invalidText;
5051 switch(this.msgTarget){
5053 this.el.dom.qtip = msg;
5054 this.el.dom.qclass = 'x-form-invalid-tip';
5055 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5056 Roo.QuickTips.enable();
5060 this.el.dom.title = msg;
5064 var elp = this.el.findParent('.x-form-element', 5, true);
5065 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5066 this.errorEl.setWidth(elp.getWidth(true)-20);
5068 this.errorEl.update(msg);
5069 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5072 if(!this.errorIcon){
5073 var elp = this.el.findParent('.x-form-element', 5, true);
5074 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5076 this.alignErrorIcon();
5077 this.errorIcon.dom.qtip = msg;
5078 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5079 this.errorIcon.show();
5080 this.on('resize', this.alignErrorIcon, this);
5083 var t = Roo.getDom(this.msgTarget);
5085 t.style.display = this.msgDisplay;
5089 this.fireEvent('invalid', this, msg);
5092 SafariOnKeyDown : function(event)
5094 // this is a workaround for a password hang bug on chrome/ webkit.
5096 var isSelectAll = false;
5098 if(this.inputEl().dom.selectionEnd > 0){
5099 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5101 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5102 event.preventDefault();
5107 if(isSelectAll){ // backspace and delete key
5109 event.preventDefault();
5110 // this is very hacky as keydown always get's upper case.
5112 var cc = String.fromCharCode(event.getCharCode());
5113 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5117 adjustWidth : function(tag, w){
5118 tag = tag.toLowerCase();
5119 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5120 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5124 if(tag == 'textarea'){
5127 }else if(Roo.isOpera){
5131 if(tag == 'textarea'){
5150 * @class Roo.bootstrap.TextArea
5151 * @extends Roo.bootstrap.Input
5152 * Bootstrap TextArea class
5153 * @cfg {Number} cols Specifies the visible width of a text area
5154 * @cfg {Number} rows Specifies the visible number of lines in a text area
5155 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5156 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5157 * @cfg {string} html text
5160 * Create a new TextArea
5161 * @param {Object} config The config object
5164 Roo.bootstrap.TextArea = function(config){
5165 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5169 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5179 getAutoCreate : function(){
5181 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5192 value : this.value || '',
5193 html: this.html || '',
5194 cls : 'form-control',
5195 placeholder : this.placeholder || ''
5199 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5200 input.maxLength = this.maxLength;
5204 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5208 input.cols = this.cols;
5211 if (this.readOnly) {
5212 input.readonly = true;
5216 input.name = this.name;
5220 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5224 ['xs','sm','md','lg'].map(function(size){
5225 if (settings[size]) {
5226 cfg.cls += ' col-' + size + '-' + settings[size];
5230 var inputblock = input;
5232 if (this.before || this.after) {
5235 cls : 'input-group',
5239 inputblock.cn.push({
5241 cls : 'input-group-addon',
5245 inputblock.cn.push(input);
5247 inputblock.cn.push({
5249 cls : 'input-group-addon',
5256 if (align ==='left' && this.fieldLabel.length) {
5257 Roo.log("left and has label");
5263 cls : 'control-label col-sm-' + this.labelWidth,
5264 html : this.fieldLabel
5268 cls : "col-sm-" + (12 - this.labelWidth),
5275 } else if ( this.fieldLabel.length) {
5281 //cls : 'input-group-addon',
5282 html : this.fieldLabel
5292 Roo.log(" no label && no align");
5302 if (this.disabled) {
5303 input.disabled=true;
5310 * return the real textarea element.
5312 inputEl: function ()
5314 return this.el.select('textarea.form-control',true).first();
5322 * trigger field - base class for combo..
5327 * @class Roo.bootstrap.TriggerField
5328 * @extends Roo.bootstrap.Input
5329 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5330 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5331 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5332 * for which you can provide a custom implementation. For example:
5334 var trigger = new Roo.bootstrap.TriggerField();
5335 trigger.onTriggerClick = myTriggerFn;
5336 trigger.applyTo('my-field');
5339 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5340 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5341 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5342 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5344 * Create a new TriggerField.
5345 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5346 * to the base TextField)
5348 Roo.bootstrap.TriggerField = function(config){
5349 this.mimicing = false;
5350 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5353 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5355 * @cfg {String} triggerClass A CSS class to apply to the trigger
5358 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5362 /** @cfg {Boolean} grow @hide */
5363 /** @cfg {Number} growMin @hide */
5364 /** @cfg {Number} growMax @hide */
5370 autoSize: Roo.emptyFn,
5377 actionMode : 'wrap',
5381 getAutoCreate : function(){
5383 var parent = this.parent();
5385 var align = this.parentLabelAlign();
5390 cls: 'form-group' //input-group
5397 type : this.inputType,
5398 cls : 'form-control',
5399 autocomplete: 'off',
5400 placeholder : this.placeholder || ''
5404 input.name = this.name;
5407 input.cls += ' input-' + this.size;
5410 if (this.disabled) {
5411 input.disabled=true;
5414 var inputblock = input;
5416 if (this.before || this.after) {
5419 cls : 'input-group',
5423 inputblock.cn.push({
5425 cls : 'input-group-addon',
5429 inputblock.cn.push(input);
5431 inputblock.cn.push({
5433 cls : 'input-group-addon',
5446 cls: 'form-hidden-field'
5454 Roo.log('multiple');
5462 cls: 'form-hidden-field'
5466 cls: 'select2-choices',
5470 cls: 'select2-search-field',
5483 cls: 'select2-container input-group',
5488 cls: 'typeahead typeahead-long dropdown-menu',
5489 style: 'display:none'
5497 cls : 'input-group-addon btn dropdown-toggle',
5505 cls: 'combobox-clear',
5519 combobox.cls += ' select2-container-multi';
5522 if (align ==='left' && this.fieldLabel.length) {
5524 Roo.log("left and has label");
5530 cls : 'control-label col-sm-' + this.labelWidth,
5531 html : this.fieldLabel
5535 cls : "col-sm-" + (12 - this.labelWidth),
5542 } else if ( this.fieldLabel.length) {
5548 //cls : 'input-group-addon',
5549 html : this.fieldLabel
5559 Roo.log(" no label && no align");
5566 ['xs','sm','md','lg'].map(function(size){
5567 if (settings[size]) {
5568 cfg.cls += ' col-' + size + '-' + settings[size];
5579 onResize : function(w, h){
5580 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5581 // if(typeof w == 'number'){
5582 // var x = w - this.trigger.getWidth();
5583 // this.inputEl().setWidth(this.adjustWidth('input', x));
5584 // this.trigger.setStyle('left', x+'px');
5589 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5592 getResizeEl : function(){
5593 return this.inputEl();
5597 getPositionEl : function(){
5598 return this.inputEl();
5602 alignErrorIcon : function(){
5603 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5607 initEvents : function(){
5609 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5610 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5612 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5613 if(this.hideTrigger){
5614 this.trigger.setDisplayed(false);
5616 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5620 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5623 //this.trigger.addClassOnOver('x-form-trigger-over');
5624 //this.trigger.addClassOnClick('x-form-trigger-click');
5627 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5632 initTrigger : function(){
5637 onDestroy : function(){
5639 this.trigger.removeAllListeners();
5640 // this.trigger.remove();
5643 // this.wrap.remove();
5645 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5649 onFocus : function(){
5650 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5653 this.wrap.addClass('x-trigger-wrap-focus');
5654 this.mimicing = true;
5655 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5656 if(this.monitorTab){
5657 this.el.on("keydown", this.checkTab, this);
5664 checkTab : function(e){
5665 if(e.getKey() == e.TAB){
5671 onBlur : function(){
5676 mimicBlur : function(e, t){
5678 if(!this.wrap.contains(t) && this.validateBlur()){
5685 triggerBlur : function(){
5686 this.mimicing = false;
5687 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5688 if(this.monitorTab){
5689 this.el.un("keydown", this.checkTab, this);
5691 //this.wrap.removeClass('x-trigger-wrap-focus');
5692 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5696 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5697 validateBlur : function(e, t){
5702 onDisable : function(){
5703 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5705 // this.wrap.addClass('x-item-disabled');
5710 onEnable : function(){
5711 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5713 // this.el.removeClass('x-item-disabled');
5718 onShow : function(){
5719 var ae = this.getActionEl();
5722 ae.dom.style.display = '';
5723 ae.dom.style.visibility = 'visible';
5729 onHide : function(){
5730 var ae = this.getActionEl();
5731 ae.dom.style.display = 'none';
5735 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5736 * by an implementing function.
5738 * @param {EventObject} e
5740 onTriggerClick : Roo.emptyFn
5744 * Ext JS Library 1.1.1
5745 * Copyright(c) 2006-2007, Ext JS, LLC.
5747 * Originally Released Under LGPL - original licence link has changed is not relivant.
5750 * <script type="text/javascript">
5755 * @class Roo.data.SortTypes
5757 * Defines the default sorting (casting?) comparison functions used when sorting data.
5759 Roo.data.SortTypes = {
5761 * Default sort that does nothing
5762 * @param {Mixed} s The value being converted
5763 * @return {Mixed} The comparison value
5770 * The regular expression used to strip tags
5774 stripTagsRE : /<\/?[^>]+>/gi,
5777 * Strips all HTML tags to sort on text only
5778 * @param {Mixed} s The value being converted
5779 * @return {String} The comparison value
5781 asText : function(s){
5782 return String(s).replace(this.stripTagsRE, "");
5786 * Strips all HTML tags to sort on text only - Case insensitive
5787 * @param {Mixed} s The value being converted
5788 * @return {String} The comparison value
5790 asUCText : function(s){
5791 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5795 * Case insensitive string
5796 * @param {Mixed} s The value being converted
5797 * @return {String} The comparison value
5799 asUCString : function(s) {
5800 return String(s).toUpperCase();
5805 * @param {Mixed} s The value being converted
5806 * @return {Number} The comparison value
5808 asDate : function(s) {
5812 if(s instanceof Date){
5815 return Date.parse(String(s));
5820 * @param {Mixed} s The value being converted
5821 * @return {Float} The comparison value
5823 asFloat : function(s) {
5824 var val = parseFloat(String(s).replace(/,/g, ""));
5825 if(isNaN(val)) val = 0;
5831 * @param {Mixed} s The value being converted
5832 * @return {Number} The comparison value
5834 asInt : function(s) {
5835 var val = parseInt(String(s).replace(/,/g, ""));
5836 if(isNaN(val)) val = 0;
5841 * Ext JS Library 1.1.1
5842 * Copyright(c) 2006-2007, Ext JS, LLC.
5844 * Originally Released Under LGPL - original licence link has changed is not relivant.
5847 * <script type="text/javascript">
5851 * @class Roo.data.Record
5852 * Instances of this class encapsulate both record <em>definition</em> information, and record
5853 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5854 * to access Records cached in an {@link Roo.data.Store} object.<br>
5856 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5857 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5860 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5862 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5863 * {@link #create}. The parameters are the same.
5864 * @param {Array} data An associative Array of data values keyed by the field name.
5865 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5866 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5867 * not specified an integer id is generated.
5869 Roo.data.Record = function(data, id){
5870 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5875 * Generate a constructor for a specific record layout.
5876 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5877 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5878 * Each field definition object may contain the following properties: <ul>
5879 * <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,
5880 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5881 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5882 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5883 * is being used, then this is a string containing the javascript expression to reference the data relative to
5884 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5885 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5886 * this may be omitted.</p></li>
5887 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5888 * <ul><li>auto (Default, implies no conversion)</li>
5893 * <li>date</li></ul></p></li>
5894 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5895 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5896 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5897 * by the Reader into an object that will be stored in the Record. It is passed the
5898 * following parameters:<ul>
5899 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5901 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5903 * <br>usage:<br><pre><code>
5904 var TopicRecord = Roo.data.Record.create(
5905 {name: 'title', mapping: 'topic_title'},
5906 {name: 'author', mapping: 'username'},
5907 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5908 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5909 {name: 'lastPoster', mapping: 'user2'},
5910 {name: 'excerpt', mapping: 'post_text'}
5913 var myNewRecord = new TopicRecord({
5914 title: 'Do my job please',
5917 lastPost: new Date(),
5918 lastPoster: 'Animal',
5919 excerpt: 'No way dude!'
5921 myStore.add(myNewRecord);
5926 Roo.data.Record.create = function(o){
5928 f.superclass.constructor.apply(this, arguments);
5930 Roo.extend(f, Roo.data.Record);
5931 var p = f.prototype;
5932 p.fields = new Roo.util.MixedCollection(false, function(field){
5935 for(var i = 0, len = o.length; i < len; i++){
5936 p.fields.add(new Roo.data.Field(o[i]));
5938 f.getField = function(name){
5939 return p.fields.get(name);
5944 Roo.data.Record.AUTO_ID = 1000;
5945 Roo.data.Record.EDIT = 'edit';
5946 Roo.data.Record.REJECT = 'reject';
5947 Roo.data.Record.COMMIT = 'commit';
5949 Roo.data.Record.prototype = {
5951 * Readonly flag - true if this record has been modified.
5960 join : function(store){
5965 * Set the named field to the specified value.
5966 * @param {String} name The name of the field to set.
5967 * @param {Object} value The value to set the field to.
5969 set : function(name, value){
5970 if(this.data[name] == value){
5977 if(typeof this.modified[name] == 'undefined'){
5978 this.modified[name] = this.data[name];
5980 this.data[name] = value;
5981 if(!this.editing && this.store){
5982 this.store.afterEdit(this);
5987 * Get the value of the named field.
5988 * @param {String} name The name of the field to get the value of.
5989 * @return {Object} The value of the field.
5991 get : function(name){
5992 return this.data[name];
5996 beginEdit : function(){
5997 this.editing = true;
6002 cancelEdit : function(){
6003 this.editing = false;
6004 delete this.modified;
6008 endEdit : function(){
6009 this.editing = false;
6010 if(this.dirty && this.store){
6011 this.store.afterEdit(this);
6016 * Usually called by the {@link Roo.data.Store} which owns the Record.
6017 * Rejects all changes made to the Record since either creation, or the last commit operation.
6018 * Modified fields are reverted to their original values.
6020 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6021 * of reject operations.
6023 reject : function(){
6024 var m = this.modified;
6026 if(typeof m[n] != "function"){
6027 this.data[n] = m[n];
6031 delete this.modified;
6032 this.editing = false;
6034 this.store.afterReject(this);
6039 * Usually called by the {@link Roo.data.Store} which owns the Record.
6040 * Commits all changes made to the Record since either creation, or the last commit operation.
6042 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6043 * of commit operations.
6045 commit : function(){
6047 delete this.modified;
6048 this.editing = false;
6050 this.store.afterCommit(this);
6055 hasError : function(){
6056 return this.error != null;
6060 clearError : function(){
6065 * Creates a copy of this record.
6066 * @param {String} id (optional) A new record id if you don't want to use this record's id
6069 copy : function(newId) {
6070 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6074 * Ext JS Library 1.1.1
6075 * Copyright(c) 2006-2007, Ext JS, LLC.
6077 * Originally Released Under LGPL - original licence link has changed is not relivant.
6080 * <script type="text/javascript">
6086 * @class Roo.data.Store
6087 * @extends Roo.util.Observable
6088 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6089 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6091 * 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
6092 * has no knowledge of the format of the data returned by the Proxy.<br>
6094 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6095 * instances from the data object. These records are cached and made available through accessor functions.
6097 * Creates a new Store.
6098 * @param {Object} config A config object containing the objects needed for the Store to access data,
6099 * and read the data into Records.
6101 Roo.data.Store = function(config){
6102 this.data = new Roo.util.MixedCollection(false);
6103 this.data.getKey = function(o){
6106 this.baseParams = {};
6113 "multisort" : "_multisort"
6116 if(config && config.data){
6117 this.inlineData = config.data;
6121 Roo.apply(this, config);
6123 if(this.reader){ // reader passed
6124 this.reader = Roo.factory(this.reader, Roo.data);
6125 this.reader.xmodule = this.xmodule || false;
6126 if(!this.recordType){
6127 this.recordType = this.reader.recordType;
6129 if(this.reader.onMetaChange){
6130 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6134 if(this.recordType){
6135 this.fields = this.recordType.prototype.fields;
6141 * @event datachanged
6142 * Fires when the data cache has changed, and a widget which is using this Store
6143 * as a Record cache should refresh its view.
6144 * @param {Store} this
6149 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6150 * @param {Store} this
6151 * @param {Object} meta The JSON metadata
6156 * Fires when Records have been added to the Store
6157 * @param {Store} this
6158 * @param {Roo.data.Record[]} records The array of Records added
6159 * @param {Number} index The index at which the record(s) were added
6164 * Fires when a Record has been removed from the Store
6165 * @param {Store} this
6166 * @param {Roo.data.Record} record The Record that was removed
6167 * @param {Number} index The index at which the record was removed
6172 * Fires when a Record has been updated
6173 * @param {Store} this
6174 * @param {Roo.data.Record} record The Record that was updated
6175 * @param {String} operation The update operation being performed. Value may be one of:
6177 Roo.data.Record.EDIT
6178 Roo.data.Record.REJECT
6179 Roo.data.Record.COMMIT
6185 * Fires when the data cache has been cleared.
6186 * @param {Store} this
6191 * Fires before a request is made for a new data object. If the beforeload handler returns false
6192 * the load action will be canceled.
6193 * @param {Store} this
6194 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6198 * @event beforeloadadd
6199 * Fires after a new set of Records has been loaded.
6200 * @param {Store} this
6201 * @param {Roo.data.Record[]} records The Records that were loaded
6202 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6204 beforeloadadd : true,
6207 * Fires after a new set of Records has been loaded, before they are added to the store.
6208 * @param {Store} this
6209 * @param {Roo.data.Record[]} records The Records that were loaded
6210 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6211 * @params {Object} return from reader
6215 * @event loadexception
6216 * Fires if an exception occurs in the Proxy during loading.
6217 * Called with the signature of the Proxy's "loadexception" event.
6218 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6221 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6222 * @param {Object} load options
6223 * @param {Object} jsonData from your request (normally this contains the Exception)
6225 loadexception : true
6229 this.proxy = Roo.factory(this.proxy, Roo.data);
6230 this.proxy.xmodule = this.xmodule || false;
6231 this.relayEvents(this.proxy, ["loadexception"]);
6233 this.sortToggle = {};
6234 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6236 Roo.data.Store.superclass.constructor.call(this);
6238 if(this.inlineData){
6239 this.loadData(this.inlineData);
6240 delete this.inlineData;
6244 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6246 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6247 * without a remote query - used by combo/forms at present.
6251 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6254 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6257 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6258 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6261 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6262 * on any HTTP request
6265 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6268 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6272 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6273 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6278 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6279 * loaded or when a record is removed. (defaults to false).
6281 pruneModifiedRecords : false,
6287 * Add Records to the Store and fires the add event.
6288 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6290 add : function(records){
6291 records = [].concat(records);
6292 for(var i = 0, len = records.length; i < len; i++){
6293 records[i].join(this);
6295 var index = this.data.length;
6296 this.data.addAll(records);
6297 this.fireEvent("add", this, records, index);
6301 * Remove a Record from the Store and fires the remove event.
6302 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6304 remove : function(record){
6305 var index = this.data.indexOf(record);
6306 this.data.removeAt(index);
6307 if(this.pruneModifiedRecords){
6308 this.modified.remove(record);
6310 this.fireEvent("remove", this, record, index);
6314 * Remove all Records from the Store and fires the clear event.
6316 removeAll : function(){
6318 if(this.pruneModifiedRecords){
6321 this.fireEvent("clear", this);
6325 * Inserts Records to the Store at the given index and fires the add event.
6326 * @param {Number} index The start index at which to insert the passed Records.
6327 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6329 insert : function(index, records){
6330 records = [].concat(records);
6331 for(var i = 0, len = records.length; i < len; i++){
6332 this.data.insert(index, records[i]);
6333 records[i].join(this);
6335 this.fireEvent("add", this, records, index);
6339 * Get the index within the cache of the passed Record.
6340 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6341 * @return {Number} The index of the passed Record. Returns -1 if not found.
6343 indexOf : function(record){
6344 return this.data.indexOf(record);
6348 * Get the index within the cache of the Record with the passed id.
6349 * @param {String} id The id of the Record to find.
6350 * @return {Number} The index of the Record. Returns -1 if not found.
6352 indexOfId : function(id){
6353 return this.data.indexOfKey(id);
6357 * Get the Record with the specified id.
6358 * @param {String} id The id of the Record to find.
6359 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6361 getById : function(id){
6362 return this.data.key(id);
6366 * Get the Record at the specified index.
6367 * @param {Number} index The index of the Record to find.
6368 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6370 getAt : function(index){
6371 return this.data.itemAt(index);
6375 * Returns a range of Records between specified indices.
6376 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6377 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6378 * @return {Roo.data.Record[]} An array of Records
6380 getRange : function(start, end){
6381 return this.data.getRange(start, end);
6385 storeOptions : function(o){
6386 o = Roo.apply({}, o);
6389 this.lastOptions = o;
6393 * Loads the Record cache from the configured Proxy using the configured Reader.
6395 * If using remote paging, then the first load call must specify the <em>start</em>
6396 * and <em>limit</em> properties in the options.params property to establish the initial
6397 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6399 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6400 * and this call will return before the new data has been loaded. Perform any post-processing
6401 * in a callback function, or in a "load" event handler.</strong>
6403 * @param {Object} options An object containing properties which control loading options:<ul>
6404 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6405 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6406 * passed the following arguments:<ul>
6407 * <li>r : Roo.data.Record[]</li>
6408 * <li>options: Options object from the load call</li>
6409 * <li>success: Boolean success indicator</li></ul></li>
6410 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6411 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6414 load : function(options){
6415 options = options || {};
6416 if(this.fireEvent("beforeload", this, options) !== false){
6417 this.storeOptions(options);
6418 var p = Roo.apply(options.params || {}, this.baseParams);
6419 // if meta was not loaded from remote source.. try requesting it.
6420 if (!this.reader.metaFromRemote) {
6423 if(this.sortInfo && this.remoteSort){
6424 var pn = this.paramNames;
6425 p[pn["sort"]] = this.sortInfo.field;
6426 p[pn["dir"]] = this.sortInfo.direction;
6428 if (this.multiSort) {
6429 var pn = this.paramNames;
6430 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6433 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6438 * Reloads the Record cache from the configured Proxy using the configured Reader and
6439 * the options from the last load operation performed.
6440 * @param {Object} options (optional) An object containing properties which may override the options
6441 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6442 * the most recently used options are reused).
6444 reload : function(options){
6445 this.load(Roo.applyIf(options||{}, this.lastOptions));
6449 // Called as a callback by the Reader during a load operation.
6450 loadRecords : function(o, options, success){
6451 if(!o || success === false){
6452 if(success !== false){
6453 this.fireEvent("load", this, [], options, o);
6455 if(options.callback){
6456 options.callback.call(options.scope || this, [], options, false);
6460 // if data returned failure - throw an exception.
6461 if (o.success === false) {
6462 // show a message if no listener is registered.
6463 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6464 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6466 // loadmask wil be hooked into this..
6467 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6470 var r = o.records, t = o.totalRecords || r.length;
6472 this.fireEvent("beforeloadadd", this, r, options, o);
6474 if(!options || options.add !== true){
6475 if(this.pruneModifiedRecords){
6478 for(var i = 0, len = r.length; i < len; i++){
6482 this.data = this.snapshot;
6483 delete this.snapshot;
6486 this.data.addAll(r);
6487 this.totalLength = t;
6489 this.fireEvent("datachanged", this);
6491 this.totalLength = Math.max(t, this.data.length+r.length);
6494 this.fireEvent("load", this, r, options, o);
6495 if(options.callback){
6496 options.callback.call(options.scope || this, r, options, true);
6502 * Loads data from a passed data block. A Reader which understands the format of the data
6503 * must have been configured in the constructor.
6504 * @param {Object} data The data block from which to read the Records. The format of the data expected
6505 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6506 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6508 loadData : function(o, append){
6509 var r = this.reader.readRecords(o);
6510 this.loadRecords(r, {add: append}, true);
6514 * Gets the number of cached records.
6516 * <em>If using paging, this may not be the total size of the dataset. If the data object
6517 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6518 * the data set size</em>
6520 getCount : function(){
6521 return this.data.length || 0;
6525 * Gets the total number of records in the dataset as returned by the server.
6527 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6528 * the dataset size</em>
6530 getTotalCount : function(){
6531 return this.totalLength || 0;
6535 * Returns the sort state of the Store as an object with two properties:
6537 field {String} The name of the field by which the Records are sorted
6538 direction {String} The sort order, "ASC" or "DESC"
6541 getSortState : function(){
6542 return this.sortInfo;
6546 applySort : function(){
6547 if(this.sortInfo && !this.remoteSort){
6548 var s = this.sortInfo, f = s.field;
6549 var st = this.fields.get(f).sortType;
6550 var fn = function(r1, r2){
6551 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6552 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6554 this.data.sort(s.direction, fn);
6555 if(this.snapshot && this.snapshot != this.data){
6556 this.snapshot.sort(s.direction, fn);
6562 * Sets the default sort column and order to be used by the next load operation.
6563 * @param {String} fieldName The name of the field to sort by.
6564 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6566 setDefaultSort : function(field, dir){
6567 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6572 * If remote sorting is used, the sort is performed on the server, and the cache is
6573 * reloaded. If local sorting is used, the cache is sorted internally.
6574 * @param {String} fieldName The name of the field to sort by.
6575 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6577 sort : function(fieldName, dir){
6578 var f = this.fields.get(fieldName);
6580 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6582 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6583 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6588 this.sortToggle[f.name] = dir;
6589 this.sortInfo = {field: f.name, direction: dir};
6590 if(!this.remoteSort){
6592 this.fireEvent("datachanged", this);
6594 this.load(this.lastOptions);
6599 * Calls the specified function for each of the Records in the cache.
6600 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6601 * Returning <em>false</em> aborts and exits the iteration.
6602 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6604 each : function(fn, scope){
6605 this.data.each(fn, scope);
6609 * Gets all records modified since the last commit. Modified records are persisted across load operations
6610 * (e.g., during paging).
6611 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6613 getModifiedRecords : function(){
6614 return this.modified;
6618 createFilterFn : function(property, value, anyMatch){
6619 if(!value.exec){ // not a regex
6620 value = String(value);
6621 if(value.length == 0){
6624 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6627 return value.test(r.data[property]);
6632 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6633 * @param {String} property A field on your records
6634 * @param {Number} start The record index to start at (defaults to 0)
6635 * @param {Number} end The last record index to include (defaults to length - 1)
6636 * @return {Number} The sum
6638 sum : function(property, start, end){
6639 var rs = this.data.items, v = 0;
6641 end = (end || end === 0) ? end : rs.length-1;
6643 for(var i = start; i <= end; i++){
6644 v += (rs[i].data[property] || 0);
6650 * Filter the records by a specified property.
6651 * @param {String} field A field on your records
6652 * @param {String/RegExp} value Either a string that the field
6653 * should start with or a RegExp to test against the field
6654 * @param {Boolean} anyMatch True to match any part not just the beginning
6656 filter : function(property, value, anyMatch){
6657 var fn = this.createFilterFn(property, value, anyMatch);
6658 return fn ? this.filterBy(fn) : this.clearFilter();
6662 * Filter by a function. The specified function will be called with each
6663 * record in this data source. If the function returns true the record is included,
6664 * otherwise it is filtered.
6665 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6666 * @param {Object} scope (optional) The scope of the function (defaults to this)
6668 filterBy : function(fn, scope){
6669 this.snapshot = this.snapshot || this.data;
6670 this.data = this.queryBy(fn, scope||this);
6671 this.fireEvent("datachanged", this);
6675 * Query the records by a specified property.
6676 * @param {String} field A field on your records
6677 * @param {String/RegExp} value Either a string that the field
6678 * should start with or a RegExp to test against the field
6679 * @param {Boolean} anyMatch True to match any part not just the beginning
6680 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6682 query : function(property, value, anyMatch){
6683 var fn = this.createFilterFn(property, value, anyMatch);
6684 return fn ? this.queryBy(fn) : this.data.clone();
6688 * Query by a function. The specified function will be called with each
6689 * record in this data source. If the function returns true the record is included
6691 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6692 * @param {Object} scope (optional) The scope of the function (defaults to this)
6693 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6695 queryBy : function(fn, scope){
6696 var data = this.snapshot || this.data;
6697 return data.filterBy(fn, scope||this);
6701 * Collects unique values for a particular dataIndex from this store.
6702 * @param {String} dataIndex The property to collect
6703 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6704 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6705 * @return {Array} An array of the unique values
6707 collect : function(dataIndex, allowNull, bypassFilter){
6708 var d = (bypassFilter === true && this.snapshot) ?
6709 this.snapshot.items : this.data.items;
6710 var v, sv, r = [], l = {};
6711 for(var i = 0, len = d.length; i < len; i++){
6712 v = d[i].data[dataIndex];
6714 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6723 * Revert to a view of the Record cache with no filtering applied.
6724 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6726 clearFilter : function(suppressEvent){
6727 if(this.snapshot && this.snapshot != this.data){
6728 this.data = this.snapshot;
6729 delete this.snapshot;
6730 if(suppressEvent !== true){
6731 this.fireEvent("datachanged", this);
6737 afterEdit : function(record){
6738 if(this.modified.indexOf(record) == -1){
6739 this.modified.push(record);
6741 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6745 afterReject : function(record){
6746 this.modified.remove(record);
6747 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6751 afterCommit : function(record){
6752 this.modified.remove(record);
6753 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6757 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6758 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6760 commitChanges : function(){
6761 var m = this.modified.slice(0);
6763 for(var i = 0, len = m.length; i < len; i++){
6769 * Cancel outstanding changes on all changed records.
6771 rejectChanges : function(){
6772 var m = this.modified.slice(0);
6774 for(var i = 0, len = m.length; i < len; i++){
6779 onMetaChange : function(meta, rtype, o){
6780 this.recordType = rtype;
6781 this.fields = rtype.prototype.fields;
6782 delete this.snapshot;
6783 this.sortInfo = meta.sortInfo || this.sortInfo;
6785 this.fireEvent('metachange', this, this.reader.meta);
6788 moveIndex : function(data, type)
6790 var index = this.indexOf(data);
6792 var newIndex = index + type;
6796 this.insert(newIndex, data);
6801 * Ext JS Library 1.1.1
6802 * Copyright(c) 2006-2007, Ext JS, LLC.
6804 * Originally Released Under LGPL - original licence link has changed is not relivant.
6807 * <script type="text/javascript">
6811 * @class Roo.data.SimpleStore
6812 * @extends Roo.data.Store
6813 * Small helper class to make creating Stores from Array data easier.
6814 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6815 * @cfg {Array} fields An array of field definition objects, or field name strings.
6816 * @cfg {Array} data The multi-dimensional array of data
6818 * @param {Object} config
6820 Roo.data.SimpleStore = function(config){
6821 Roo.data.SimpleStore.superclass.constructor.call(this, {
6823 reader: new Roo.data.ArrayReader({
6826 Roo.data.Record.create(config.fields)
6828 proxy : new Roo.data.MemoryProxy(config.data)
6832 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6834 * Ext JS Library 1.1.1
6835 * Copyright(c) 2006-2007, Ext JS, LLC.
6837 * Originally Released Under LGPL - original licence link has changed is not relivant.
6840 * <script type="text/javascript">
6845 * @extends Roo.data.Store
6846 * @class Roo.data.JsonStore
6847 * Small helper class to make creating Stores for JSON data easier. <br/>
6849 var store = new Roo.data.JsonStore({
6850 url: 'get-images.php',
6852 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6855 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6856 * JsonReader and HttpProxy (unless inline data is provided).</b>
6857 * @cfg {Array} fields An array of field definition objects, or field name strings.
6859 * @param {Object} config
6861 Roo.data.JsonStore = function(c){
6862 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6863 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6864 reader: new Roo.data.JsonReader(c, c.fields)
6867 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6869 * Ext JS Library 1.1.1
6870 * Copyright(c) 2006-2007, Ext JS, LLC.
6872 * Originally Released Under LGPL - original licence link has changed is not relivant.
6875 * <script type="text/javascript">
6879 Roo.data.Field = function(config){
6880 if(typeof config == "string"){
6881 config = {name: config};
6883 Roo.apply(this, config);
6889 var st = Roo.data.SortTypes;
6890 // named sortTypes are supported, here we look them up
6891 if(typeof this.sortType == "string"){
6892 this.sortType = st[this.sortType];
6895 // set default sortType for strings and dates
6899 this.sortType = st.asUCString;
6902 this.sortType = st.asDate;
6905 this.sortType = st.none;
6910 var stripRe = /[\$,%]/g;
6912 // prebuilt conversion function for this field, instead of
6913 // switching every time we're reading a value
6915 var cv, dateFormat = this.dateFormat;
6920 cv = function(v){ return v; };
6923 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6927 return v !== undefined && v !== null && v !== '' ?
6928 parseInt(String(v).replace(stripRe, ""), 10) : '';
6933 return v !== undefined && v !== null && v !== '' ?
6934 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6939 cv = function(v){ return v === true || v === "true" || v == 1; };
6946 if(v instanceof Date){
6950 if(dateFormat == "timestamp"){
6951 return new Date(v*1000);
6953 return Date.parseDate(v, dateFormat);
6955 var parsed = Date.parse(v);
6956 return parsed ? new Date(parsed) : null;
6965 Roo.data.Field.prototype = {
6973 * Ext JS Library 1.1.1
6974 * Copyright(c) 2006-2007, Ext JS, LLC.
6976 * Originally Released Under LGPL - original licence link has changed is not relivant.
6979 * <script type="text/javascript">
6982 // Base class for reading structured data from a data source. This class is intended to be
6983 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6986 * @class Roo.data.DataReader
6987 * Base class for reading structured data from a data source. This class is intended to be
6988 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6991 Roo.data.DataReader = function(meta, recordType){
6995 this.recordType = recordType instanceof Array ?
6996 Roo.data.Record.create(recordType) : recordType;
6999 Roo.data.DataReader.prototype = {
7001 * Create an empty record
7002 * @param {Object} data (optional) - overlay some values
7003 * @return {Roo.data.Record} record created.
7005 newRow : function(d) {
7007 this.recordType.prototype.fields.each(function(c) {
7009 case 'int' : da[c.name] = 0; break;
7010 case 'date' : da[c.name] = new Date(); break;
7011 case 'float' : da[c.name] = 0.0; break;
7012 case 'boolean' : da[c.name] = false; break;
7013 default : da[c.name] = ""; break;
7017 return new this.recordType(Roo.apply(da, d));
7022 * Ext JS Library 1.1.1
7023 * Copyright(c) 2006-2007, Ext JS, LLC.
7025 * Originally Released Under LGPL - original licence link has changed is not relivant.
7028 * <script type="text/javascript">
7032 * @class Roo.data.DataProxy
7033 * @extends Roo.data.Observable
7034 * This class is an abstract base class for implementations which provide retrieval of
7035 * unformatted data objects.<br>
7037 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7038 * (of the appropriate type which knows how to parse the data object) to provide a block of
7039 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7041 * Custom implementations must implement the load method as described in
7042 * {@link Roo.data.HttpProxy#load}.
7044 Roo.data.DataProxy = function(){
7048 * Fires before a network request is made to retrieve a data object.
7049 * @param {Object} This DataProxy object.
7050 * @param {Object} params The params parameter to the load function.
7055 * Fires before the load method's callback is called.
7056 * @param {Object} This DataProxy object.
7057 * @param {Object} o The data object.
7058 * @param {Object} arg The callback argument object passed to the load function.
7062 * @event loadexception
7063 * Fires if an Exception occurs during data retrieval.
7064 * @param {Object} This DataProxy object.
7065 * @param {Object} o The data object.
7066 * @param {Object} arg The callback argument object passed to the load function.
7067 * @param {Object} e The Exception.
7069 loadexception : true
7071 Roo.data.DataProxy.superclass.constructor.call(this);
7074 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7077 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7081 * Ext JS Library 1.1.1
7082 * Copyright(c) 2006-2007, Ext JS, LLC.
7084 * Originally Released Under LGPL - original licence link has changed is not relivant.
7087 * <script type="text/javascript">
7090 * @class Roo.data.MemoryProxy
7091 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7092 * to the Reader when its load method is called.
7094 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7096 Roo.data.MemoryProxy = function(data){
7100 Roo.data.MemoryProxy.superclass.constructor.call(this);
7104 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7106 * Load data from the requested source (in this case an in-memory
7107 * data object passed to the constructor), read the data object into
7108 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7109 * process that block using the passed callback.
7110 * @param {Object} params This parameter is not used by the MemoryProxy class.
7111 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7112 * object into a block of Roo.data.Records.
7113 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7114 * The function must be passed <ul>
7115 * <li>The Record block object</li>
7116 * <li>The "arg" argument from the load function</li>
7117 * <li>A boolean success indicator</li>
7119 * @param {Object} scope The scope in which to call the callback
7120 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7122 load : function(params, reader, callback, scope, arg){
7123 params = params || {};
7126 result = reader.readRecords(this.data);
7128 this.fireEvent("loadexception", this, arg, null, e);
7129 callback.call(scope, null, arg, false);
7132 callback.call(scope, result, arg, true);
7136 update : function(params, records){
7141 * Ext JS Library 1.1.1
7142 * Copyright(c) 2006-2007, Ext JS, LLC.
7144 * Originally Released Under LGPL - original licence link has changed is not relivant.
7147 * <script type="text/javascript">
7150 * @class Roo.data.HttpProxy
7151 * @extends Roo.data.DataProxy
7152 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7153 * configured to reference a certain URL.<br><br>
7155 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7156 * from which the running page was served.<br><br>
7158 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7160 * Be aware that to enable the browser to parse an XML document, the server must set
7161 * the Content-Type header in the HTTP response to "text/xml".
7163 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7164 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7165 * will be used to make the request.
7167 Roo.data.HttpProxy = function(conn){
7168 Roo.data.HttpProxy.superclass.constructor.call(this);
7169 // is conn a conn config or a real conn?
7171 this.useAjax = !conn || !conn.events;
7175 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7176 // thse are take from connection...
7179 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7182 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7183 * extra parameters to each request made by this object. (defaults to undefined)
7186 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7187 * to each request made by this object. (defaults to undefined)
7190 * @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)
7193 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7196 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7202 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7206 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7207 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7208 * a finer-grained basis than the DataProxy events.
7210 getConnection : function(){
7211 return this.useAjax ? Roo.Ajax : this.conn;
7215 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7216 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7217 * process that block using the passed callback.
7218 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7219 * for the request to the remote server.
7220 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7221 * object into a block of Roo.data.Records.
7222 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7223 * The function must be passed <ul>
7224 * <li>The Record block object</li>
7225 * <li>The "arg" argument from the load function</li>
7226 * <li>A boolean success indicator</li>
7228 * @param {Object} scope The scope in which to call the callback
7229 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7231 load : function(params, reader, callback, scope, arg){
7232 if(this.fireEvent("beforeload", this, params) !== false){
7234 params : params || {},
7236 callback : callback,
7241 callback : this.loadResponse,
7245 Roo.applyIf(o, this.conn);
7246 if(this.activeRequest){
7247 Roo.Ajax.abort(this.activeRequest);
7249 this.activeRequest = Roo.Ajax.request(o);
7251 this.conn.request(o);
7254 callback.call(scope||this, null, arg, false);
7259 loadResponse : function(o, success, response){
7260 delete this.activeRequest;
7262 this.fireEvent("loadexception", this, o, response);
7263 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7268 result = o.reader.read(response);
7270 this.fireEvent("loadexception", this, o, response, e);
7271 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7275 this.fireEvent("load", this, o, o.request.arg);
7276 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7280 update : function(dataSet){
7285 updateResponse : function(dataSet){
7290 * Ext JS Library 1.1.1
7291 * Copyright(c) 2006-2007, Ext JS, LLC.
7293 * Originally Released Under LGPL - original licence link has changed is not relivant.
7296 * <script type="text/javascript">
7300 * @class Roo.data.ScriptTagProxy
7301 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7302 * other than the originating domain of the running page.<br><br>
7304 * <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
7305 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7307 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7308 * source code that is used as the source inside a <script> tag.<br><br>
7310 * In order for the browser to process the returned data, the server must wrap the data object
7311 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7312 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7313 * depending on whether the callback name was passed:
7316 boolean scriptTag = false;
7317 String cb = request.getParameter("callback");
7320 response.setContentType("text/javascript");
7322 response.setContentType("application/x-json");
7324 Writer out = response.getWriter();
7326 out.write(cb + "(");
7328 out.print(dataBlock.toJsonString());
7335 * @param {Object} config A configuration object.
7337 Roo.data.ScriptTagProxy = function(config){
7338 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7339 Roo.apply(this, config);
7340 this.head = document.getElementsByTagName("head")[0];
7343 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7345 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7347 * @cfg {String} url The URL from which to request the data object.
7350 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7354 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7355 * the server the name of the callback function set up by the load call to process the returned data object.
7356 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7357 * javascript output which calls this named function passing the data object as its only parameter.
7359 callbackParam : "callback",
7361 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7362 * name to the request.
7367 * Load data from the configured URL, read the data object into
7368 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7369 * process that block using the passed callback.
7370 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7371 * for the request to the remote server.
7372 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7373 * object into a block of Roo.data.Records.
7374 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7375 * The function must be passed <ul>
7376 * <li>The Record block object</li>
7377 * <li>The "arg" argument from the load function</li>
7378 * <li>A boolean success indicator</li>
7380 * @param {Object} scope The scope in which to call the callback
7381 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7383 load : function(params, reader, callback, scope, arg){
7384 if(this.fireEvent("beforeload", this, params) !== false){
7386 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7389 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7391 url += "&_dc=" + (new Date().getTime());
7393 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7396 cb : "stcCallback"+transId,
7397 scriptId : "stcScript"+transId,
7401 callback : callback,
7407 window[trans.cb] = function(o){
7408 conn.handleResponse(o, trans);
7411 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7413 if(this.autoAbort !== false){
7417 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7419 var script = document.createElement("script");
7420 script.setAttribute("src", url);
7421 script.setAttribute("type", "text/javascript");
7422 script.setAttribute("id", trans.scriptId);
7423 this.head.appendChild(script);
7427 callback.call(scope||this, null, arg, false);
7432 isLoading : function(){
7433 return this.trans ? true : false;
7437 * Abort the current server request.
7440 if(this.isLoading()){
7441 this.destroyTrans(this.trans);
7446 destroyTrans : function(trans, isLoaded){
7447 this.head.removeChild(document.getElementById(trans.scriptId));
7448 clearTimeout(trans.timeoutId);
7450 window[trans.cb] = undefined;
7452 delete window[trans.cb];
7455 // if hasn't been loaded, wait for load to remove it to prevent script error
7456 window[trans.cb] = function(){
7457 window[trans.cb] = undefined;
7459 delete window[trans.cb];
7466 handleResponse : function(o, trans){
7468 this.destroyTrans(trans, true);
7471 result = trans.reader.readRecords(o);
7473 this.fireEvent("loadexception", this, o, trans.arg, e);
7474 trans.callback.call(trans.scope||window, null, trans.arg, false);
7477 this.fireEvent("load", this, o, trans.arg);
7478 trans.callback.call(trans.scope||window, result, trans.arg, true);
7482 handleFailure : function(trans){
7484 this.destroyTrans(trans, false);
7485 this.fireEvent("loadexception", this, null, trans.arg);
7486 trans.callback.call(trans.scope||window, null, trans.arg, false);
7490 * Ext JS Library 1.1.1
7491 * Copyright(c) 2006-2007, Ext JS, LLC.
7493 * Originally Released Under LGPL - original licence link has changed is not relivant.
7496 * <script type="text/javascript">
7500 * @class Roo.data.JsonReader
7501 * @extends Roo.data.DataReader
7502 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7503 * based on mappings in a provided Roo.data.Record constructor.
7505 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7506 * in the reply previously.
7511 var RecordDef = Roo.data.Record.create([
7512 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7513 {name: 'occupation'} // This field will use "occupation" as the mapping.
7515 var myReader = new Roo.data.JsonReader({
7516 totalProperty: "results", // The property which contains the total dataset size (optional)
7517 root: "rows", // The property which contains an Array of row objects
7518 id: "id" // The property within each row object that provides an ID for the record (optional)
7522 * This would consume a JSON file like this:
7524 { 'results': 2, 'rows': [
7525 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7526 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7529 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7530 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7531 * paged from the remote server.
7532 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7533 * @cfg {String} root name of the property which contains the Array of row objects.
7534 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7536 * Create a new JsonReader
7537 * @param {Object} meta Metadata configuration options
7538 * @param {Object} recordType Either an Array of field definition objects,
7539 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7541 Roo.data.JsonReader = function(meta, recordType){
7544 // set some defaults:
7546 totalProperty: 'total',
7547 successProperty : 'success',
7552 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7554 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7557 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7558 * Used by Store query builder to append _requestMeta to params.
7561 metaFromRemote : false,
7563 * This method is only used by a DataProxy which has retrieved data from a remote server.
7564 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7565 * @return {Object} data A data block which is used by an Roo.data.Store object as
7566 * a cache of Roo.data.Records.
7568 read : function(response){
7569 var json = response.responseText;
7571 var o = /* eval:var:o */ eval("("+json+")");
7573 throw {message: "JsonReader.read: Json object not found"};
7579 this.metaFromRemote = true;
7580 this.meta = o.metaData;
7581 this.recordType = Roo.data.Record.create(o.metaData.fields);
7582 this.onMetaChange(this.meta, this.recordType, o);
7584 return this.readRecords(o);
7587 // private function a store will implement
7588 onMetaChange : function(meta, recordType, o){
7595 simpleAccess: function(obj, subsc) {
7602 getJsonAccessor: function(){
7604 return function(expr) {
7606 return(re.test(expr))
7607 ? new Function("obj", "return obj." + expr)
7617 * Create a data block containing Roo.data.Records from an XML document.
7618 * @param {Object} o An object which contains an Array of row objects in the property specified
7619 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7620 * which contains the total size of the dataset.
7621 * @return {Object} data A data block which is used by an Roo.data.Store object as
7622 * a cache of Roo.data.Records.
7624 readRecords : function(o){
7626 * After any data loads, the raw JSON data is available for further custom processing.
7630 var s = this.meta, Record = this.recordType,
7631 f = Record.prototype.fields, fi = f.items, fl = f.length;
7633 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7635 if(s.totalProperty) {
7636 this.getTotal = this.getJsonAccessor(s.totalProperty);
7638 if(s.successProperty) {
7639 this.getSuccess = this.getJsonAccessor(s.successProperty);
7641 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7643 var g = this.getJsonAccessor(s.id);
7644 this.getId = function(rec) {
7646 return (r === undefined || r === "") ? null : r;
7649 this.getId = function(){return null;};
7652 for(var jj = 0; jj < fl; jj++){
7654 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7655 this.ef[jj] = this.getJsonAccessor(map);
7659 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7660 if(s.totalProperty){
7661 var vt = parseInt(this.getTotal(o), 10);
7666 if(s.successProperty){
7667 var vs = this.getSuccess(o);
7668 if(vs === false || vs === 'false'){
7673 for(var i = 0; i < c; i++){
7676 var id = this.getId(n);
7677 for(var j = 0; j < fl; j++){
7679 var v = this.ef[j](n);
7681 Roo.log('missing convert for ' + f.name);
7685 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7687 var record = new Record(values, id);
7689 records[i] = record;
7695 totalRecords : totalRecords
7700 * Ext JS Library 1.1.1
7701 * Copyright(c) 2006-2007, Ext JS, LLC.
7703 * Originally Released Under LGPL - original licence link has changed is not relivant.
7706 * <script type="text/javascript">
7710 * @class Roo.data.ArrayReader
7711 * @extends Roo.data.DataReader
7712 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7713 * Each element of that Array represents a row of data fields. The
7714 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7715 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7719 var RecordDef = Roo.data.Record.create([
7720 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7721 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7723 var myReader = new Roo.data.ArrayReader({
7724 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7728 * This would consume an Array like this:
7730 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7732 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7734 * Create a new JsonReader
7735 * @param {Object} meta Metadata configuration options.
7736 * @param {Object} recordType Either an Array of field definition objects
7737 * as specified to {@link Roo.data.Record#create},
7738 * or an {@link Roo.data.Record} object
7739 * created using {@link Roo.data.Record#create}.
7741 Roo.data.ArrayReader = function(meta, recordType){
7742 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7745 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7747 * Create a data block containing Roo.data.Records from an XML document.
7748 * @param {Object} o An Array of row objects which represents the dataset.
7749 * @return {Object} data A data block which is used by an Roo.data.Store object as
7750 * a cache of Roo.data.Records.
7752 readRecords : function(o){
7753 var sid = this.meta ? this.meta.id : null;
7754 var recordType = this.recordType, fields = recordType.prototype.fields;
7757 for(var i = 0; i < root.length; i++){
7760 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7761 for(var j = 0, jlen = fields.length; j < jlen; j++){
7762 var f = fields.items[j];
7763 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7764 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7768 var record = new recordType(values, id);
7770 records[records.length] = record;
7774 totalRecords : records.length
7783 * @class Roo.bootstrap.ComboBox
7784 * @extends Roo.bootstrap.TriggerField
7785 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7786 * @cfg {Boolean} append (true|false) default false
7788 * Create a new ComboBox.
7789 * @param {Object} config Configuration options
7791 Roo.bootstrap.ComboBox = function(config){
7792 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7796 * Fires when the dropdown list is expanded
7797 * @param {Roo.bootstrap.ComboBox} combo This combo box
7802 * Fires when the dropdown list is collapsed
7803 * @param {Roo.bootstrap.ComboBox} combo This combo box
7807 * @event beforeselect
7808 * Fires before a list item is selected. Return false to cancel the selection.
7809 * @param {Roo.bootstrap.ComboBox} combo This combo box
7810 * @param {Roo.data.Record} record The data record returned from the underlying store
7811 * @param {Number} index The index of the selected item in the dropdown list
7813 'beforeselect' : true,
7816 * Fires when a list item is selected
7817 * @param {Roo.bootstrap.ComboBox} combo This combo box
7818 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7819 * @param {Number} index The index of the selected item in the dropdown list
7823 * @event beforequery
7824 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7825 * The event object passed has these properties:
7826 * @param {Roo.bootstrap.ComboBox} combo This combo box
7827 * @param {String} query The query
7828 * @param {Boolean} forceAll true to force "all" query
7829 * @param {Boolean} cancel true to cancel the query
7830 * @param {Object} e The query event object
7832 'beforequery': true,
7835 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7836 * @param {Roo.bootstrap.ComboBox} combo This combo box
7841 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7842 * @param {Roo.bootstrap.ComboBox} combo This combo box
7843 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7848 * Fires when the remove value from the combobox array
7849 * @param {Roo.bootstrap.ComboBox} combo This combo box
7856 this.selectedIndex = -1;
7857 if(this.mode == 'local'){
7858 if(config.queryDelay === undefined){
7859 this.queryDelay = 10;
7861 if(config.minChars === undefined){
7867 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7870 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7871 * rendering into an Roo.Editor, defaults to false)
7874 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7875 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7878 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7881 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7882 * the dropdown list (defaults to undefined, with no header element)
7886 * @cfg {String/Roo.Template} tpl The template to use to render the output
7890 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7892 listWidth: undefined,
7894 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7895 * mode = 'remote' or 'text' if mode = 'local')
7897 displayField: undefined,
7899 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7900 * mode = 'remote' or 'value' if mode = 'local').
7901 * Note: use of a valueField requires the user make a selection
7902 * in order for a value to be mapped.
7904 valueField: undefined,
7908 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7909 * field's data value (defaults to the underlying DOM element's name)
7911 hiddenName: undefined,
7913 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7917 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7919 selectedClass: 'active',
7922 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7926 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7927 * anchor positions (defaults to 'tl-bl')
7929 listAlign: 'tl-bl?',
7931 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7935 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7936 * query specified by the allQuery config option (defaults to 'query')
7938 triggerAction: 'query',
7940 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7941 * (defaults to 4, does not apply if editable = false)
7945 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7946 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7950 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7951 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7955 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7956 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7960 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7961 * when editable = true (defaults to false)
7963 selectOnFocus:false,
7965 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7967 queryParam: 'query',
7969 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7970 * when mode = 'remote' (defaults to 'Loading...')
7972 loadingText: 'Loading...',
7974 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7978 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7982 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7983 * traditional select (defaults to true)
7987 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7991 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7995 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7996 * listWidth has a higher value)
8000 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8001 * allow the user to set arbitrary text into the field (defaults to false)
8003 forceSelection:false,
8005 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8006 * if typeAhead = true (defaults to 250)
8008 typeAheadDelay : 250,
8010 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8011 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8013 valueNotFoundText : undefined,
8015 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8020 * @cfg {Boolean} disableClear Disable showing of clear button.
8022 disableClear : false,
8024 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
8026 alwaysQuery : false,
8029 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8043 // element that contains real text value.. (when hidden is used..)
8046 initEvents: function(){
8049 throw "can not find store for combo";
8051 this.store = Roo.factory(this.store, Roo.data);
8055 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8058 if(this.hiddenName){
8060 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8062 this.hiddenField.dom.value =
8063 this.hiddenValue !== undefined ? this.hiddenValue :
8064 this.value !== undefined ? this.value : '';
8066 // prevent input submission
8067 this.el.dom.removeAttribute('name');
8068 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8073 // this.el.dom.setAttribute('autocomplete', 'off');
8076 var cls = 'x-combo-list';
8077 this.list = this.el.select('ul.dropdown-menu',true).first();
8079 //this.list = new Roo.Layer({
8080 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8083 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8084 this.list.setWidth(lw);
8086 this.list.on('mouseover', this.onViewOver, this);
8087 this.list.on('mousemove', this.onViewMove, this);
8089 this.list.on('scroll', this.onViewScroll, this);
8092 this.list.swallowEvent('mousewheel');
8093 this.assetHeight = 0;
8096 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8097 this.assetHeight += this.header.getHeight();
8100 this.innerList = this.list.createChild({cls:cls+'-inner'});
8101 this.innerList.on('mouseover', this.onViewOver, this);
8102 this.innerList.on('mousemove', this.onViewMove, this);
8103 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8105 if(this.allowBlank && !this.pageSize && !this.disableClear){
8106 this.footer = this.list.createChild({cls:cls+'-ft'});
8107 this.pageTb = new Roo.Toolbar(this.footer);
8111 this.footer = this.list.createChild({cls:cls+'-ft'});
8112 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8113 {pageSize: this.pageSize});
8117 if (this.pageTb && this.allowBlank && !this.disableClear) {
8119 this.pageTb.add(new Roo.Toolbar.Fill(), {
8120 cls: 'x-btn-icon x-btn-clear',
8126 _this.onSelect(false, -1);
8131 this.assetHeight += this.footer.getHeight();
8136 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8139 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8140 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8142 //this.view.wrapEl.setDisplayed(false);
8143 this.view.on('click', this.onViewClick, this);
8147 this.store.on('beforeload', this.onBeforeLoad, this);
8148 this.store.on('load', this.onLoad, this);
8149 this.store.on('loadexception', this.onLoadException, this);
8152 this.resizer = new Roo.Resizable(this.list, {
8153 pinned:true, handles:'se'
8155 this.resizer.on('resize', function(r, w, h){
8156 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8158 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8159 this.restrictHeight();
8161 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8165 this.editable = true;
8166 this.setEditable(false);
8171 if (typeof(this.events.add.listeners) != 'undefined') {
8173 this.addicon = this.wrap.createChild(
8174 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8176 this.addicon.on('click', function(e) {
8177 this.fireEvent('add', this);
8180 if (typeof(this.events.edit.listeners) != 'undefined') {
8182 this.editicon = this.wrap.createChild(
8183 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8185 this.editicon.setStyle('margin-left', '40px');
8187 this.editicon.on('click', function(e) {
8189 // we fire even if inothing is selected..
8190 this.fireEvent('edit', this, this.lastData );
8196 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8198 this.inKeyMode = true;
8202 "down" : function(e){
8203 if(!this.isExpanded()){
8204 this.onTriggerClick();
8206 this.inKeyMode = true;
8211 "enter" : function(e){
8216 "esc" : function(e){
8220 "tab" : function(e){
8223 if(this.fireEvent("specialkey", this, e)){
8224 this.onViewClick(false);
8232 doRelay : function(foo, bar, hname){
8233 if(hname == 'down' || this.scope.isExpanded()){
8234 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8243 this.queryDelay = Math.max(this.queryDelay || 10,
8244 this.mode == 'local' ? 10 : 250);
8247 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8250 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8252 if(this.editable !== false){
8253 this.inputEl().on("keyup", this.onKeyUp, this);
8255 if(this.forceSelection){
8256 this.on('blur', this.doForce, this);
8260 this.choices = this.el.select('ul.select2-choices', true).first();
8261 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8265 onDestroy : function(){
8267 this.view.setStore(null);
8268 this.view.el.removeAllListeners();
8269 this.view.el.remove();
8270 this.view.purgeListeners();
8273 this.list.dom.innerHTML = '';
8276 this.store.un('beforeload', this.onBeforeLoad, this);
8277 this.store.un('load', this.onLoad, this);
8278 this.store.un('loadexception', this.onLoadException, this);
8280 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8284 fireKey : function(e){
8285 if(e.isNavKeyPress() && !this.list.isVisible()){
8286 this.fireEvent("specialkey", this, e);
8291 onResize: function(w, h){
8292 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8294 // if(typeof w != 'number'){
8295 // // we do not handle it!?!?
8298 // var tw = this.trigger.getWidth();
8299 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8300 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8302 // this.inputEl().setWidth( this.adjustWidth('input', x));
8304 // //this.trigger.setStyle('left', x+'px');
8306 // if(this.list && this.listWidth === undefined){
8307 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8308 // this.list.setWidth(lw);
8309 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8317 * Allow or prevent the user from directly editing the field text. If false is passed,
8318 * the user will only be able to select from the items defined in the dropdown list. This method
8319 * is the runtime equivalent of setting the 'editable' config option at config time.
8320 * @param {Boolean} value True to allow the user to directly edit the field text
8322 setEditable : function(value){
8323 if(value == this.editable){
8326 this.editable = value;
8328 this.inputEl().dom.setAttribute('readOnly', true);
8329 this.inputEl().on('mousedown', this.onTriggerClick, this);
8330 this.inputEl().addClass('x-combo-noedit');
8332 this.inputEl().dom.setAttribute('readOnly', false);
8333 this.inputEl().un('mousedown', this.onTriggerClick, this);
8334 this.inputEl().removeClass('x-combo-noedit');
8340 onBeforeLoad : function(combo,opts){
8345 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8347 this.restrictHeight();
8348 this.selectedIndex = -1;
8352 onLoad : function(){
8354 this.hasQuery = false;
8360 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8361 this.loading.hide();
8364 if(this.store.getCount() > 0){
8366 this.restrictHeight();
8367 if(this.lastQuery == this.allQuery){
8369 this.inputEl().dom.select();
8371 if(!this.selectByValue(this.value, true)){
8372 this.select(0, true);
8376 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8377 this.taTask.delay(this.typeAheadDelay);
8381 this.onEmptyResults();
8387 onLoadException : function()
8389 this.hasQuery = false;
8391 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8392 this.loading.hide();
8396 Roo.log(this.store.reader.jsonData);
8397 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8399 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8405 onTypeAhead : function(){
8406 if(this.store.getCount() > 0){
8407 var r = this.store.getAt(0);
8408 var newValue = r.data[this.displayField];
8409 var len = newValue.length;
8410 var selStart = this.getRawValue().length;
8412 if(selStart != len){
8413 this.setRawValue(newValue);
8414 this.selectText(selStart, newValue.length);
8420 onSelect : function(record, index){
8422 if(this.fireEvent('beforeselect', this, record, index) !== false){
8424 this.setFromData(index > -1 ? record.data : false);
8427 this.fireEvent('select', this, record, index);
8432 * Returns the currently selected field value or empty string if no value is set.
8433 * @return {String} value The selected value
8435 getValue : function(){
8438 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8441 if(this.valueField){
8442 return typeof this.value != 'undefined' ? this.value : '';
8444 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8449 * Clears any text/value currently set in the field
8451 clearValue : function(){
8452 if(this.hiddenField){
8453 this.hiddenField.dom.value = '';
8456 this.setRawValue('');
8457 this.lastSelectionText = '';
8462 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8463 * will be displayed in the field. If the value does not match the data value of an existing item,
8464 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8465 * Otherwise the field will be blank (although the value will still be set).
8466 * @param {String} value The value to match
8468 setValue : function(v){
8475 if(this.valueField){
8476 var r = this.findRecord(this.valueField, v);
8478 text = r.data[this.displayField];
8479 }else if(this.valueNotFoundText !== undefined){
8480 text = this.valueNotFoundText;
8483 this.lastSelectionText = text;
8484 if(this.hiddenField){
8485 this.hiddenField.dom.value = v;
8487 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8491 * @property {Object} the last set data for the element
8496 * Sets the value of the field based on a object which is related to the record format for the store.
8497 * @param {Object} value the value to set as. or false on reset?
8499 setFromData : function(o){
8506 var dv = ''; // display value
8507 var vv = ''; // value value..
8509 if (this.displayField) {
8510 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8512 // this is an error condition!!!
8513 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8516 if(this.valueField){
8517 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8520 if(this.hiddenField){
8521 this.hiddenField.dom.value = vv;
8523 this.lastSelectionText = dv;
8524 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8528 // no hidden field.. - we store the value in 'value', but still display
8529 // display field!!!!
8530 this.lastSelectionText = dv;
8531 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8538 // overridden so that last data is reset..
8539 this.setValue(this.originalValue);
8540 this.clearInvalid();
8541 this.lastData = false;
8543 this.view.clearSelections();
8547 findRecord : function(prop, value){
8549 if(this.store.getCount() > 0){
8550 this.store.each(function(r){
8551 if(r.data[prop] == value){
8563 // returns hidden if it's set..
8564 if (!this.rendered) {return ''};
8565 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8569 onViewMove : function(e, t){
8570 this.inKeyMode = false;
8574 onViewOver : function(e, t){
8575 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8578 var item = this.view.findItemFromChild(t);
8580 var index = this.view.indexOf(item);
8581 this.select(index, false);
8586 onViewClick : function(doFocus)
8588 var index = this.view.getSelectedIndexes()[0];
8589 var r = this.store.getAt(index);
8591 this.onSelect(r, index);
8593 if(doFocus !== false && !this.blockFocus){
8594 this.inputEl().focus();
8599 restrictHeight : function(){
8600 //this.innerList.dom.style.height = '';
8601 //var inner = this.innerList.dom;
8602 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8603 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8604 //this.list.beginUpdate();
8605 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8606 this.list.alignTo(this.inputEl(), this.listAlign);
8607 //this.list.endUpdate();
8611 onEmptyResults : function(){
8616 * Returns true if the dropdown list is expanded, else false.
8618 isExpanded : function(){
8619 return this.list.isVisible();
8623 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8624 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8625 * @param {String} value The data value of the item to select
8626 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8627 * selected item if it is not currently in view (defaults to true)
8628 * @return {Boolean} True if the value matched an item in the list, else false
8630 selectByValue : function(v, scrollIntoView){
8631 if(v !== undefined && v !== null){
8632 var r = this.findRecord(this.valueField || this.displayField, v);
8634 this.select(this.store.indexOf(r), scrollIntoView);
8642 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8643 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8644 * @param {Number} index The zero-based index of the list item to select
8645 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8646 * selected item if it is not currently in view (defaults to true)
8648 select : function(index, scrollIntoView){
8649 this.selectedIndex = index;
8650 this.view.select(index);
8651 if(scrollIntoView !== false){
8652 var el = this.view.getNode(index);
8654 //this.innerList.scrollChildIntoView(el, false);
8661 selectNext : function(){
8662 var ct = this.store.getCount();
8664 if(this.selectedIndex == -1){
8666 }else if(this.selectedIndex < ct-1){
8667 this.select(this.selectedIndex+1);
8673 selectPrev : function(){
8674 var ct = this.store.getCount();
8676 if(this.selectedIndex == -1){
8678 }else if(this.selectedIndex != 0){
8679 this.select(this.selectedIndex-1);
8685 onKeyUp : function(e){
8686 if(this.editable !== false && !e.isSpecialKey()){
8687 this.lastKey = e.getKey();
8688 this.dqTask.delay(this.queryDelay);
8693 validateBlur : function(){
8694 return !this.list || !this.list.isVisible();
8698 initQuery : function(){
8699 this.doQuery(this.getRawValue());
8703 doForce : function(){
8704 if(this.el.dom.value.length > 0){
8706 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8712 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8713 * query allowing the query action to be canceled if needed.
8714 * @param {String} query The SQL query to execute
8715 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8716 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8717 * saved in the current store (defaults to false)
8719 doQuery : function(q, forceAll){
8721 if(q === undefined || q === null){
8730 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8735 forceAll = qe.forceAll;
8736 if(forceAll === true || (q.length >= this.minChars)){
8738 this.hasQuery = true;
8740 if(this.lastQuery != q || this.alwaysQuery){
8742 if(this.mode == 'local'){
8743 this.selectedIndex = -1;
8745 this.store.clearFilter();
8747 this.store.filter(this.displayField, q);
8751 this.store.baseParams[this.queryParam] = q;
8753 var options = {params : this.getParams(q)};
8757 options.params.start = this.page * this.pageSize;
8760 this.store.load(options);
8764 this.selectedIndex = -1;
8769 this.loadNext = false;
8773 getParams : function(q){
8775 //p[this.queryParam] = q;
8779 p.limit = this.pageSize;
8785 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8787 collapse : function(){
8788 if(!this.isExpanded()){
8793 Roo.get(document).un('mousedown', this.collapseIf, this);
8794 Roo.get(document).un('mousewheel', this.collapseIf, this);
8795 if (!this.editable) {
8796 Roo.get(document).un('keydown', this.listKeyPress, this);
8798 this.fireEvent('collapse', this);
8802 collapseIf : function(e){
8803 var in_combo = e.within(this.el);
8804 var in_list = e.within(this.list);
8806 if (in_combo || in_list) {
8807 //e.stopPropagation();
8816 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8818 expand : function(){
8820 if(this.isExpanded() || !this.hasFocus){
8824 this.list.alignTo(this.inputEl(), this.listAlign);
8826 Roo.get(document).on('mousedown', this.collapseIf, this);
8827 Roo.get(document).on('mousewheel', this.collapseIf, this);
8828 if (!this.editable) {
8829 Roo.get(document).on('keydown', this.listKeyPress, this);
8832 this.fireEvent('expand', this);
8836 // Implements the default empty TriggerField.onTriggerClick function
8837 onTriggerClick : function()
8839 Roo.log('trigger click');
8846 this.loadNext = false;
8848 if(this.isExpanded()){
8850 if (!this.blockFocus) {
8851 this.inputEl().focus();
8855 this.hasFocus = true;
8856 if(this.triggerAction == 'all') {
8857 this.doQuery(this.allQuery, true);
8859 this.doQuery(this.getRawValue());
8861 if (!this.blockFocus) {
8862 this.inputEl().focus();
8866 listKeyPress : function(e)
8868 //Roo.log('listkeypress');
8869 // scroll to first matching element based on key pres..
8870 if (e.isSpecialKey()) {
8873 var k = String.fromCharCode(e.getKey()).toUpperCase();
8876 var csel = this.view.getSelectedNodes();
8877 var cselitem = false;
8879 var ix = this.view.indexOf(csel[0]);
8880 cselitem = this.store.getAt(ix);
8881 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8887 this.store.each(function(v) {
8889 // start at existing selection.
8890 if (cselitem.id == v.id) {
8896 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8897 match = this.store.indexOf(v);
8903 if (match === false) {
8904 return true; // no more action?
8907 this.view.select(match);
8908 var sn = Roo.get(this.view.getSelectedNodes()[0])
8909 //sn.scrollIntoView(sn.dom.parentNode, false);
8912 onViewScroll : function(e, t){
8914 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8918 this.hasQuery = true;
8920 this.loading = this.list.select('.loading', true).first();
8922 if(this.loading === null){
8923 this.list.createChild({
8925 cls: 'loading select2-more-results select2-active',
8926 html: 'Loading more results...'
8929 this.loading = this.list.select('.loading', true).first();
8931 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8933 this.loading.hide();
8936 this.loading.show();
8941 this.loadNext = true;
8943 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8948 addItem : function(o)
8950 var dv = ''; // display value
8952 if (this.displayField) {
8953 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8955 // this is an error condition!!!
8956 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8963 var choice = this.choices.createChild({
8965 cls: 'select2-search-choice',
8974 cls: 'select2-search-choice-close',
8979 }, this.searchField);
8981 var close = choice.select('a.select2-search-choice-close', true).first()
8983 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8990 this.inputEl().dom.value = '';
8994 onRemoveItem : function(e, _self, o)
8996 Roo.log('remove item');
8997 var index = this.item.indexOf(o.data) * 1;
9000 Roo.log('not this item?!');
9004 this.item.splice(index, 1);
9009 this.fireEvent('remove', this);
9013 syncValue : function()
9015 if(!this.item.length){
9022 Roo.each(this.item, function(i){
9023 if(_this.valueField){
9024 value.push(i[_this.valueField]);
9031 this.value = value.join(',');
9033 if(this.hiddenField){
9034 this.hiddenField.dom.value = this.value;
9038 clearItem : function()
9046 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9056 * @cfg {Boolean} grow
9060 * @cfg {Number} growMin
9064 * @cfg {Number} growMax
9074 * Ext JS Library 1.1.1
9075 * Copyright(c) 2006-2007, Ext JS, LLC.
9077 * Originally Released Under LGPL - original licence link has changed is not relivant.
9080 * <script type="text/javascript">
9085 * @extends Roo.util.Observable
9086 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9087 * This class also supports single and multi selection modes. <br>
9088 * Create a data model bound view:
9090 var store = new Roo.data.Store(...);
9092 var view = new Roo.View({
9094 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9097 selectedClass: "ydataview-selected",
9101 // listen for node click?
9102 view.on("click", function(vw, index, node, e){
9103 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9107 dataModel.load("foobar.xml");
9109 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9111 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9112 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9114 * Note: old style constructor is still suported (container, template, config)
9118 * @param {Object} config The config object
9121 Roo.View = function(config, depreciated_tpl, depreciated_config){
9123 if (typeof(depreciated_tpl) == 'undefined') {
9124 // new way.. - universal constructor.
9125 Roo.apply(this, config);
9126 this.el = Roo.get(this.el);
9129 this.el = Roo.get(config);
9130 this.tpl = depreciated_tpl;
9131 Roo.apply(this, depreciated_config);
9133 this.wrapEl = this.el.wrap().wrap();
9134 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9137 if(typeof(this.tpl) == "string"){
9138 this.tpl = new Roo.Template(this.tpl);
9140 // support xtype ctors..
9141 this.tpl = new Roo.factory(this.tpl, Roo);
9153 * @event beforeclick
9154 * Fires before a click is processed. Returns false to cancel the default action.
9155 * @param {Roo.View} this
9156 * @param {Number} index The index of the target node
9157 * @param {HTMLElement} node The target node
9158 * @param {Roo.EventObject} e The raw event object
9160 "beforeclick" : true,
9163 * Fires when a template node is clicked.
9164 * @param {Roo.View} this
9165 * @param {Number} index The index of the target node
9166 * @param {HTMLElement} node The target node
9167 * @param {Roo.EventObject} e The raw event object
9172 * Fires when a template node is double clicked.
9173 * @param {Roo.View} this
9174 * @param {Number} index The index of the target node
9175 * @param {HTMLElement} node The target node
9176 * @param {Roo.EventObject} e The raw event object
9180 * @event contextmenu
9181 * Fires when a template node is right clicked.
9182 * @param {Roo.View} this
9183 * @param {Number} index The index of the target node
9184 * @param {HTMLElement} node The target node
9185 * @param {Roo.EventObject} e The raw event object
9187 "contextmenu" : true,
9189 * @event selectionchange
9190 * Fires when the selected nodes change.
9191 * @param {Roo.View} this
9192 * @param {Array} selections Array of the selected nodes
9194 "selectionchange" : true,
9197 * @event beforeselect
9198 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9199 * @param {Roo.View} this
9200 * @param {HTMLElement} node The node to be selected
9201 * @param {Array} selections Array of currently selected nodes
9203 "beforeselect" : true,
9205 * @event preparedata
9206 * Fires on every row to render, to allow you to change the data.
9207 * @param {Roo.View} this
9208 * @param {Object} data to be rendered (change this)
9210 "preparedata" : true
9218 "click": this.onClick,
9219 "dblclick": this.onDblClick,
9220 "contextmenu": this.onContextMenu,
9224 this.selections = [];
9226 this.cmp = new Roo.CompositeElementLite([]);
9228 this.store = Roo.factory(this.store, Roo.data);
9229 this.setStore(this.store, true);
9232 if ( this.footer && this.footer.xtype) {
9234 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9236 this.footer.dataSource = this.store
9237 this.footer.container = fctr;
9238 this.footer = Roo.factory(this.footer, Roo);
9239 fctr.insertFirst(this.el);
9241 // this is a bit insane - as the paging toolbar seems to detach the el..
9242 // dom.parentNode.parentNode.parentNode
9243 // they get detached?
9247 Roo.View.superclass.constructor.call(this);
9252 Roo.extend(Roo.View, Roo.util.Observable, {
9255 * @cfg {Roo.data.Store} store Data store to load data from.
9260 * @cfg {String|Roo.Element} el The container element.
9265 * @cfg {String|Roo.Template} tpl The template used by this View
9269 * @cfg {String} dataName the named area of the template to use as the data area
9270 * Works with domtemplates roo-name="name"
9274 * @cfg {String} selectedClass The css class to add to selected nodes
9276 selectedClass : "x-view-selected",
9278 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9283 * @cfg {String} text to display on mask (default Loading)
9287 * @cfg {Boolean} multiSelect Allow multiple selection
9289 multiSelect : false,
9291 * @cfg {Boolean} singleSelect Allow single selection
9293 singleSelect: false,
9296 * @cfg {Boolean} toggleSelect - selecting
9298 toggleSelect : false,
9301 * Returns the element this view is bound to.
9302 * @return {Roo.Element}
9311 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9313 refresh : function(){
9317 // if we are using something like 'domtemplate', then
9318 // the what gets used is:
9319 // t.applySubtemplate(NAME, data, wrapping data..)
9320 // the outer template then get' applied with
9321 // the store 'extra data'
9322 // and the body get's added to the
9323 // roo-name="data" node?
9324 // <span class='roo-tpl-{name}'></span> ?????
9328 this.clearSelections();
9331 var records = this.store.getRange();
9332 if(records.length < 1) {
9334 // is this valid?? = should it render a template??
9336 this.el.update(this.emptyText);
9340 if (this.dataName) {
9341 this.el.update(t.apply(this.store.meta)); //????
9342 el = this.el.child('.roo-tpl-' + this.dataName);
9345 for(var i = 0, len = records.length; i < len; i++){
9346 var data = this.prepareData(records[i].data, i, records[i]);
9347 this.fireEvent("preparedata", this, data, i, records[i]);
9348 html[html.length] = Roo.util.Format.trim(
9350 t.applySubtemplate(this.dataName, data, this.store.meta) :
9357 el.update(html.join(""));
9358 this.nodes = el.dom.childNodes;
9359 this.updateIndexes(0);
9364 * Function to override to reformat the data that is sent to
9365 * the template for each node.
9366 * DEPRICATED - use the preparedata event handler.
9367 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9368 * a JSON object for an UpdateManager bound view).
9370 prepareData : function(data, index, record)
9372 this.fireEvent("preparedata", this, data, index, record);
9376 onUpdate : function(ds, record){
9377 Roo.log('on update');
9378 this.clearSelections();
9379 var index = this.store.indexOf(record);
9380 var n = this.nodes[index];
9381 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9382 n.parentNode.removeChild(n);
9383 this.updateIndexes(index, index);
9389 onAdd : function(ds, records, index)
9391 Roo.log(['on Add', ds, records, index] );
9392 this.clearSelections();
9393 if(this.nodes.length == 0){
9397 var n = this.nodes[index];
9398 for(var i = 0, len = records.length; i < len; i++){
9399 var d = this.prepareData(records[i].data, i, records[i]);
9401 this.tpl.insertBefore(n, d);
9404 this.tpl.append(this.el, d);
9407 this.updateIndexes(index);
9410 onRemove : function(ds, record, index){
9411 Roo.log('onRemove');
9412 this.clearSelections();
9413 var el = this.dataName ?
9414 this.el.child('.roo-tpl-' + this.dataName) :
9417 el.dom.removeChild(this.nodes[index]);
9418 this.updateIndexes(index);
9422 * Refresh an individual node.
9423 * @param {Number} index
9425 refreshNode : function(index){
9426 this.onUpdate(this.store, this.store.getAt(index));
9429 updateIndexes : function(startIndex, endIndex){
9430 var ns = this.nodes;
9431 startIndex = startIndex || 0;
9432 endIndex = endIndex || ns.length - 1;
9433 for(var i = startIndex; i <= endIndex; i++){
9434 ns[i].nodeIndex = i;
9439 * Changes the data store this view uses and refresh the view.
9440 * @param {Store} store
9442 setStore : function(store, initial){
9443 if(!initial && this.store){
9444 this.store.un("datachanged", this.refresh);
9445 this.store.un("add", this.onAdd);
9446 this.store.un("remove", this.onRemove);
9447 this.store.un("update", this.onUpdate);
9448 this.store.un("clear", this.refresh);
9449 this.store.un("beforeload", this.onBeforeLoad);
9450 this.store.un("load", this.onLoad);
9451 this.store.un("loadexception", this.onLoad);
9455 store.on("datachanged", this.refresh, this);
9456 store.on("add", this.onAdd, this);
9457 store.on("remove", this.onRemove, this);
9458 store.on("update", this.onUpdate, this);
9459 store.on("clear", this.refresh, this);
9460 store.on("beforeload", this.onBeforeLoad, this);
9461 store.on("load", this.onLoad, this);
9462 store.on("loadexception", this.onLoad, this);
9470 * onbeforeLoad - masks the loading area.
9473 onBeforeLoad : function(store,opts)
9475 Roo.log('onBeforeLoad');
9479 this.el.mask(this.mask ? this.mask : "Loading" );
9481 onLoad : function ()
9488 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9489 * @param {HTMLElement} node
9490 * @return {HTMLElement} The template node
9492 findItemFromChild : function(node){
9493 var el = this.dataName ?
9494 this.el.child('.roo-tpl-' + this.dataName,true) :
9497 if(!node || node.parentNode == el){
9500 var p = node.parentNode;
9501 while(p && p != el){
9502 if(p.parentNode == el){
9511 onClick : function(e){
9512 var item = this.findItemFromChild(e.getTarget());
9514 var index = this.indexOf(item);
9515 if(this.onItemClick(item, index, e) !== false){
9516 this.fireEvent("click", this, index, item, e);
9519 this.clearSelections();
9524 onContextMenu : function(e){
9525 var item = this.findItemFromChild(e.getTarget());
9527 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9532 onDblClick : function(e){
9533 var item = this.findItemFromChild(e.getTarget());
9535 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9539 onItemClick : function(item, index, e)
9541 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9544 if (this.toggleSelect) {
9545 var m = this.isSelected(item) ? 'unselect' : 'select';
9548 _t[m](item, true, false);
9551 if(this.multiSelect || this.singleSelect){
9552 if(this.multiSelect && e.shiftKey && this.lastSelection){
9553 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9555 this.select(item, this.multiSelect && e.ctrlKey);
9556 this.lastSelection = item;
9564 * Get the number of selected nodes.
9567 getSelectionCount : function(){
9568 return this.selections.length;
9572 * Get the currently selected nodes.
9573 * @return {Array} An array of HTMLElements
9575 getSelectedNodes : function(){
9576 return this.selections;
9580 * Get the indexes of the selected nodes.
9583 getSelectedIndexes : function(){
9584 var indexes = [], s = this.selections;
9585 for(var i = 0, len = s.length; i < len; i++){
9586 indexes.push(s[i].nodeIndex);
9592 * Clear all selections
9593 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9595 clearSelections : function(suppressEvent){
9596 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9597 this.cmp.elements = this.selections;
9598 this.cmp.removeClass(this.selectedClass);
9599 this.selections = [];
9601 this.fireEvent("selectionchange", this, this.selections);
9607 * Returns true if the passed node is selected
9608 * @param {HTMLElement/Number} node The node or node index
9611 isSelected : function(node){
9612 var s = this.selections;
9616 node = this.getNode(node);
9617 return s.indexOf(node) !== -1;
9622 * @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
9623 * @param {Boolean} keepExisting (optional) true to keep existing selections
9624 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9626 select : function(nodeInfo, keepExisting, suppressEvent){
9627 if(nodeInfo instanceof Array){
9629 this.clearSelections(true);
9631 for(var i = 0, len = nodeInfo.length; i < len; i++){
9632 this.select(nodeInfo[i], true, true);
9636 var node = this.getNode(nodeInfo);
9637 if(!node || this.isSelected(node)){
9638 return; // already selected.
9641 this.clearSelections(true);
9643 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9644 Roo.fly(node).addClass(this.selectedClass);
9645 this.selections.push(node);
9647 this.fireEvent("selectionchange", this, this.selections);
9655 * @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
9656 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9657 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9659 unselect : function(nodeInfo, keepExisting, suppressEvent)
9661 if(nodeInfo instanceof Array){
9662 Roo.each(this.selections, function(s) {
9663 this.unselect(s, nodeInfo);
9667 var node = this.getNode(nodeInfo);
9668 if(!node || !this.isSelected(node)){
9669 Roo.log("not selected");
9670 return; // not selected.
9674 Roo.each(this.selections, function(s) {
9676 Roo.fly(node).removeClass(this.selectedClass);
9683 this.selections= ns;
9684 this.fireEvent("selectionchange", this, this.selections);
9688 * Gets a template node.
9689 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9690 * @return {HTMLElement} The node or null if it wasn't found
9692 getNode : function(nodeInfo){
9693 if(typeof nodeInfo == "string"){
9694 return document.getElementById(nodeInfo);
9695 }else if(typeof nodeInfo == "number"){
9696 return this.nodes[nodeInfo];
9702 * Gets a range template nodes.
9703 * @param {Number} startIndex
9704 * @param {Number} endIndex
9705 * @return {Array} An array of nodes
9707 getNodes : function(start, end){
9708 var ns = this.nodes;
9710 end = typeof end == "undefined" ? ns.length - 1 : end;
9713 for(var i = start; i <= end; i++){
9717 for(var i = start; i >= end; i--){
9725 * Finds the index of the passed node
9726 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9727 * @return {Number} The index of the node or -1
9729 indexOf : function(node){
9730 node = this.getNode(node);
9731 if(typeof node.nodeIndex == "number"){
9732 return node.nodeIndex;
9734 var ns = this.nodes;
9735 for(var i = 0, len = ns.length; i < len; i++){
9746 * based on jquery fullcalendar
9750 Roo.bootstrap = Roo.bootstrap || {};
9752 * @class Roo.bootstrap.Calendar
9753 * @extends Roo.bootstrap.Component
9754 * Bootstrap Calendar class
9755 * @cfg {Boolean} loadMask (true|false) default false
9758 * Create a new Container
9759 * @param {Object} config The config object
9764 Roo.bootstrap.Calendar = function(config){
9765 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9769 * Fires when a date is selected
9770 * @param {DatePicker} this
9771 * @param {Date} date The selected date
9775 * @event monthchange
9776 * Fires when the displayed month changes
9777 * @param {DatePicker} this
9778 * @param {Date} date The selected month
9780 'monthchange': true,
9783 * Fires when mouse over an event
9784 * @param {Calendar} this
9785 * @param {event} Event
9790 * Fires when the mouse leaves an
9791 * @param {Calendar} this
9797 * Fires when the mouse click an
9798 * @param {Calendar} this
9807 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9810 * @cfg {Number} startDay
9811 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9817 getAutoCreate : function(){
9820 var fc_button = function(name, corner, style, content ) {
9821 return Roo.apply({},{
9823 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9825 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9828 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9836 style : 'width:100%',
9843 cls : 'fc-header-left',
9845 fc_button('prev', 'left', 'arrow', '‹' ),
9846 fc_button('next', 'right', 'arrow', '›' ),
9847 { tag: 'span', cls: 'fc-header-space' },
9848 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9856 cls : 'fc-header-center',
9860 cls: 'fc-header-title',
9863 html : 'month / year'
9871 cls : 'fc-header-right',
9873 /* fc_button('month', 'left', '', 'month' ),
9874 fc_button('week', '', '', 'week' ),
9875 fc_button('day', 'right', '', 'day' )
9887 var cal_heads = function() {
9889 // fixme - handle this.
9891 for (var i =0; i < Date.dayNames.length; i++) {
9892 var d = Date.dayNames[i];
9895 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9896 html : d.substring(0,3)
9900 ret[0].cls += ' fc-first';
9901 ret[6].cls += ' fc-last';
9904 var cal_cell = function(n) {
9907 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9912 cls: 'fc-day-number',
9916 cls: 'fc-day-content',
9920 style: 'position: relative;' // height: 17px;
9932 var cal_rows = function() {
9935 for (var r = 0; r < 6; r++) {
9942 for (var i =0; i < Date.dayNames.length; i++) {
9943 var d = Date.dayNames[i];
9944 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9947 row.cn[0].cls+=' fc-first';
9948 row.cn[0].cn[0].style = 'min-height:90px';
9949 row.cn[6].cls+=' fc-last';
9953 ret[0].cls += ' fc-first';
9954 ret[4].cls += ' fc-prev-last';
9955 ret[5].cls += ' fc-last';
9962 cls: 'fc-border-separate',
9963 style : 'width:100%',
9971 cls : 'fc-first fc-last',
9990 style : "position: relative;",
9993 cls : 'fc-view fc-view-month fc-grid',
9994 style : 'position: relative',
9995 unselectable : 'on',
9998 cls : 'fc-event-container',
9999 style : 'position:absolute;z-index:8;top:0;left:0;'
10017 initEvents : function()
10020 throw "can not find store for calendar";
10026 style: "text-align:center",
10030 style: "background-color:white;width:50%;margin:250 auto",
10034 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10045 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10047 var size = this.el.select('.fc-content', true).first().getSize();
10048 this.maskEl.setSize(size.width, size.height);
10049 this.maskEl.enableDisplayMode("block");
10050 if(!this.loadMask){
10051 this.maskEl.hide();
10054 this.store = Roo.factory(this.store, Roo.data);
10055 this.store.on('load', this.onLoad, this);
10056 this.store.on('beforeload', this.onBeforeLoad, this);
10060 this.cells = this.el.select('.fc-day',true);
10061 //Roo.log(this.cells);
10062 this.textNodes = this.el.query('.fc-day-number');
10063 this.cells.addClassOnOver('fc-state-hover');
10065 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10066 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10067 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10068 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10070 this.on('monthchange', this.onMonthChange, this);
10072 this.update(new Date().clearTime());
10075 resize : function() {
10076 var sz = this.el.getSize();
10078 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10079 this.el.select('.fc-day-content div',true).setHeight(34);
10084 showPrevMonth : function(e){
10085 this.update(this.activeDate.add("mo", -1));
10087 showToday : function(e){
10088 this.update(new Date().clearTime());
10091 showNextMonth : function(e){
10092 this.update(this.activeDate.add("mo", 1));
10096 showPrevYear : function(){
10097 this.update(this.activeDate.add("y", -1));
10101 showNextYear : function(){
10102 this.update(this.activeDate.add("y", 1));
10107 update : function(date)
10109 var vd = this.activeDate;
10110 this.activeDate = date;
10111 // if(vd && this.el){
10112 // var t = date.getTime();
10113 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10114 // Roo.log('using add remove');
10116 // this.fireEvent('monthchange', this, date);
10118 // this.cells.removeClass("fc-state-highlight");
10119 // this.cells.each(function(c){
10120 // if(c.dateValue == t){
10121 // c.addClass("fc-state-highlight");
10122 // setTimeout(function(){
10123 // try{c.dom.firstChild.focus();}catch(e){}
10133 var days = date.getDaysInMonth();
10135 var firstOfMonth = date.getFirstDateOfMonth();
10136 var startingPos = firstOfMonth.getDay()-this.startDay;
10138 if(startingPos < this.startDay){
10142 var pm = date.add(Date.MONTH, -1);
10143 var prevStart = pm.getDaysInMonth()-startingPos;
10145 this.cells = this.el.select('.fc-day',true);
10146 this.textNodes = this.el.query('.fc-day-number');
10147 this.cells.addClassOnOver('fc-state-hover');
10149 var cells = this.cells.elements;
10150 var textEls = this.textNodes;
10152 Roo.each(cells, function(cell){
10153 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10156 days += startingPos;
10158 // convert everything to numbers so it's fast
10159 var day = 86400000;
10160 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10163 //Roo.log(prevStart);
10165 var today = new Date().clearTime().getTime();
10166 var sel = date.clearTime().getTime();
10167 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10168 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10169 var ddMatch = this.disabledDatesRE;
10170 var ddText = this.disabledDatesText;
10171 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10172 var ddaysText = this.disabledDaysText;
10173 var format = this.format;
10175 var setCellClass = function(cal, cell){
10177 //Roo.log('set Cell Class');
10179 var t = d.getTime();
10183 cell.dateValue = t;
10185 cell.className += " fc-today";
10186 cell.className += " fc-state-highlight";
10187 cell.title = cal.todayText;
10190 // disable highlight in other month..
10191 //cell.className += " fc-state-highlight";
10196 cell.className = " fc-state-disabled";
10197 cell.title = cal.minText;
10201 cell.className = " fc-state-disabled";
10202 cell.title = cal.maxText;
10206 if(ddays.indexOf(d.getDay()) != -1){
10207 cell.title = ddaysText;
10208 cell.className = " fc-state-disabled";
10211 if(ddMatch && format){
10212 var fvalue = d.dateFormat(format);
10213 if(ddMatch.test(fvalue)){
10214 cell.title = ddText.replace("%0", fvalue);
10215 cell.className = " fc-state-disabled";
10219 if (!cell.initialClassName) {
10220 cell.initialClassName = cell.dom.className;
10223 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10228 for(; i < startingPos; i++) {
10229 textEls[i].innerHTML = (++prevStart);
10230 d.setDate(d.getDate()+1);
10232 cells[i].className = "fc-past fc-other-month";
10233 setCellClass(this, cells[i]);
10238 for(; i < days; i++){
10239 intDay = i - startingPos + 1;
10240 textEls[i].innerHTML = (intDay);
10241 d.setDate(d.getDate()+1);
10243 cells[i].className = ''; // "x-date-active";
10244 setCellClass(this, cells[i]);
10248 for(; i < 42; i++) {
10249 textEls[i].innerHTML = (++extraDays);
10250 d.setDate(d.getDate()+1);
10252 cells[i].className = "fc-future fc-other-month";
10253 setCellClass(this, cells[i]);
10256 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10258 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10260 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10261 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10263 if(totalRows != 6){
10264 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10265 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10268 this.fireEvent('monthchange', this, date);
10272 if(!this.internalRender){
10273 var main = this.el.dom.firstChild;
10274 var w = main.offsetWidth;
10275 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10276 Roo.fly(main).setWidth(w);
10277 this.internalRender = true;
10278 // opera does not respect the auto grow header center column
10279 // then, after it gets a width opera refuses to recalculate
10280 // without a second pass
10281 if(Roo.isOpera && !this.secondPass){
10282 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10283 this.secondPass = true;
10284 this.update.defer(10, this, [date]);
10291 findCell : function(dt) {
10292 dt = dt.clearTime().getTime();
10294 this.cells.each(function(c){
10295 //Roo.log("check " +c.dateValue + '?=' + dt);
10296 if(c.dateValue == dt){
10306 findCells : function(ev) {
10307 var s = ev.start.clone().clearTime().getTime();
10309 var e= ev.end.clone().clearTime().getTime();
10312 this.cells.each(function(c){
10313 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10315 if(c.dateValue > e){
10318 if(c.dateValue < s){
10327 findBestRow: function(cells)
10331 for (var i =0 ; i < cells.length;i++) {
10332 ret = Math.max(cells[i].rows || 0,ret);
10339 addItem : function(ev)
10341 // look for vertical location slot in
10342 var cells = this.findCells(ev);
10344 ev.row = this.findBestRow(cells);
10346 // work out the location.
10350 for(var i =0; i < cells.length; i++) {
10358 if (crow.start.getY() == cells[i].getY()) {
10360 crow.end = cells[i];
10376 for (var i = 0; i < cells.length;i++) {
10377 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10381 this.calevents.push(ev);
10384 clearEvents: function() {
10386 if(!this.calevents){
10390 Roo.each(this.cells.elements, function(c){
10394 Roo.each(this.calevents, function(e) {
10395 Roo.each(e.els, function(el) {
10396 el.un('mouseenter' ,this.onEventEnter, this);
10397 el.un('mouseleave' ,this.onEventLeave, this);
10404 renderEvents: function()
10406 // first make sure there is enough space..
10408 this.cells.each(function(c) {
10410 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10413 for (var e = 0; e < this.calevents.length; e++) {
10414 var ev = this.calevents[e];
10415 var cells = ev.cells;
10416 var rows = ev.rows;
10418 for(var i =0; i < rows.length; i++) {
10421 // how many rows should it span..
10424 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10425 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10427 unselectable : "on",
10430 cls: 'fc-event-inner',
10434 // cls: 'fc-event-time',
10435 // html : cells.length > 1 ? '' : ev.time
10439 cls: 'fc-event-title',
10440 html : String.format('{0}', ev.title)
10447 cls: 'ui-resizable-handle ui-resizable-e',
10448 html : '  '
10454 cfg.cls += ' fc-event-start';
10456 if ((i+1) == rows.length) {
10457 cfg.cls += ' fc-event-end';
10460 var ctr = this.el.select('.fc-event-container',true).first();
10461 var cg = ctr.createChild(cfg);
10463 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10464 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10465 cg.on('click', this.onEventClick, this, ev);
10469 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10470 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10472 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10473 cg.setWidth(ebox.right - sbox.x -2);
10481 onEventEnter: function (e, el,event,d) {
10482 this.fireEvent('evententer', this, el, event);
10485 onEventLeave: function (e, el,event,d) {
10486 this.fireEvent('eventleave', this, el, event);
10489 onEventClick: function (e, el,event,d) {
10490 this.fireEvent('eventclick', this, el, event);
10493 onMonthChange: function () {
10497 onLoad: function ()
10499 this.calevents = [];
10502 if(this.store.getCount() > 0){
10503 this.store.data.each(function(d){
10506 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10507 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10508 time : d.data.start_time,
10509 title : d.data.title,
10510 description : d.data.description,
10511 venue : d.data.venue
10516 this.renderEvents();
10519 this.maskEl.hide();
10523 onBeforeLoad: function()
10525 this.clearEvents();
10528 this.maskEl.show();
10542 * @class Roo.bootstrap.Popover
10543 * @extends Roo.bootstrap.Component
10544 * Bootstrap Popover class
10545 * @cfg {String} html contents of the popover (or false to use children..)
10546 * @cfg {String} title of popover (or false to hide)
10547 * @cfg {String} placement how it is placed
10548 * @cfg {String} trigger click || hover (or false to trigger manually)
10549 * @cfg {String} over what (parent or false to trigger manually.)
10552 * Create a new Popover
10553 * @param {Object} config The config object
10556 Roo.bootstrap.Popover = function(config){
10557 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10560 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10562 title: 'Fill in a title',
10565 placement : 'right',
10566 trigger : 'hover', // hover
10570 can_build_overlaid : false,
10572 getChildContainer : function()
10574 return this.el.select('.popover-content',true).first();
10577 getAutoCreate : function(){
10578 Roo.log('make popover?');
10580 cls : 'popover roo-dynamic',
10581 style: 'display:block',
10587 cls : 'popover-inner',
10591 cls: 'popover-title',
10595 cls : 'popover-content',
10606 setTitle: function(str)
10608 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10610 setContent: function(str)
10612 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10614 // as it get's added to the bottom of the page.
10615 onRender : function(ct, position)
10617 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10619 var cfg = Roo.apply({}, this.getAutoCreate());
10623 cfg.cls += ' ' + this.cls;
10626 cfg.style = this.style;
10628 Roo.log("adding to ")
10629 this.el = Roo.get(document.body).createChild(cfg, position);
10635 initEvents : function()
10637 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10638 this.el.enableDisplayMode('block');
10640 if (this.over === false) {
10643 if (this.triggers === false) {
10646 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10647 var triggers = this.trigger ? this.trigger.split(' ') : [];
10648 Roo.each(triggers, function(trigger) {
10650 if (trigger == 'click') {
10651 on_el.on('click', this.toggle, this);
10652 } else if (trigger != 'manual') {
10653 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10654 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10656 on_el.on(eventIn ,this.enter, this);
10657 on_el.on(eventOut, this.leave, this);
10668 toggle : function () {
10669 this.hoverState == 'in' ? this.leave() : this.enter();
10672 enter : function () {
10675 clearTimeout(this.timeout);
10677 this.hoverState = 'in'
10679 if (!this.delay || !this.delay.show) {
10684 this.timeout = setTimeout(function () {
10685 if (_t.hoverState == 'in') {
10688 }, this.delay.show)
10690 leave : function() {
10691 clearTimeout(this.timeout);
10693 this.hoverState = 'out'
10695 if (!this.delay || !this.delay.hide) {
10700 this.timeout = setTimeout(function () {
10701 if (_t.hoverState == 'out') {
10704 }, this.delay.hide)
10707 show : function (on_el)
10710 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10713 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10714 if (this.html !== false) {
10715 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10717 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10718 if (!this.title.length) {
10719 this.el.select('.popover-title',true).hide();
10722 var placement = typeof this.placement == 'function' ?
10723 this.placement.call(this, this.el, on_el) :
10726 var autoToken = /\s?auto?\s?/i;
10727 var autoPlace = autoToken.test(placement);
10729 placement = placement.replace(autoToken, '') || 'top';
10733 //this.el.setXY([0,0]);
10735 this.el.dom.style.display='block';
10736 this.el.addClass(placement);
10738 //this.el.appendTo(on_el);
10740 var p = this.getPosition();
10741 var box = this.el.getBox();
10746 var align = Roo.bootstrap.Popover.alignment[placement]
10747 this.el.alignTo(on_el, align[0],align[1]);
10748 //var arrow = this.el.select('.arrow',true).first();
10749 //arrow.set(align[2],
10751 this.el.addClass('in');
10752 this.hoverState = null;
10754 if (this.el.hasClass('fade')) {
10761 this.el.setXY([0,0]);
10762 this.el.removeClass('in');
10769 Roo.bootstrap.Popover.alignment = {
10770 'left' : ['r-l', [-10,0], 'right'],
10771 'right' : ['l-r', [10,0], 'left'],
10772 'bottom' : ['t-b', [0,10], 'top'],
10773 'top' : [ 'b-t', [0,-10], 'bottom']
10784 * @class Roo.bootstrap.Progress
10785 * @extends Roo.bootstrap.Component
10786 * Bootstrap Progress class
10787 * @cfg {Boolean} striped striped of the progress bar
10788 * @cfg {Boolean} active animated of the progress bar
10792 * Create a new Progress
10793 * @param {Object} config The config object
10796 Roo.bootstrap.Progress = function(config){
10797 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10800 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10805 getAutoCreate : function(){
10813 cfg.cls += ' progress-striped';
10817 cfg.cls += ' active';
10836 * @class Roo.bootstrap.ProgressBar
10837 * @extends Roo.bootstrap.Component
10838 * Bootstrap ProgressBar class
10839 * @cfg {Number} aria_valuenow aria-value now
10840 * @cfg {Number} aria_valuemin aria-value min
10841 * @cfg {Number} aria_valuemax aria-value max
10842 * @cfg {String} label label for the progress bar
10843 * @cfg {String} panel (success | info | warning | danger )
10844 * @cfg {String} role role of the progress bar
10845 * @cfg {String} sr_only text
10849 * Create a new ProgressBar
10850 * @param {Object} config The config object
10853 Roo.bootstrap.ProgressBar = function(config){
10854 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10857 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10861 aria_valuemax : 100,
10867 getAutoCreate : function()
10872 cls: 'progress-bar',
10873 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10885 cfg.role = this.role;
10888 if(this.aria_valuenow){
10889 cfg['aria-valuenow'] = this.aria_valuenow;
10892 if(this.aria_valuemin){
10893 cfg['aria-valuemin'] = this.aria_valuemin;
10896 if(this.aria_valuemax){
10897 cfg['aria-valuemax'] = this.aria_valuemax;
10900 if(this.label && !this.sr_only){
10901 cfg.html = this.label;
10905 cfg.cls += ' progress-bar-' + this.panel;
10911 update : function(aria_valuenow)
10913 this.aria_valuenow = aria_valuenow;
10915 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10930 * @class Roo.bootstrap.TabPanel
10931 * @extends Roo.bootstrap.Component
10932 * Bootstrap TabPanel class
10933 * @cfg {Boolean} active panel active
10934 * @cfg {String} html panel content
10935 * @cfg {String} tabId tab relate id
10939 * Create a new TabPanel
10940 * @param {Object} config The config object
10943 Roo.bootstrap.TabPanel = function(config){
10944 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10947 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10953 getAutoCreate : function(){
10957 html: this.html || ''
10961 cfg.cls += ' active';
10965 cfg.tabId = this.tabId;
10983 * @class Roo.bootstrap.DateField
10984 * @extends Roo.bootstrap.Input
10985 * Bootstrap DateField class
10986 * @cfg {Number} weekStart default 0
10987 * @cfg {Number} weekStart default 0
10988 * @cfg {Number} viewMode default empty, (months|years)
10989 * @cfg {Number} minViewMode default empty, (months|years)
10990 * @cfg {Number} startDate default -Infinity
10991 * @cfg {Number} endDate default Infinity
10992 * @cfg {Boolean} todayHighlight default false
10993 * @cfg {Boolean} todayBtn default false
10994 * @cfg {Boolean} calendarWeeks default false
10995 * @cfg {Object} daysOfWeekDisabled default empty
10997 * @cfg {Boolean} keyboardNavigation default true
10998 * @cfg {String} language default en
11001 * Create a new DateField
11002 * @param {Object} config The config object
11005 Roo.bootstrap.DateField = function(config){
11006 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11010 * Fires when this field show.
11011 * @param {Roo.bootstrap.DateField} this
11012 * @param {Mixed} date The date value
11017 * Fires when this field hide.
11018 * @param {Roo.bootstrap.DateField} this
11019 * @param {Mixed} date The date value
11024 * Fires when select a date.
11025 * @param {Roo.bootstrap.DateField} this
11026 * @param {Mixed} date The date value
11032 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
11035 * @cfg {String} format
11036 * The default date format string which can be overriden for localization support. The format must be
11037 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11041 * @cfg {String} altFormats
11042 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11043 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11045 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11053 todayHighlight : false,
11059 keyboardNavigation: true,
11061 calendarWeeks: false,
11063 startDate: -Infinity,
11067 daysOfWeekDisabled: [],
11071 UTCDate: function()
11073 return new Date(Date.UTC.apply(Date, arguments));
11076 UTCToday: function()
11078 var today = new Date();
11079 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11082 getDate: function() {
11083 var d = this.getUTCDate();
11084 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11087 getUTCDate: function() {
11091 setDate: function(d) {
11092 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11095 setUTCDate: function(d) {
11097 this.setValue(this.formatDate(this.date));
11100 onRender: function(ct, position)
11103 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11105 this.language = this.language || 'en';
11106 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11107 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11109 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11110 this.format = this.format || 'm/d/y';
11111 this.isInline = false;
11112 this.isInput = true;
11113 this.component = this.el.select('.add-on', true).first() || false;
11114 this.component = (this.component && this.component.length === 0) ? false : this.component;
11115 this.hasInput = this.component && this.inputEL().length;
11117 if (typeof(this.minViewMode === 'string')) {
11118 switch (this.minViewMode) {
11120 this.minViewMode = 1;
11123 this.minViewMode = 2;
11126 this.minViewMode = 0;
11131 if (typeof(this.viewMode === 'string')) {
11132 switch (this.viewMode) {
11145 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11147 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11149 this.picker().on('mousedown', this.onMousedown, this);
11150 this.picker().on('click', this.onClick, this);
11152 this.picker().addClass('datepicker-dropdown');
11154 this.startViewMode = this.viewMode;
11157 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11158 if(!this.calendarWeeks){
11163 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11164 v.attr('colspan', function(i, val){
11165 return parseInt(val) + 1;
11170 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11172 this.setStartDate(this.startDate);
11173 this.setEndDate(this.endDate);
11175 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11182 if(this.isInline) {
11187 picker : function()
11189 return this.el.select('.datepicker', true).first();
11192 fillDow: function()
11194 var dowCnt = this.weekStart;
11203 if(this.calendarWeeks){
11211 while (dowCnt < this.weekStart + 7) {
11215 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11219 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11222 fillMonths: function()
11225 var months = this.picker().select('>.datepicker-months td', true).first();
11227 months.dom.innerHTML = '';
11233 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11236 months.createChild(month);
11241 update: function(){
11243 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11245 if (this.date < this.startDate) {
11246 this.viewDate = new Date(this.startDate);
11247 } else if (this.date > this.endDate) {
11248 this.viewDate = new Date(this.endDate);
11250 this.viewDate = new Date(this.date);
11257 var d = new Date(this.viewDate),
11258 year = d.getUTCFullYear(),
11259 month = d.getUTCMonth(),
11260 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11261 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11262 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11263 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11264 currentDate = this.date && this.date.valueOf(),
11265 today = this.UTCToday();
11267 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11269 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11271 // this.picker.select('>tfoot th.today').
11272 // .text(dates[this.language].today)
11273 // .toggle(this.todayBtn !== false);
11275 this.updateNavArrows();
11278 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11280 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11282 prevMonth.setUTCDate(day);
11284 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11286 var nextMonth = new Date(prevMonth);
11288 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11290 nextMonth = nextMonth.valueOf();
11292 var fillMonths = false;
11294 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11296 while(prevMonth.valueOf() < nextMonth) {
11299 if (prevMonth.getUTCDay() === this.weekStart) {
11301 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11309 if(this.calendarWeeks){
11310 // ISO 8601: First week contains first thursday.
11311 // ISO also states week starts on Monday, but we can be more abstract here.
11313 // Start of current week: based on weekstart/current date
11314 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11315 // Thursday of this week
11316 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11317 // First Thursday of year, year from thursday
11318 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11319 // Calendar week: ms between thursdays, div ms per day, div 7 days
11320 calWeek = (th - yth) / 864e5 / 7 + 1;
11322 fillMonths.cn.push({
11330 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11332 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11335 if (this.todayHighlight &&
11336 prevMonth.getUTCFullYear() == today.getFullYear() &&
11337 prevMonth.getUTCMonth() == today.getMonth() &&
11338 prevMonth.getUTCDate() == today.getDate()) {
11339 clsName += ' today';
11342 if (currentDate && prevMonth.valueOf() === currentDate) {
11343 clsName += ' active';
11346 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11347 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11348 clsName += ' disabled';
11351 fillMonths.cn.push({
11353 cls: 'day ' + clsName,
11354 html: prevMonth.getDate()
11357 prevMonth.setDate(prevMonth.getDate()+1);
11360 var currentYear = this.date && this.date.getUTCFullYear();
11361 var currentMonth = this.date && this.date.getUTCMonth();
11363 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11365 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11366 v.removeClass('active');
11368 if(currentYear === year && k === currentMonth){
11369 v.addClass('active');
11372 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11373 v.addClass('disabled');
11379 year = parseInt(year/10, 10) * 10;
11381 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11383 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11386 for (var i = -1; i < 11; i++) {
11387 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11389 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11397 showMode: function(dir) {
11399 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11401 Roo.each(this.picker().select('>div',true).elements, function(v){
11402 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11405 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11410 if(this.isInline) return;
11412 this.picker().removeClass(['bottom', 'top']);
11414 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11416 * place to the top of element!
11420 this.picker().addClass('top');
11421 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11426 this.picker().addClass('bottom');
11428 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11431 parseDate : function(value){
11432 if(!value || value instanceof Date){
11435 var v = Date.parseDate(value, this.format);
11436 if (!v && this.useIso) {
11437 v = Date.parseDate(value, 'Y-m-d');
11439 if(!v && this.altFormats){
11440 if(!this.altFormatsArray){
11441 this.altFormatsArray = this.altFormats.split("|");
11443 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11444 v = Date.parseDate(value, this.altFormatsArray[i]);
11450 formatDate : function(date, fmt){
11451 return (!date || !(date instanceof Date)) ?
11452 date : date.dateFormat(fmt || this.format);
11455 onFocus : function()
11457 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11461 onBlur : function()
11463 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11469 this.picker().show();
11473 this.fireEvent('show', this, this.date);
11478 if(this.isInline) return;
11479 this.picker().hide();
11480 this.viewMode = this.startViewMode;
11483 this.fireEvent('hide', this, this.date);
11487 onMousedown: function(e){
11488 e.stopPropagation();
11489 e.preventDefault();
11492 keyup: function(e){
11493 Roo.bootstrap.DateField.superclass.keyup.call(this);
11498 setValue: function(v){
11499 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11501 this.fireEvent('select', this, this.date);
11505 fireKey: function(e){
11506 if (!this.picker().isVisible()){
11507 if (e.keyCode == 27) // allow escape to hide and re-show picker
11511 var dateChanged = false,
11513 newDate, newViewDate;
11517 e.preventDefault();
11521 if (!this.keyboardNavigation) break;
11522 dir = e.keyCode == 37 ? -1 : 1;
11525 newDate = this.moveYear(this.date, dir);
11526 newViewDate = this.moveYear(this.viewDate, dir);
11527 } else if (e.shiftKey){
11528 newDate = this.moveMonth(this.date, dir);
11529 newViewDate = this.moveMonth(this.viewDate, dir);
11531 newDate = new Date(this.date);
11532 newDate.setUTCDate(this.date.getUTCDate() + dir);
11533 newViewDate = new Date(this.viewDate);
11534 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11536 if (this.dateWithinRange(newDate)){
11537 this.date = newDate;
11538 this.viewDate = newViewDate;
11539 this.setValue(this.formatDate(this.date));
11541 e.preventDefault();
11542 dateChanged = true;
11547 if (!this.keyboardNavigation) break;
11548 dir = e.keyCode == 38 ? -1 : 1;
11550 newDate = this.moveYear(this.date, dir);
11551 newViewDate = this.moveYear(this.viewDate, dir);
11552 } else if (e.shiftKey){
11553 newDate = this.moveMonth(this.date, dir);
11554 newViewDate = this.moveMonth(this.viewDate, dir);
11556 newDate = new Date(this.date);
11557 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11558 newViewDate = new Date(this.viewDate);
11559 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11561 if (this.dateWithinRange(newDate)){
11562 this.date = newDate;
11563 this.viewDate = newViewDate;
11564 this.setValue(this.formatDate(this.date));
11566 e.preventDefault();
11567 dateChanged = true;
11571 this.setValue(this.formatDate(this.date));
11573 e.preventDefault();
11576 this.setValue(this.formatDate(this.date));
11583 onClick: function(e) {
11584 e.stopPropagation();
11585 e.preventDefault();
11587 var target = e.getTarget();
11589 if(target.nodeName.toLowerCase() === 'i'){
11590 target = Roo.get(target).dom.parentNode;
11593 var nodeName = target.nodeName;
11594 var className = target.className;
11595 var html = target.innerHTML;
11597 switch(nodeName.toLowerCase()) {
11599 switch(className) {
11605 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11606 switch(this.viewMode){
11608 this.viewDate = this.moveMonth(this.viewDate, dir);
11612 this.viewDate = this.moveYear(this.viewDate, dir);
11618 var date = new Date();
11619 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11621 this.setValue(this.formatDate(this.date));
11627 if (className.indexOf('disabled') === -1) {
11628 this.viewDate.setUTCDate(1);
11629 if (className.indexOf('month') !== -1) {
11630 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11632 var year = parseInt(html, 10) || 0;
11633 this.viewDate.setUTCFullYear(year);
11642 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11643 var day = parseInt(html, 10) || 1;
11644 var year = this.viewDate.getUTCFullYear(),
11645 month = this.viewDate.getUTCMonth();
11647 if (className.indexOf('old') !== -1) {
11654 } else if (className.indexOf('new') !== -1) {
11662 this.date = this.UTCDate(year, month, day,0,0,0,0);
11663 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11665 this.setValue(this.formatDate(this.date));
11672 setStartDate: function(startDate){
11673 this.startDate = startDate || -Infinity;
11674 if (this.startDate !== -Infinity) {
11675 this.startDate = this.parseDate(this.startDate);
11678 this.updateNavArrows();
11681 setEndDate: function(endDate){
11682 this.endDate = endDate || Infinity;
11683 if (this.endDate !== Infinity) {
11684 this.endDate = this.parseDate(this.endDate);
11687 this.updateNavArrows();
11690 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11691 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11692 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11693 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11695 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11696 return parseInt(d, 10);
11699 this.updateNavArrows();
11702 updateNavArrows: function() {
11703 var d = new Date(this.viewDate),
11704 year = d.getUTCFullYear(),
11705 month = d.getUTCMonth();
11707 Roo.each(this.picker().select('.prev', true).elements, function(v){
11709 switch (this.viewMode) {
11712 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11718 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11725 Roo.each(this.picker().select('.next', true).elements, function(v){
11727 switch (this.viewMode) {
11730 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11736 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11744 moveMonth: function(date, dir){
11745 if (!dir) return date;
11746 var new_date = new Date(date.valueOf()),
11747 day = new_date.getUTCDate(),
11748 month = new_date.getUTCMonth(),
11749 mag = Math.abs(dir),
11751 dir = dir > 0 ? 1 : -1;
11754 // If going back one month, make sure month is not current month
11755 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11757 return new_date.getUTCMonth() == month;
11759 // If going forward one month, make sure month is as expected
11760 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11762 return new_date.getUTCMonth() != new_month;
11764 new_month = month + dir;
11765 new_date.setUTCMonth(new_month);
11766 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11767 if (new_month < 0 || new_month > 11)
11768 new_month = (new_month + 12) % 12;
11770 // For magnitudes >1, move one month at a time...
11771 for (var i=0; i<mag; i++)
11772 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11773 new_date = this.moveMonth(new_date, dir);
11774 // ...then reset the day, keeping it in the new month
11775 new_month = new_date.getUTCMonth();
11776 new_date.setUTCDate(day);
11778 return new_month != new_date.getUTCMonth();
11781 // Common date-resetting loop -- if date is beyond end of month, make it
11784 new_date.setUTCDate(--day);
11785 new_date.setUTCMonth(new_month);
11790 moveYear: function(date, dir){
11791 return this.moveMonth(date, dir*12);
11794 dateWithinRange: function(date){
11795 return date >= this.startDate && date <= this.endDate;
11799 remove: function() {
11800 this.picker().remove();
11805 Roo.apply(Roo.bootstrap.DateField, {
11816 html: '<i class="icon-arrow-left"/>'
11826 html: '<i class="icon-arrow-right"/>'
11868 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11869 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11870 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11871 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11872 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11885 navFnc: 'FullYear',
11890 navFnc: 'FullYear',
11895 Roo.apply(Roo.bootstrap.DateField, {
11899 cls: 'datepicker dropdown-menu',
11903 cls: 'datepicker-days',
11907 cls: 'table-condensed',
11909 Roo.bootstrap.DateField.head,
11913 Roo.bootstrap.DateField.footer
11920 cls: 'datepicker-months',
11924 cls: 'table-condensed',
11926 Roo.bootstrap.DateField.head,
11927 Roo.bootstrap.DateField.content,
11928 Roo.bootstrap.DateField.footer
11935 cls: 'datepicker-years',
11939 cls: 'table-condensed',
11941 Roo.bootstrap.DateField.head,
11942 Roo.bootstrap.DateField.content,
11943 Roo.bootstrap.DateField.footer
11962 * @class Roo.bootstrap.TimeField
11963 * @extends Roo.bootstrap.Input
11964 * Bootstrap DateField class
11968 * Create a new TimeField
11969 * @param {Object} config The config object
11972 Roo.bootstrap.TimeField = function(config){
11973 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11977 * Fires when this field show.
11978 * @param {Roo.bootstrap.DateField} this
11979 * @param {Mixed} date The date value
11984 * Fires when this field hide.
11985 * @param {Roo.bootstrap.DateField} this
11986 * @param {Mixed} date The date value
11991 * Fires when select a date.
11992 * @param {Roo.bootstrap.DateField} this
11993 * @param {Mixed} date The date value
11999 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
12002 * @cfg {String} format
12003 * The default time format string which can be overriden for localization support. The format must be
12004 * valid according to {@link Date#parseDate} (defaults to 'H:i').
12008 onRender: function(ct, position)
12011 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12013 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12015 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12017 this.pop = this.picker().select('>.datepicker-time',true).first();
12018 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
12020 this.picker().on('mousedown', this.onMousedown, this);
12021 this.picker().on('click', this.onClick, this);
12023 this.picker().addClass('datepicker-dropdown');
12028 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12029 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12030 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12031 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12032 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12033 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12037 fireKey: function(e){
12038 if (!this.picker().isVisible()){
12039 if (e.keyCode == 27) // allow escape to hide and re-show picker
12044 e.preventDefault();
12052 this.onTogglePeriod();
12055 this.onIncrementMinutes();
12058 this.onDecrementMinutes();
12067 onClick: function(e) {
12068 e.stopPropagation();
12069 e.preventDefault();
12072 picker : function()
12074 return this.el.select('.datepicker', true).first();
12077 fillTime: function()
12079 var time = this.pop.select('tbody', true).first();
12081 time.dom.innerHTML = '';
12096 cls: 'hours-up glyphicon glyphicon-chevron-up'
12116 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12137 cls: 'timepicker-hour',
12152 cls: 'timepicker-minute',
12167 cls: 'btn btn-primary period',
12189 cls: 'hours-down glyphicon glyphicon-chevron-down'
12209 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12227 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12234 var hours = this.time.getHours();
12235 var minutes = this.time.getMinutes();
12248 hours = hours - 12;
12252 hours = '0' + hours;
12256 minutes = '0' + minutes;
12259 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12260 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12261 this.pop.select('button', true).first().dom.innerHTML = period;
12267 this.picker().removeClass(['bottom', 'top']);
12269 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12271 * place to the top of element!
12275 this.picker().addClass('top');
12276 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12281 this.picker().addClass('bottom');
12283 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12286 onFocus : function()
12288 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12292 onBlur : function()
12294 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12300 this.picker().show();
12305 this.fireEvent('show', this, this.date);
12310 this.picker().hide();
12313 this.fireEvent('hide', this, this.date);
12316 setTime : function()
12319 this.setValue(this.time.format(this.format));
12321 this.fireEvent('select', this, this.date);
12326 onMousedown: function(e){
12327 e.stopPropagation();
12328 e.preventDefault();
12331 onIncrementHours: function()
12333 Roo.log('onIncrementHours');
12334 this.time = this.time.add(Date.HOUR, 1);
12339 onDecrementHours: function()
12341 Roo.log('onDecrementHours');
12342 this.time = this.time.add(Date.HOUR, -1);
12346 onIncrementMinutes: function()
12348 Roo.log('onIncrementMinutes');
12349 this.time = this.time.add(Date.MINUTE, 1);
12353 onDecrementMinutes: function()
12355 Roo.log('onDecrementMinutes');
12356 this.time = this.time.add(Date.MINUTE, -1);
12360 onTogglePeriod: function()
12362 Roo.log('onTogglePeriod');
12363 this.time = this.time.add(Date.HOUR, 12);
12370 Roo.apply(Roo.bootstrap.TimeField, {
12400 cls: 'btn btn-info ok',
12412 Roo.apply(Roo.bootstrap.TimeField, {
12416 cls: 'datepicker dropdown-menu',
12420 cls: 'datepicker-time',
12424 cls: 'table-condensed',
12426 Roo.bootstrap.TimeField.content,
12427 Roo.bootstrap.TimeField.footer
12446 * @class Roo.bootstrap.CheckBox
12447 * @extends Roo.bootstrap.Input
12448 * Bootstrap CheckBox class
12450 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12451 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12452 * @cfg {String} boxLabel The text that appears beside the checkbox
12453 * @cfg {Boolean} checked initnal the element
12456 * Create a new CheckBox
12457 * @param {Object} config The config object
12460 Roo.bootstrap.CheckBox = function(config){
12461 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12466 * Fires when the element is checked or unchecked.
12467 * @param {Roo.bootstrap.CheckBox} this This input
12468 * @param {Boolean} checked The new checked value
12474 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12476 inputType: 'checkbox',
12482 getAutoCreate : function()
12484 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12490 cfg.cls = 'form-group' //input-group
12495 type : this.inputType,
12496 value : (!this.checked) ? this.valueOff : this.inputValue,
12498 placeholder : this.placeholder || ''
12502 if (this.disabled) {
12503 input.disabled=true;
12507 input.checked = this.checked;
12511 input.name = this.name;
12515 input.cls += ' input-' + this.size;
12519 ['xs','sm','md','lg'].map(function(size){
12520 if (settings[size]) {
12521 cfg.cls += ' col-' + size + '-' + settings[size];
12525 var inputblock = input;
12527 if (this.before || this.after) {
12530 cls : 'input-group',
12534 inputblock.cn.push({
12536 cls : 'input-group-addon',
12540 inputblock.cn.push(input);
12542 inputblock.cn.push({
12544 cls : 'input-group-addon',
12551 if (align ==='left' && this.fieldLabel.length) {
12552 Roo.log("left and has label");
12558 cls : 'control-label col-md-' + this.labelWidth,
12559 html : this.fieldLabel
12563 cls : "col-md-" + (12 - this.labelWidth),
12570 } else if ( this.fieldLabel.length) {
12575 tag: this.boxLabel ? 'span' : 'label',
12577 cls: 'control-label box-input-label',
12578 //cls : 'input-group-addon',
12579 html : this.fieldLabel
12589 Roo.log(" no label && no align");
12604 html: this.boxLabel
12613 * return the real input element.
12615 inputEl: function ()
12617 return this.el.select('input.form-box',true).first();
12620 initEvents : function()
12622 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12624 this.inputEl().on('click', this.onClick, this);
12628 onClick : function()
12630 this.setChecked(!this.checked);
12633 setChecked : function(state,suppressEvent)
12635 this.checked = state;
12637 this.inputEl().dom.checked = state;
12639 if(suppressEvent !== true){
12640 this.fireEvent('check', this, state);
12643 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12647 setValue : function(v,suppressEvent)
12649 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12663 * @class Roo.bootstrap.Radio
12664 * @extends Roo.bootstrap.CheckBox
12665 * Bootstrap Radio class
12668 * Create a new Radio
12669 * @param {Object} config The config object
12672 Roo.bootstrap.Radio = function(config){
12673 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12677 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12679 inputType: 'radio',
12683 getAutoCreate : function()
12685 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12691 cfg.cls = 'form-group' //input-group
12696 type : this.inputType,
12697 value : (!this.checked) ? this.valueOff : this.inputValue,
12699 placeholder : this.placeholder || ''
12703 if (this.disabled) {
12704 input.disabled=true;
12708 input.checked = this.checked;
12712 input.name = this.name;
12716 input.cls += ' input-' + this.size;
12720 ['xs','sm','md','lg'].map(function(size){
12721 if (settings[size]) {
12722 cfg.cls += ' col-' + size + '-' + settings[size];
12726 var inputblock = input;
12728 if (this.before || this.after) {
12731 cls : 'input-group',
12735 inputblock.cn.push({
12737 cls : 'input-group-addon',
12741 inputblock.cn.push(input);
12743 inputblock.cn.push({
12745 cls : 'input-group-addon',
12752 if (align ==='left' && this.fieldLabel.length) {
12753 Roo.log("left and has label");
12759 cls : 'control-label col-md-' + this.labelWidth,
12760 html : this.fieldLabel
12764 cls : "col-md-" + (12 - this.labelWidth),
12771 } else if ( this.fieldLabel.length) {
12778 cls: 'control-label box-input-label',
12779 //cls : 'input-group-addon',
12780 html : this.fieldLabel
12790 Roo.log(" no label && no align");
12805 html: this.boxLabel
12813 onClick : function()
12815 this.setChecked(true);
12818 setChecked : function(state,suppressEvent)
12821 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12822 v.dom.checked = false;
12826 this.checked = state;
12827 this.inputEl().dom.checked = state;
12829 if(suppressEvent !== true){
12830 this.fireEvent('check', this, state);
12833 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12837 getGroupValue : function()
12840 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12841 if(v.dom.checked == true){
12842 value = v.dom.value;
12850 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12851 * @return {Mixed} value The field value
12853 getValue : function(){
12854 return this.getGroupValue();
12860 //<script type="text/javascript">
12863 * Based Ext JS Library 1.1.1
12864 * Copyright(c) 2006-2007, Ext JS, LLC.
12870 * @class Roo.HtmlEditorCore
12871 * @extends Roo.Component
12872 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12874 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12877 Roo.HtmlEditorCore = function(config){
12880 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12883 * @event initialize
12884 * Fires when the editor is fully initialized (including the iframe)
12885 * @param {Roo.HtmlEditorCore} this
12890 * Fires when the editor is first receives the focus. Any insertion must wait
12891 * until after this event.
12892 * @param {Roo.HtmlEditorCore} this
12896 * @event beforesync
12897 * Fires before the textarea is updated with content from the editor iframe. Return false
12898 * to cancel the sync.
12899 * @param {Roo.HtmlEditorCore} this
12900 * @param {String} html
12904 * @event beforepush
12905 * Fires before the iframe editor is updated with content from the textarea. Return false
12906 * to cancel the push.
12907 * @param {Roo.HtmlEditorCore} this
12908 * @param {String} html
12913 * Fires when the textarea is updated with content from the editor iframe.
12914 * @param {Roo.HtmlEditorCore} this
12915 * @param {String} html
12920 * Fires when the iframe editor is updated with content from the textarea.
12921 * @param {Roo.HtmlEditorCore} this
12922 * @param {String} html
12927 * @event editorevent
12928 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12929 * @param {Roo.HtmlEditorCore} this
12937 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12941 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12947 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12952 * @cfg {Number} height (in pixels)
12956 * @cfg {Number} width (in pixels)
12961 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12964 stylesheets: false,
12969 // private properties
12970 validationEvent : false,
12972 initialized : false,
12974 sourceEditMode : false,
12975 onFocus : Roo.emptyFn,
12977 hideMode:'offsets',
12985 * Protected method that will not generally be called directly. It
12986 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12987 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12989 getDocMarkup : function(){
12992 Roo.log(this.stylesheets);
12994 // inherit styels from page...??
12995 if (this.stylesheets === false) {
12997 Roo.get(document.head).select('style').each(function(node) {
12998 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13001 Roo.get(document.head).select('link').each(function(node) {
13002 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13005 } else if (!this.stylesheets.length) {
13007 st = '<style type="text/css">' +
13008 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13011 Roo.each(this.stylesheets, function(s) {
13012 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13017 st += '<style type="text/css">' +
13018 'IMG { cursor: pointer } ' +
13022 return '<html><head>' + st +
13023 //<style type="text/css">' +
13024 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13026 ' </head><body class="roo-htmleditor-body"></body></html>';
13030 onRender : function(ct, position)
13033 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13034 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13037 this.el.dom.style.border = '0 none';
13038 this.el.dom.setAttribute('tabIndex', -1);
13039 this.el.addClass('x-hidden hide');
13043 if(Roo.isIE){ // fix IE 1px bogus margin
13044 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13048 this.frameId = Roo.id();
13052 var iframe = this.owner.wrap.createChild({
13054 cls: 'form-control', // bootstrap..
13056 name: this.frameId,
13057 frameBorder : 'no',
13058 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13063 this.iframe = iframe.dom;
13065 this.assignDocWin();
13067 this.doc.designMode = 'on';
13070 this.doc.write(this.getDocMarkup());
13074 var task = { // must defer to wait for browser to be ready
13076 //console.log("run task?" + this.doc.readyState);
13077 this.assignDocWin();
13078 if(this.doc.body || this.doc.readyState == 'complete'){
13080 this.doc.designMode="on";
13084 Roo.TaskMgr.stop(task);
13085 this.initEditor.defer(10, this);
13092 Roo.TaskMgr.start(task);
13099 onResize : function(w, h)
13101 Roo.log('resize: ' +w + ',' + h );
13102 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13106 if(typeof w == 'number'){
13108 this.iframe.style.width = w + 'px';
13110 if(typeof h == 'number'){
13112 this.iframe.style.height = h + 'px';
13114 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13121 * Toggles the editor between standard and source edit mode.
13122 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13124 toggleSourceEdit : function(sourceEditMode){
13126 this.sourceEditMode = sourceEditMode === true;
13128 if(this.sourceEditMode){
13130 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13133 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13134 //this.iframe.className = '';
13137 //this.setSize(this.owner.wrap.getSize());
13138 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13145 * Protected method that will not generally be called directly. If you need/want
13146 * custom HTML cleanup, this is the method you should override.
13147 * @param {String} html The HTML to be cleaned
13148 * return {String} The cleaned HTML
13150 cleanHtml : function(html){
13151 html = String(html);
13152 if(html.length > 5){
13153 if(Roo.isSafari){ // strip safari nonsense
13154 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13157 if(html == ' '){
13164 * HTML Editor -> Textarea
13165 * Protected method that will not generally be called directly. Syncs the contents
13166 * of the editor iframe with the textarea.
13168 syncValue : function(){
13169 if(this.initialized){
13170 var bd = (this.doc.body || this.doc.documentElement);
13171 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13172 var html = bd.innerHTML;
13174 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13175 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13177 html = '<div style="'+m[0]+'">' + html + '</div>';
13180 html = this.cleanHtml(html);
13181 // fix up the special chars.. normaly like back quotes in word...
13182 // however we do not want to do this with chinese..
13183 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13184 var cc = b.charCodeAt();
13186 (cc >= 0x4E00 && cc < 0xA000 ) ||
13187 (cc >= 0x3400 && cc < 0x4E00 ) ||
13188 (cc >= 0xf900 && cc < 0xfb00 )
13194 if(this.owner.fireEvent('beforesync', this, html) !== false){
13195 this.el.dom.value = html;
13196 this.owner.fireEvent('sync', this, html);
13202 * Protected method that will not generally be called directly. Pushes the value of the textarea
13203 * into the iframe editor.
13205 pushValue : function(){
13206 if(this.initialized){
13207 var v = this.el.dom.value.trim();
13209 // if(v.length < 1){
13213 if(this.owner.fireEvent('beforepush', this, v) !== false){
13214 var d = (this.doc.body || this.doc.documentElement);
13216 this.cleanUpPaste();
13217 this.el.dom.value = d.innerHTML;
13218 this.owner.fireEvent('push', this, v);
13224 deferFocus : function(){
13225 this.focus.defer(10, this);
13229 focus : function(){
13230 if(this.win && !this.sourceEditMode){
13237 assignDocWin: function()
13239 var iframe = this.iframe;
13242 this.doc = iframe.contentWindow.document;
13243 this.win = iframe.contentWindow;
13245 if (!Roo.get(this.frameId)) {
13248 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13249 this.win = Roo.get(this.frameId).dom.contentWindow;
13254 initEditor : function(){
13255 //console.log("INIT EDITOR");
13256 this.assignDocWin();
13260 this.doc.designMode="on";
13262 this.doc.write(this.getDocMarkup());
13265 var dbody = (this.doc.body || this.doc.documentElement);
13266 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13267 // this copies styles from the containing element into thsi one..
13268 // not sure why we need all of this..
13269 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13270 ss['background-attachment'] = 'fixed'; // w3c
13271 dbody.bgProperties = 'fixed'; // ie
13272 Roo.DomHelper.applyStyles(dbody, ss);
13273 Roo.EventManager.on(this.doc, {
13274 //'mousedown': this.onEditorEvent,
13275 'mouseup': this.onEditorEvent,
13276 'dblclick': this.onEditorEvent,
13277 'click': this.onEditorEvent,
13278 'keyup': this.onEditorEvent,
13283 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13285 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13286 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13288 this.initialized = true;
13290 this.owner.fireEvent('initialize', this);
13295 onDestroy : function(){
13301 //for (var i =0; i < this.toolbars.length;i++) {
13302 // // fixme - ask toolbars for heights?
13303 // this.toolbars[i].onDestroy();
13306 //this.wrap.dom.innerHTML = '';
13307 //this.wrap.remove();
13312 onFirstFocus : function(){
13314 this.assignDocWin();
13317 this.activated = true;
13320 if(Roo.isGecko){ // prevent silly gecko errors
13322 var s = this.win.getSelection();
13323 if(!s.focusNode || s.focusNode.nodeType != 3){
13324 var r = s.getRangeAt(0);
13325 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13330 this.execCmd('useCSS', true);
13331 this.execCmd('styleWithCSS', false);
13334 this.owner.fireEvent('activate', this);
13338 adjustFont: function(btn){
13339 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13340 //if(Roo.isSafari){ // safari
13343 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13344 if(Roo.isSafari){ // safari
13345 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13346 v = (v < 10) ? 10 : v;
13347 v = (v > 48) ? 48 : v;
13348 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13353 v = Math.max(1, v+adjust);
13355 this.execCmd('FontSize', v );
13358 onEditorEvent : function(e){
13359 this.owner.fireEvent('editorevent', this, e);
13360 // this.updateToolbar();
13361 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13364 insertTag : function(tg)
13366 // could be a bit smarter... -> wrap the current selected tRoo..
13367 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13369 range = this.createRange(this.getSelection());
13370 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13371 wrappingNode.appendChild(range.extractContents());
13372 range.insertNode(wrappingNode);
13379 this.execCmd("formatblock", tg);
13383 insertText : function(txt)
13387 var range = this.createRange();
13388 range.deleteContents();
13389 //alert(Sender.getAttribute('label'));
13391 range.insertNode(this.doc.createTextNode(txt));
13397 * Executes a Midas editor command on the editor document and performs necessary focus and
13398 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13399 * @param {String} cmd The Midas command
13400 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13402 relayCmd : function(cmd, value){
13404 this.execCmd(cmd, value);
13405 this.owner.fireEvent('editorevent', this);
13406 //this.updateToolbar();
13407 this.owner.deferFocus();
13411 * Executes a Midas editor command directly on the editor document.
13412 * For visual commands, you should use {@link #relayCmd} instead.
13413 * <b>This should only be called after the editor is initialized.</b>
13414 * @param {String} cmd The Midas command
13415 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13417 execCmd : function(cmd, value){
13418 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13425 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13427 * @param {String} text | dom node..
13429 insertAtCursor : function(text)
13434 if(!this.activated){
13440 var r = this.doc.selection.createRange();
13451 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13455 // from jquery ui (MIT licenced)
13457 var win = this.win;
13459 if (win.getSelection && win.getSelection().getRangeAt) {
13460 range = win.getSelection().getRangeAt(0);
13461 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13462 range.insertNode(node);
13463 } else if (win.document.selection && win.document.selection.createRange) {
13464 // no firefox support
13465 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13466 win.document.selection.createRange().pasteHTML(txt);
13468 // no firefox support
13469 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13470 this.execCmd('InsertHTML', txt);
13479 mozKeyPress : function(e){
13481 var c = e.getCharCode(), cmd;
13484 c = String.fromCharCode(c).toLowerCase();
13498 this.cleanUpPaste.defer(100, this);
13506 e.preventDefault();
13514 fixKeys : function(){ // load time branching for fastest keydown performance
13516 return function(e){
13517 var k = e.getKey(), r;
13520 r = this.doc.selection.createRange();
13523 r.pasteHTML('    ');
13530 r = this.doc.selection.createRange();
13532 var target = r.parentElement();
13533 if(!target || target.tagName.toLowerCase() != 'li'){
13535 r.pasteHTML('<br />');
13541 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13542 this.cleanUpPaste.defer(100, this);
13548 }else if(Roo.isOpera){
13549 return function(e){
13550 var k = e.getKey();
13554 this.execCmd('InsertHTML','    ');
13557 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13558 this.cleanUpPaste.defer(100, this);
13563 }else if(Roo.isSafari){
13564 return function(e){
13565 var k = e.getKey();
13569 this.execCmd('InsertText','\t');
13573 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13574 this.cleanUpPaste.defer(100, this);
13582 getAllAncestors: function()
13584 var p = this.getSelectedNode();
13587 a.push(p); // push blank onto stack..
13588 p = this.getParentElement();
13592 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13596 a.push(this.doc.body);
13600 lastSelNode : false,
13603 getSelection : function()
13605 this.assignDocWin();
13606 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13609 getSelectedNode: function()
13611 // this may only work on Gecko!!!
13613 // should we cache this!!!!
13618 var range = this.createRange(this.getSelection()).cloneRange();
13621 var parent = range.parentElement();
13623 var testRange = range.duplicate();
13624 testRange.moveToElementText(parent);
13625 if (testRange.inRange(range)) {
13628 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13631 parent = parent.parentElement;
13636 // is ancestor a text element.
13637 var ac = range.commonAncestorContainer;
13638 if (ac.nodeType == 3) {
13639 ac = ac.parentNode;
13642 var ar = ac.childNodes;
13645 var other_nodes = [];
13646 var has_other_nodes = false;
13647 for (var i=0;i<ar.length;i++) {
13648 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13651 // fullly contained node.
13653 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13658 // probably selected..
13659 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13660 other_nodes.push(ar[i]);
13664 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13669 has_other_nodes = true;
13671 if (!nodes.length && other_nodes.length) {
13672 nodes= other_nodes;
13674 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13680 createRange: function(sel)
13682 // this has strange effects when using with
13683 // top toolbar - not sure if it's a great idea.
13684 //this.editor.contentWindow.focus();
13685 if (typeof sel != "undefined") {
13687 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13689 return this.doc.createRange();
13692 return this.doc.createRange();
13695 getParentElement: function()
13698 this.assignDocWin();
13699 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13701 var range = this.createRange(sel);
13704 var p = range.commonAncestorContainer;
13705 while (p.nodeType == 3) { // text node
13716 * Range intersection.. the hard stuff...
13720 * [ -- selected range --- ]
13724 * if end is before start or hits it. fail.
13725 * if start is after end or hits it fail.
13727 * if either hits (but other is outside. - then it's not
13733 // @see http://www.thismuchiknow.co.uk/?p=64.
13734 rangeIntersectsNode : function(range, node)
13736 var nodeRange = node.ownerDocument.createRange();
13738 nodeRange.selectNode(node);
13740 nodeRange.selectNodeContents(node);
13743 var rangeStartRange = range.cloneRange();
13744 rangeStartRange.collapse(true);
13746 var rangeEndRange = range.cloneRange();
13747 rangeEndRange.collapse(false);
13749 var nodeStartRange = nodeRange.cloneRange();
13750 nodeStartRange.collapse(true);
13752 var nodeEndRange = nodeRange.cloneRange();
13753 nodeEndRange.collapse(false);
13755 return rangeStartRange.compareBoundaryPoints(
13756 Range.START_TO_START, nodeEndRange) == -1 &&
13757 rangeEndRange.compareBoundaryPoints(
13758 Range.START_TO_START, nodeStartRange) == 1;
13762 rangeCompareNode : function(range, node)
13764 var nodeRange = node.ownerDocument.createRange();
13766 nodeRange.selectNode(node);
13768 nodeRange.selectNodeContents(node);
13772 range.collapse(true);
13774 nodeRange.collapse(true);
13776 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13777 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13779 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13781 var nodeIsBefore = ss == 1;
13782 var nodeIsAfter = ee == -1;
13784 if (nodeIsBefore && nodeIsAfter)
13786 if (!nodeIsBefore && nodeIsAfter)
13787 return 1; //right trailed.
13789 if (nodeIsBefore && !nodeIsAfter)
13790 return 2; // left trailed.
13795 // private? - in a new class?
13796 cleanUpPaste : function()
13798 // cleans up the whole document..
13799 Roo.log('cleanuppaste');
13801 this.cleanUpChildren(this.doc.body);
13802 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13803 if (clean != this.doc.body.innerHTML) {
13804 this.doc.body.innerHTML = clean;
13809 cleanWordChars : function(input) {// change the chars to hex code
13810 var he = Roo.HtmlEditorCore;
13812 var output = input;
13813 Roo.each(he.swapCodes, function(sw) {
13814 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13816 output = output.replace(swapper, sw[1]);
13823 cleanUpChildren : function (n)
13825 if (!n.childNodes.length) {
13828 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13829 this.cleanUpChild(n.childNodes[i]);
13836 cleanUpChild : function (node)
13839 //console.log(node);
13840 if (node.nodeName == "#text") {
13841 // clean up silly Windows -- stuff?
13844 if (node.nodeName == "#comment") {
13845 node.parentNode.removeChild(node);
13846 // clean up silly Windows -- stuff?
13850 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13852 node.parentNode.removeChild(node);
13857 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13859 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13860 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13862 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13863 // remove_keep_children = true;
13866 if (remove_keep_children) {
13867 this.cleanUpChildren(node);
13868 // inserts everything just before this node...
13869 while (node.childNodes.length) {
13870 var cn = node.childNodes[0];
13871 node.removeChild(cn);
13872 node.parentNode.insertBefore(cn, node);
13874 node.parentNode.removeChild(node);
13878 if (!node.attributes || !node.attributes.length) {
13879 this.cleanUpChildren(node);
13883 function cleanAttr(n,v)
13886 if (v.match(/^\./) || v.match(/^\//)) {
13889 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13892 if (v.match(/^#/)) {
13895 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13896 node.removeAttribute(n);
13900 function cleanStyle(n,v)
13902 if (v.match(/expression/)) { //XSS?? should we even bother..
13903 node.removeAttribute(n);
13906 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13907 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13910 var parts = v.split(/;/);
13913 Roo.each(parts, function(p) {
13914 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13918 var l = p.split(':').shift().replace(/\s+/g,'');
13919 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13921 if ( cblack.indexOf(l) > -1) {
13922 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13923 //node.removeAttribute(n);
13927 // only allow 'c whitelisted system attributes'
13928 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13929 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13930 //node.removeAttribute(n);
13940 if (clean.length) {
13941 node.setAttribute(n, clean.join(';'));
13943 node.removeAttribute(n);
13949 for (var i = node.attributes.length-1; i > -1 ; i--) {
13950 var a = node.attributes[i];
13953 if (a.name.toLowerCase().substr(0,2)=='on') {
13954 node.removeAttribute(a.name);
13957 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13958 node.removeAttribute(a.name);
13961 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13962 cleanAttr(a.name,a.value); // fixme..
13965 if (a.name == 'style') {
13966 cleanStyle(a.name,a.value);
13969 /// clean up MS crap..
13970 // tecnically this should be a list of valid class'es..
13973 if (a.name == 'class') {
13974 if (a.value.match(/^Mso/)) {
13975 node.className = '';
13978 if (a.value.match(/body/)) {
13979 node.className = '';
13990 this.cleanUpChildren(node);
13996 // hide stuff that is not compatible
14010 * @event specialkey
14014 * @cfg {String} fieldClass @hide
14017 * @cfg {String} focusClass @hide
14020 * @cfg {String} autoCreate @hide
14023 * @cfg {String} inputType @hide
14026 * @cfg {String} invalidClass @hide
14029 * @cfg {String} invalidText @hide
14032 * @cfg {String} msgFx @hide
14035 * @cfg {String} validateOnBlur @hide
14039 Roo.HtmlEditorCore.white = [
14040 'area', 'br', 'img', 'input', 'hr', 'wbr',
14042 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14043 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14044 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14045 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14046 'table', 'ul', 'xmp',
14048 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14051 'dir', 'menu', 'ol', 'ul', 'dl',
14057 Roo.HtmlEditorCore.black = [
14058 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14060 'base', 'basefont', 'bgsound', 'blink', 'body',
14061 'frame', 'frameset', 'head', 'html', 'ilayer',
14062 'iframe', 'layer', 'link', 'meta', 'object',
14063 'script', 'style' ,'title', 'xml' // clean later..
14065 Roo.HtmlEditorCore.clean = [
14066 'script', 'style', 'title', 'xml'
14068 Roo.HtmlEditorCore.remove = [
14073 Roo.HtmlEditorCore.ablack = [
14077 Roo.HtmlEditorCore.aclean = [
14078 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14082 Roo.HtmlEditorCore.pwhite= [
14083 'http', 'https', 'mailto'
14086 // white listed style attributes.
14087 Roo.HtmlEditorCore.cwhite= [
14088 // 'text-align', /// default is to allow most things..
14094 // black listed style attributes.
14095 Roo.HtmlEditorCore.cblack= [
14096 // 'font-size' -- this can be set by the project
14100 Roo.HtmlEditorCore.swapCodes =[
14119 * @class Roo.bootstrap.HtmlEditor
14120 * @extends Roo.bootstrap.TextArea
14121 * Bootstrap HtmlEditor class
14124 * Create a new HtmlEditor
14125 * @param {Object} config The config object
14128 Roo.bootstrap.HtmlEditor = function(config){
14129 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14130 if (!this.toolbars) {
14131 this.toolbars = [];
14133 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14136 * @event initialize
14137 * Fires when the editor is fully initialized (including the iframe)
14138 * @param {HtmlEditor} this
14143 * Fires when the editor is first receives the focus. Any insertion must wait
14144 * until after this event.
14145 * @param {HtmlEditor} this
14149 * @event beforesync
14150 * Fires before the textarea is updated with content from the editor iframe. Return false
14151 * to cancel the sync.
14152 * @param {HtmlEditor} this
14153 * @param {String} html
14157 * @event beforepush
14158 * Fires before the iframe editor is updated with content from the textarea. Return false
14159 * to cancel the push.
14160 * @param {HtmlEditor} this
14161 * @param {String} html
14166 * Fires when the textarea is updated with content from the editor iframe.
14167 * @param {HtmlEditor} this
14168 * @param {String} html
14173 * Fires when the iframe editor is updated with content from the textarea.
14174 * @param {HtmlEditor} this
14175 * @param {String} html
14179 * @event editmodechange
14180 * Fires when the editor switches edit modes
14181 * @param {HtmlEditor} this
14182 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14184 editmodechange: true,
14186 * @event editorevent
14187 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14188 * @param {HtmlEditor} this
14192 * @event firstfocus
14193 * Fires when on first focus - needed by toolbars..
14194 * @param {HtmlEditor} this
14199 * Auto save the htmlEditor value as a file into Events
14200 * @param {HtmlEditor} this
14204 * @event savedpreview
14205 * preview the saved version of htmlEditor
14206 * @param {HtmlEditor} this
14213 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14217 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14222 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14227 * @cfg {Number} height (in pixels)
14231 * @cfg {Number} width (in pixels)
14236 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14239 stylesheets: false,
14244 // private properties
14245 validationEvent : false,
14247 initialized : false,
14250 onFocus : Roo.emptyFn,
14252 hideMode:'offsets',
14255 tbContainer : false,
14257 toolbarContainer :function() {
14258 return this.wrap.select('.x-html-editor-tb',true).first();
14262 * Protected method that will not generally be called directly. It
14263 * is called when the editor creates its toolbar. Override this method if you need to
14264 * add custom toolbar buttons.
14265 * @param {HtmlEditor} editor
14267 createToolbar : function(){
14269 Roo.log("create toolbars");
14271 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14272 this.toolbars[0].render(this.toolbarContainer());
14276 // if (!editor.toolbars || !editor.toolbars.length) {
14277 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14280 // for (var i =0 ; i < editor.toolbars.length;i++) {
14281 // editor.toolbars[i] = Roo.factory(
14282 // typeof(editor.toolbars[i]) == 'string' ?
14283 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14284 // Roo.bootstrap.HtmlEditor);
14285 // editor.toolbars[i].init(editor);
14291 onRender : function(ct, position)
14293 // Roo.log("Call onRender: " + this.xtype);
14295 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14297 this.wrap = this.inputEl().wrap({
14298 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14301 this.editorcore.onRender(ct, position);
14303 if (this.resizable) {
14304 this.resizeEl = new Roo.Resizable(this.wrap, {
14308 minHeight : this.height,
14309 height: this.height,
14310 handles : this.resizable,
14313 resize : function(r, w, h) {
14314 _t.onResize(w,h); // -something
14320 this.createToolbar(this);
14323 if(!this.width && this.resizable){
14324 this.setSize(this.wrap.getSize());
14326 if (this.resizeEl) {
14327 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14328 // should trigger onReize..
14334 onResize : function(w, h)
14336 Roo.log('resize: ' +w + ',' + h );
14337 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14341 if(this.inputEl() ){
14342 if(typeof w == 'number'){
14343 var aw = w - this.wrap.getFrameWidth('lr');
14344 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14347 if(typeof h == 'number'){
14348 var tbh = -11; // fixme it needs to tool bar size!
14349 for (var i =0; i < this.toolbars.length;i++) {
14350 // fixme - ask toolbars for heights?
14351 tbh += this.toolbars[i].el.getHeight();
14352 //if (this.toolbars[i].footer) {
14353 // tbh += this.toolbars[i].footer.el.getHeight();
14361 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14362 ah -= 5; // knock a few pixes off for look..
14363 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14367 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14368 this.editorcore.onResize(ew,eh);
14373 * Toggles the editor between standard and source edit mode.
14374 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14376 toggleSourceEdit : function(sourceEditMode)
14378 this.editorcore.toggleSourceEdit(sourceEditMode);
14380 if(this.editorcore.sourceEditMode){
14381 Roo.log('editor - showing textarea');
14384 // Roo.log(this.syncValue());
14386 this.inputEl().removeClass('hide');
14387 this.inputEl().dom.removeAttribute('tabIndex');
14388 this.inputEl().focus();
14390 Roo.log('editor - hiding textarea');
14392 // Roo.log(this.pushValue());
14395 this.inputEl().addClass('hide');
14396 this.inputEl().dom.setAttribute('tabIndex', -1);
14397 //this.deferFocus();
14400 if(this.resizable){
14401 this.setSize(this.wrap.getSize());
14404 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14407 // private (for BoxComponent)
14408 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14410 // private (for BoxComponent)
14411 getResizeEl : function(){
14415 // private (for BoxComponent)
14416 getPositionEl : function(){
14421 initEvents : function(){
14422 this.originalValue = this.getValue();
14426 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14429 // markInvalid : Roo.emptyFn,
14431 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14434 // clearInvalid : Roo.emptyFn,
14436 setValue : function(v){
14437 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14438 this.editorcore.pushValue();
14443 deferFocus : function(){
14444 this.focus.defer(10, this);
14448 focus : function(){
14449 this.editorcore.focus();
14455 onDestroy : function(){
14461 for (var i =0; i < this.toolbars.length;i++) {
14462 // fixme - ask toolbars for heights?
14463 this.toolbars[i].onDestroy();
14466 this.wrap.dom.innerHTML = '';
14467 this.wrap.remove();
14472 onFirstFocus : function(){
14473 //Roo.log("onFirstFocus");
14474 this.editorcore.onFirstFocus();
14475 for (var i =0; i < this.toolbars.length;i++) {
14476 this.toolbars[i].onFirstFocus();
14482 syncValue : function()
14484 this.editorcore.syncValue();
14487 pushValue : function()
14489 this.editorcore.pushValue();
14493 // hide stuff that is not compatible
14507 * @event specialkey
14511 * @cfg {String} fieldClass @hide
14514 * @cfg {String} focusClass @hide
14517 * @cfg {String} autoCreate @hide
14520 * @cfg {String} inputType @hide
14523 * @cfg {String} invalidClass @hide
14526 * @cfg {String} invalidText @hide
14529 * @cfg {String} msgFx @hide
14532 * @cfg {String} validateOnBlur @hide
14543 * @class Roo.bootstrap.HtmlEditorToolbar1
14548 new Roo.bootstrap.HtmlEditor({
14551 new Roo.bootstrap.HtmlEditorToolbar1({
14552 disable : { fonts: 1 , format: 1, ..., ... , ...],
14558 * @cfg {Object} disable List of elements to disable..
14559 * @cfg {Array} btns List of additional buttons.
14563 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14566 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14569 Roo.apply(this, config);
14571 // default disabled, based on 'good practice'..
14572 this.disable = this.disable || {};
14573 Roo.applyIf(this.disable, {
14576 specialElements : true
14578 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14580 this.editor = config.editor;
14581 this.editorcore = config.editor.editorcore;
14583 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14585 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14586 // dont call parent... till later.
14588 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14594 editorcore : false,
14599 "h1","h2","h3","h4","h5","h6",
14601 "abbr", "acronym", "address", "cite", "samp", "var",
14605 onRender : function(ct, position)
14607 // Roo.log("Call onRender: " + this.xtype);
14609 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14611 this.el.dom.style.marginBottom = '0';
14613 var editorcore = this.editorcore;
14614 var editor= this.editor;
14617 var btn = function(id,cmd , toggle, handler){
14619 var event = toggle ? 'toggle' : 'click';
14624 xns: Roo.bootstrap,
14627 enableToggle:toggle !== false,
14629 pressed : toggle ? false : null,
14632 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14633 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14642 xns: Roo.bootstrap,
14643 glyphicon : 'font',
14647 xns: Roo.bootstrap,
14651 Roo.each(this.formats, function(f) {
14652 style.menu.items.push({
14654 xns: Roo.bootstrap,
14655 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14660 editorcore.insertTag(this.tagname);
14667 children.push(style);
14670 btn('bold',false,true);
14671 btn('italic',false,true);
14672 btn('align-left', 'justifyleft',true);
14673 btn('align-center', 'justifycenter',true);
14674 btn('align-right' , 'justifyright',true);
14675 btn('link', false, false, function(btn) {
14676 //Roo.log("create link?");
14677 var url = prompt(this.createLinkText, this.defaultLinkValue);
14678 if(url && url != 'http:/'+'/'){
14679 this.editorcore.relayCmd('createlink', url);
14682 btn('list','insertunorderedlist',true);
14683 btn('pencil', false,true, function(btn){
14686 this.toggleSourceEdit(btn.pressed);
14692 xns: Roo.bootstrap,
14697 xns: Roo.bootstrap,
14702 cog.menu.items.push({
14704 xns: Roo.bootstrap,
14705 html : Clean styles,
14710 editorcore.insertTag(this.tagname);
14719 this.xtype = 'Navbar';
14721 for(var i=0;i< children.length;i++) {
14723 this.buttons.add(this.addxtypeChild(children[i]));
14727 editor.on('editorevent', this.updateToolbar, this);
14729 onBtnClick : function(id)
14731 this.editorcore.relayCmd(id);
14732 this.editorcore.focus();
14736 * Protected method that will not generally be called directly. It triggers
14737 * a toolbar update by reading the markup state of the current selection in the editor.
14739 updateToolbar: function(){
14741 if(!this.editorcore.activated){
14742 this.editor.onFirstFocus(); // is this neeed?
14746 var btns = this.buttons;
14747 var doc = this.editorcore.doc;
14748 btns.get('bold').setActive(doc.queryCommandState('bold'));
14749 btns.get('italic').setActive(doc.queryCommandState('italic'));
14750 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14752 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14753 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14754 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14756 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14757 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14760 var ans = this.editorcore.getAllAncestors();
14761 if (this.formatCombo) {
14764 var store = this.formatCombo.store;
14765 this.formatCombo.setValue("");
14766 for (var i =0; i < ans.length;i++) {
14767 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14769 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14777 // hides menus... - so this cant be on a menu...
14778 Roo.bootstrap.MenuMgr.hideAll();
14780 Roo.bootstrap.MenuMgr.hideAll();
14781 //this.editorsyncValue();
14783 onFirstFocus: function() {
14784 this.buttons.each(function(item){
14788 toggleSourceEdit : function(sourceEditMode){
14791 if(sourceEditMode){
14792 Roo.log("disabling buttons");
14793 this.buttons.each( function(item){
14794 if(item.cmd != 'pencil'){
14800 Roo.log("enabling buttons");
14801 if(this.editorcore.initialized){
14802 this.buttons.each( function(item){
14808 Roo.log("calling toggole on editor");
14809 // tell the editor that it's been pressed..
14810 this.editor.toggleSourceEdit(sourceEditMode);
14820 * @class Roo.bootstrap.Table.AbstractSelectionModel
14821 * @extends Roo.util.Observable
14822 * Abstract base class for grid SelectionModels. It provides the interface that should be
14823 * implemented by descendant classes. This class should not be directly instantiated.
14826 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14827 this.locked = false;
14828 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14832 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14833 /** @ignore Called by the grid automatically. Do not call directly. */
14834 init : function(grid){
14840 * Locks the selections.
14843 this.locked = true;
14847 * Unlocks the selections.
14849 unlock : function(){
14850 this.locked = false;
14854 * Returns true if the selections are locked.
14855 * @return {Boolean}
14857 isLocked : function(){
14858 return this.locked;
14862 * @class Roo.bootstrap.Table.ColumnModel
14863 * @extends Roo.util.Observable
14864 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14865 * the columns in the table.
14868 * @param {Object} config An Array of column config objects. See this class's
14869 * config objects for details.
14871 Roo.bootstrap.Table.ColumnModel = function(config){
14873 * The config passed into the constructor
14875 this.config = config;
14878 // if no id, create one
14879 // if the column does not have a dataIndex mapping,
14880 // map it to the order it is in the config
14881 for(var i = 0, len = config.length; i < len; i++){
14883 if(typeof c.dataIndex == "undefined"){
14886 if(typeof c.renderer == "string"){
14887 c.renderer = Roo.util.Format[c.renderer];
14889 if(typeof c.id == "undefined"){
14892 // if(c.editor && c.editor.xtype){
14893 // c.editor = Roo.factory(c.editor, Roo.grid);
14895 // if(c.editor && c.editor.isFormField){
14896 // c.editor = new Roo.grid.GridEditor(c.editor);
14899 this.lookup[c.id] = c;
14903 * The width of columns which have no width specified (defaults to 100)
14906 this.defaultWidth = 100;
14909 * Default sortable of columns which have no sortable specified (defaults to false)
14912 this.defaultSortable = false;
14916 * @event widthchange
14917 * Fires when the width of a column changes.
14918 * @param {ColumnModel} this
14919 * @param {Number} columnIndex The column index
14920 * @param {Number} newWidth The new width
14922 "widthchange": true,
14924 * @event headerchange
14925 * Fires when the text of a header changes.
14926 * @param {ColumnModel} this
14927 * @param {Number} columnIndex The column index
14928 * @param {Number} newText The new header text
14930 "headerchange": true,
14932 * @event hiddenchange
14933 * Fires when a column is hidden or "unhidden".
14934 * @param {ColumnModel} this
14935 * @param {Number} columnIndex The column index
14936 * @param {Boolean} hidden true if hidden, false otherwise
14938 "hiddenchange": true,
14940 * @event columnmoved
14941 * Fires when a column is moved.
14942 * @param {ColumnModel} this
14943 * @param {Number} oldIndex
14944 * @param {Number} newIndex
14946 "columnmoved" : true,
14948 * @event columlockchange
14949 * Fires when a column's locked state is changed
14950 * @param {ColumnModel} this
14951 * @param {Number} colIndex
14952 * @param {Boolean} locked true if locked
14954 "columnlockchange" : true
14956 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14958 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14960 * @cfg {String} header The header text to display in the Grid view.
14963 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14964 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14965 * specified, the column's index is used as an index into the Record's data Array.
14968 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14969 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14972 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14973 * Defaults to the value of the {@link #defaultSortable} property.
14974 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14977 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14980 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14983 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14986 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14989 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14990 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14991 * default renderer uses the raw data value.
14994 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14998 * Returns the id of the column at the specified index.
14999 * @param {Number} index The column index
15000 * @return {String} the id
15002 getColumnId : function(index){
15003 return this.config[index].id;
15007 * Returns the column for a specified id.
15008 * @param {String} id The column id
15009 * @return {Object} the column
15011 getColumnById : function(id){
15012 return this.lookup[id];
15017 * Returns the column for a specified dataIndex.
15018 * @param {String} dataIndex The column dataIndex
15019 * @return {Object|Boolean} the column or false if not found
15021 getColumnByDataIndex: function(dataIndex){
15022 var index = this.findColumnIndex(dataIndex);
15023 return index > -1 ? this.config[index] : false;
15027 * Returns the index for a specified column id.
15028 * @param {String} id The column id
15029 * @return {Number} the index, or -1 if not found
15031 getIndexById : function(id){
15032 for(var i = 0, len = this.config.length; i < len; i++){
15033 if(this.config[i].id == id){
15041 * Returns the index for a specified column dataIndex.
15042 * @param {String} dataIndex The column dataIndex
15043 * @return {Number} the index, or -1 if not found
15046 findColumnIndex : function(dataIndex){
15047 for(var i = 0, len = this.config.length; i < len; i++){
15048 if(this.config[i].dataIndex == dataIndex){
15056 moveColumn : function(oldIndex, newIndex){
15057 var c = this.config[oldIndex];
15058 this.config.splice(oldIndex, 1);
15059 this.config.splice(newIndex, 0, c);
15060 this.dataMap = null;
15061 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15064 isLocked : function(colIndex){
15065 return this.config[colIndex].locked === true;
15068 setLocked : function(colIndex, value, suppressEvent){
15069 if(this.isLocked(colIndex) == value){
15072 this.config[colIndex].locked = value;
15073 if(!suppressEvent){
15074 this.fireEvent("columnlockchange", this, colIndex, value);
15078 getTotalLockedWidth : function(){
15079 var totalWidth = 0;
15080 for(var i = 0; i < this.config.length; i++){
15081 if(this.isLocked(i) && !this.isHidden(i)){
15082 this.totalWidth += this.getColumnWidth(i);
15088 getLockedCount : function(){
15089 for(var i = 0, len = this.config.length; i < len; i++){
15090 if(!this.isLocked(i)){
15097 * Returns the number of columns.
15100 getColumnCount : function(visibleOnly){
15101 if(visibleOnly === true){
15103 for(var i = 0, len = this.config.length; i < len; i++){
15104 if(!this.isHidden(i)){
15110 return this.config.length;
15114 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15115 * @param {Function} fn
15116 * @param {Object} scope (optional)
15117 * @return {Array} result
15119 getColumnsBy : function(fn, scope){
15121 for(var i = 0, len = this.config.length; i < len; i++){
15122 var c = this.config[i];
15123 if(fn.call(scope||this, c, i) === true){
15131 * Returns true if the specified column is sortable.
15132 * @param {Number} col The column index
15133 * @return {Boolean}
15135 isSortable : function(col){
15136 if(typeof this.config[col].sortable == "undefined"){
15137 return this.defaultSortable;
15139 return this.config[col].sortable;
15143 * Returns the rendering (formatting) function defined for the column.
15144 * @param {Number} col The column index.
15145 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15147 getRenderer : function(col){
15148 if(!this.config[col].renderer){
15149 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15151 return this.config[col].renderer;
15155 * Sets the rendering (formatting) function for a column.
15156 * @param {Number} col The column index
15157 * @param {Function} fn The function to use to process the cell's raw data
15158 * to return HTML markup for the grid view. The render function is called with
15159 * the following parameters:<ul>
15160 * <li>Data value.</li>
15161 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15162 * <li>css A CSS style string to apply to the table cell.</li>
15163 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15164 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15165 * <li>Row index</li>
15166 * <li>Column index</li>
15167 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15169 setRenderer : function(col, fn){
15170 this.config[col].renderer = fn;
15174 * Returns the width for the specified column.
15175 * @param {Number} col The column index
15178 getColumnWidth : function(col){
15179 return this.config[col].width * 1 || this.defaultWidth;
15183 * Sets the width for a column.
15184 * @param {Number} col The column index
15185 * @param {Number} width The new width
15187 setColumnWidth : function(col, width, suppressEvent){
15188 this.config[col].width = width;
15189 this.totalWidth = null;
15190 if(!suppressEvent){
15191 this.fireEvent("widthchange", this, col, width);
15196 * Returns the total width of all columns.
15197 * @param {Boolean} includeHidden True to include hidden column widths
15200 getTotalWidth : function(includeHidden){
15201 if(!this.totalWidth){
15202 this.totalWidth = 0;
15203 for(var i = 0, len = this.config.length; i < len; i++){
15204 if(includeHidden || !this.isHidden(i)){
15205 this.totalWidth += this.getColumnWidth(i);
15209 return this.totalWidth;
15213 * Returns the header for the specified column.
15214 * @param {Number} col The column index
15217 getColumnHeader : function(col){
15218 return this.config[col].header;
15222 * Sets the header for a column.
15223 * @param {Number} col The column index
15224 * @param {String} header The new header
15226 setColumnHeader : function(col, header){
15227 this.config[col].header = header;
15228 this.fireEvent("headerchange", this, col, header);
15232 * Returns the tooltip for the specified column.
15233 * @param {Number} col The column index
15236 getColumnTooltip : function(col){
15237 return this.config[col].tooltip;
15240 * Sets the tooltip for a column.
15241 * @param {Number} col The column index
15242 * @param {String} tooltip The new tooltip
15244 setColumnTooltip : function(col, tooltip){
15245 this.config[col].tooltip = tooltip;
15249 * Returns the dataIndex for the specified column.
15250 * @param {Number} col The column index
15253 getDataIndex : function(col){
15254 return this.config[col].dataIndex;
15258 * Sets the dataIndex for a column.
15259 * @param {Number} col The column index
15260 * @param {Number} dataIndex The new dataIndex
15262 setDataIndex : function(col, dataIndex){
15263 this.config[col].dataIndex = dataIndex;
15269 * Returns true if the cell is editable.
15270 * @param {Number} colIndex The column index
15271 * @param {Number} rowIndex The row index
15272 * @return {Boolean}
15274 isCellEditable : function(colIndex, rowIndex){
15275 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15279 * Returns the editor defined for the cell/column.
15280 * return false or null to disable editing.
15281 * @param {Number} colIndex The column index
15282 * @param {Number} rowIndex The row index
15285 getCellEditor : function(colIndex, rowIndex){
15286 return this.config[colIndex].editor;
15290 * Sets if a column is editable.
15291 * @param {Number} col The column index
15292 * @param {Boolean} editable True if the column is editable
15294 setEditable : function(col, editable){
15295 this.config[col].editable = editable;
15300 * Returns true if the column is hidden.
15301 * @param {Number} colIndex The column index
15302 * @return {Boolean}
15304 isHidden : function(colIndex){
15305 return this.config[colIndex].hidden;
15310 * Returns true if the column width cannot be changed
15312 isFixed : function(colIndex){
15313 return this.config[colIndex].fixed;
15317 * Returns true if the column can be resized
15318 * @return {Boolean}
15320 isResizable : function(colIndex){
15321 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15324 * Sets if a column is hidden.
15325 * @param {Number} colIndex The column index
15326 * @param {Boolean} hidden True if the column is hidden
15328 setHidden : function(colIndex, hidden){
15329 this.config[colIndex].hidden = hidden;
15330 this.totalWidth = null;
15331 this.fireEvent("hiddenchange", this, colIndex, hidden);
15335 * Sets the editor for a column.
15336 * @param {Number} col The column index
15337 * @param {Object} editor The editor object
15339 setEditor : function(col, editor){
15340 this.config[col].editor = editor;
15344 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15345 if(typeof value == "string" && value.length < 1){
15351 // Alias for backwards compatibility
15352 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15355 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15356 * @class Roo.bootstrap.Table.RowSelectionModel
15357 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15358 * It supports multiple selections and keyboard selection/navigation.
15360 * @param {Object} config
15363 Roo.bootstrap.Table.RowSelectionModel = function(config){
15364 Roo.apply(this, config);
15365 this.selections = new Roo.util.MixedCollection(false, function(o){
15370 this.lastActive = false;
15374 * @event selectionchange
15375 * Fires when the selection changes
15376 * @param {SelectionModel} this
15378 "selectionchange" : true,
15380 * @event afterselectionchange
15381 * Fires after the selection changes (eg. by key press or clicking)
15382 * @param {SelectionModel} this
15384 "afterselectionchange" : true,
15386 * @event beforerowselect
15387 * Fires when a row is selected being selected, return false to cancel.
15388 * @param {SelectionModel} this
15389 * @param {Number} rowIndex The selected index
15390 * @param {Boolean} keepExisting False if other selections will be cleared
15392 "beforerowselect" : true,
15395 * Fires when a row is selected.
15396 * @param {SelectionModel} this
15397 * @param {Number} rowIndex The selected index
15398 * @param {Roo.data.Record} r The record
15400 "rowselect" : true,
15402 * @event rowdeselect
15403 * Fires when a row is deselected.
15404 * @param {SelectionModel} this
15405 * @param {Number} rowIndex The selected index
15407 "rowdeselect" : true
15409 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15410 this.locked = false;
15413 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15415 * @cfg {Boolean} singleSelect
15416 * True to allow selection of only one row at a time (defaults to false)
15418 singleSelect : false,
15421 initEvents : function(){
15423 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15424 this.grid.on("mousedown", this.handleMouseDown, this);
15425 }else{ // allow click to work like normal
15426 this.grid.on("rowclick", this.handleDragableRowClick, this);
15429 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15430 "up" : function(e){
15432 this.selectPrevious(e.shiftKey);
15433 }else if(this.last !== false && this.lastActive !== false){
15434 var last = this.last;
15435 this.selectRange(this.last, this.lastActive-1);
15436 this.grid.getView().focusRow(this.lastActive);
15437 if(last !== false){
15441 this.selectFirstRow();
15443 this.fireEvent("afterselectionchange", this);
15445 "down" : function(e){
15447 this.selectNext(e.shiftKey);
15448 }else if(this.last !== false && this.lastActive !== false){
15449 var last = this.last;
15450 this.selectRange(this.last, this.lastActive+1);
15451 this.grid.getView().focusRow(this.lastActive);
15452 if(last !== false){
15456 this.selectFirstRow();
15458 this.fireEvent("afterselectionchange", this);
15463 var view = this.grid.view;
15464 view.on("refresh", this.onRefresh, this);
15465 view.on("rowupdated", this.onRowUpdated, this);
15466 view.on("rowremoved", this.onRemove, this);
15470 onRefresh : function(){
15471 var ds = this.grid.dataSource, i, v = this.grid.view;
15472 var s = this.selections;
15473 s.each(function(r){
15474 if((i = ds.indexOfId(r.id)) != -1){
15483 onRemove : function(v, index, r){
15484 this.selections.remove(r);
15488 onRowUpdated : function(v, index, r){
15489 if(this.isSelected(r)){
15490 v.onRowSelect(index);
15496 * @param {Array} records The records to select
15497 * @param {Boolean} keepExisting (optional) True to keep existing selections
15499 selectRecords : function(records, keepExisting){
15501 this.clearSelections();
15503 var ds = this.grid.dataSource;
15504 for(var i = 0, len = records.length; i < len; i++){
15505 this.selectRow(ds.indexOf(records[i]), true);
15510 * Gets the number of selected rows.
15513 getCount : function(){
15514 return this.selections.length;
15518 * Selects the first row in the grid.
15520 selectFirstRow : function(){
15525 * Select the last row.
15526 * @param {Boolean} keepExisting (optional) True to keep existing selections
15528 selectLastRow : function(keepExisting){
15529 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15533 * Selects the row immediately following the last selected row.
15534 * @param {Boolean} keepExisting (optional) True to keep existing selections
15536 selectNext : function(keepExisting){
15537 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15538 this.selectRow(this.last+1, keepExisting);
15539 this.grid.getView().focusRow(this.last);
15544 * Selects the row that precedes the last selected row.
15545 * @param {Boolean} keepExisting (optional) True to keep existing selections
15547 selectPrevious : function(keepExisting){
15549 this.selectRow(this.last-1, keepExisting);
15550 this.grid.getView().focusRow(this.last);
15555 * Returns the selected records
15556 * @return {Array} Array of selected records
15558 getSelections : function(){
15559 return [].concat(this.selections.items);
15563 * Returns the first selected record.
15566 getSelected : function(){
15567 return this.selections.itemAt(0);
15572 * Clears all selections.
15574 clearSelections : function(fast){
15575 if(this.locked) return;
15577 var ds = this.grid.dataSource;
15578 var s = this.selections;
15579 s.each(function(r){
15580 this.deselectRow(ds.indexOfId(r.id));
15584 this.selections.clear();
15591 * Selects all rows.
15593 selectAll : function(){
15594 if(this.locked) return;
15595 this.selections.clear();
15596 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15597 this.selectRow(i, true);
15602 * Returns True if there is a selection.
15603 * @return {Boolean}
15605 hasSelection : function(){
15606 return this.selections.length > 0;
15610 * Returns True if the specified row is selected.
15611 * @param {Number/Record} record The record or index of the record to check
15612 * @return {Boolean}
15614 isSelected : function(index){
15615 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15616 return (r && this.selections.key(r.id) ? true : false);
15620 * Returns True if the specified record id is selected.
15621 * @param {String} id The id of record to check
15622 * @return {Boolean}
15624 isIdSelected : function(id){
15625 return (this.selections.key(id) ? true : false);
15629 handleMouseDown : function(e, t){
15630 var view = this.grid.getView(), rowIndex;
15631 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15634 if(e.shiftKey && this.last !== false){
15635 var last = this.last;
15636 this.selectRange(last, rowIndex, e.ctrlKey);
15637 this.last = last; // reset the last
15638 view.focusRow(rowIndex);
15640 var isSelected = this.isSelected(rowIndex);
15641 if(e.button !== 0 && isSelected){
15642 view.focusRow(rowIndex);
15643 }else if(e.ctrlKey && isSelected){
15644 this.deselectRow(rowIndex);
15645 }else if(!isSelected){
15646 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15647 view.focusRow(rowIndex);
15650 this.fireEvent("afterselectionchange", this);
15653 handleDragableRowClick : function(grid, rowIndex, e)
15655 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15656 this.selectRow(rowIndex, false);
15657 grid.view.focusRow(rowIndex);
15658 this.fireEvent("afterselectionchange", this);
15663 * Selects multiple rows.
15664 * @param {Array} rows Array of the indexes of the row to select
15665 * @param {Boolean} keepExisting (optional) True to keep existing selections
15667 selectRows : function(rows, keepExisting){
15669 this.clearSelections();
15671 for(var i = 0, len = rows.length; i < len; i++){
15672 this.selectRow(rows[i], true);
15677 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15678 * @param {Number} startRow The index of the first row in the range
15679 * @param {Number} endRow The index of the last row in the range
15680 * @param {Boolean} keepExisting (optional) True to retain existing selections
15682 selectRange : function(startRow, endRow, keepExisting){
15683 if(this.locked) return;
15685 this.clearSelections();
15687 if(startRow <= endRow){
15688 for(var i = startRow; i <= endRow; i++){
15689 this.selectRow(i, true);
15692 for(var i = startRow; i >= endRow; i--){
15693 this.selectRow(i, true);
15699 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15700 * @param {Number} startRow The index of the first row in the range
15701 * @param {Number} endRow The index of the last row in the range
15703 deselectRange : function(startRow, endRow, preventViewNotify){
15704 if(this.locked) return;
15705 for(var i = startRow; i <= endRow; i++){
15706 this.deselectRow(i, preventViewNotify);
15712 * @param {Number} row The index of the row to select
15713 * @param {Boolean} keepExisting (optional) True to keep existing selections
15715 selectRow : function(index, keepExisting, preventViewNotify){
15716 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15717 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15718 if(!keepExisting || this.singleSelect){
15719 this.clearSelections();
15721 var r = this.grid.dataSource.getAt(index);
15722 this.selections.add(r);
15723 this.last = this.lastActive = index;
15724 if(!preventViewNotify){
15725 this.grid.getView().onRowSelect(index);
15727 this.fireEvent("rowselect", this, index, r);
15728 this.fireEvent("selectionchange", this);
15734 * @param {Number} row The index of the row to deselect
15736 deselectRow : function(index, preventViewNotify){
15737 if(this.locked) return;
15738 if(this.last == index){
15741 if(this.lastActive == index){
15742 this.lastActive = false;
15744 var r = this.grid.dataSource.getAt(index);
15745 this.selections.remove(r);
15746 if(!preventViewNotify){
15747 this.grid.getView().onRowDeselect(index);
15749 this.fireEvent("rowdeselect", this, index);
15750 this.fireEvent("selectionchange", this);
15754 restoreLast : function(){
15756 this.last = this._last;
15761 acceptsNav : function(row, col, cm){
15762 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15766 onEditorKey : function(field, e){
15767 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15772 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15774 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15776 }else if(k == e.ENTER && !e.ctrlKey){
15780 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15782 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15784 }else if(k == e.ESC){
15788 g.startEditing(newCell[0], newCell[1]);
15799 * @class Roo.bootstrap.MessageBar
15800 * @extends Roo.bootstrap.Component
15801 * Bootstrap MessageBar class
15802 * @cfg {String} html contents of the MessageBar
15803 * @cfg {String} weight (info | success | warning | danger) default info
15804 * @cfg {String} beforeClass insert the bar before the given class
15805 * @cfg {Boolean} closable (true | false) default false
15806 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15809 * Create a new Element
15810 * @param {Object} config The config object
15813 Roo.bootstrap.MessageBar = function(config){
15814 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15817 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15823 beforeClass: 'bootstrap-sticky-wrap',
15825 getAutoCreate : function(){
15829 cls: 'alert alert-dismissable alert-' + this.weight,
15834 html: this.html || ''
15840 cfg.cls += ' alert-messages-fixed';
15854 onRender : function(ct, position)
15856 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15859 var cfg = Roo.apply({}, this.getAutoCreate());
15863 cfg.cls += ' ' + this.cls;
15866 cfg.style = this.style;
15868 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15870 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15873 this.el.select('>button.close').on('click', this.hide, this);
15879 if (!this.rendered) {
15885 this.fireEvent('show', this);
15891 if (!this.rendered) {
15897 this.fireEvent('hide', this);
15900 update : function()
15902 // var e = this.el.dom.firstChild;
15904 // if(this.closable){
15905 // e = e.nextSibling;
15908 // e.data = this.html || '';
15910 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';