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 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3054 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3057 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3058 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3062 var tbody = this.el.select('tbody', true).first();
3066 if(this.store.getCount() > 0){
3067 this.store.data.each(function(d){
3073 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3074 var renderer = cm.getRenderer(i);
3075 var config = cm.config[i];
3079 if(typeof(renderer) !== 'undefined'){
3080 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3083 if(typeof(value) === 'object'){
3093 html: (typeof(value) === 'object') ? '' : value
3096 if(typeof(config.width) != 'undefined'){
3097 td.width = config.width;
3104 tbody.createChild(row);
3112 Roo.each(renders, function(r){
3113 _this.renderColumn(r);
3117 // if(this.loadMask){
3118 // this.maskEl.hide();
3122 onBeforeLoad : function()
3124 Roo.log('ds onBeforeLoad');
3128 // if(this.loadMask){
3129 // this.maskEl.show();
3135 this.el.select('tbody', true).first().dom.innerHTML = '';
3138 getSelectionModel : function(){
3140 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3142 return this.selModel;
3145 renderColumn : function(r)
3148 r.cfg.render(Roo.get(r.id));
3151 Roo.each(r.cfg.cn, function(c){
3156 _this.renderColumn(child);
3173 * @class Roo.bootstrap.TableCell
3174 * @extends Roo.bootstrap.Component
3175 * Bootstrap TableCell class
3176 * @cfg {String} html cell contain text
3177 * @cfg {String} cls cell class
3178 * @cfg {String} tag cell tag (td|th) default td
3179 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3180 * @cfg {String} align Aligns the content in a cell
3181 * @cfg {String} axis Categorizes cells
3182 * @cfg {String} bgcolor Specifies the background color of a cell
3183 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3184 * @cfg {Number} colspan Specifies the number of columns a cell should span
3185 * @cfg {String} headers Specifies one or more header cells a cell is related to
3186 * @cfg {Number} height Sets the height of a cell
3187 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3188 * @cfg {Number} rowspan Sets the number of rows a cell should span
3189 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3190 * @cfg {String} valign Vertical aligns the content in a cell
3191 * @cfg {Number} width Specifies the width of a cell
3194 * Create a new TableCell
3195 * @param {Object} config The config object
3198 Roo.bootstrap.TableCell = function(config){
3199 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3202 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3222 getAutoCreate : function(){
3223 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3243 cfg.align=this.align
3249 cfg.bgcolor=this.bgcolor
3252 cfg.charoff=this.charoff
3255 cfg.colspan=this.colspan
3258 cfg.headers=this.headers
3261 cfg.height=this.height
3264 cfg.nowrap=this.nowrap
3267 cfg.rowspan=this.rowspan
3270 cfg.scope=this.scope
3273 cfg.valign=this.valign
3276 cfg.width=this.width
3295 * @class Roo.bootstrap.TableRow
3296 * @extends Roo.bootstrap.Component
3297 * Bootstrap TableRow class
3298 * @cfg {String} cls row class
3299 * @cfg {String} align Aligns the content in a table row
3300 * @cfg {String} bgcolor Specifies a background color for a table row
3301 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3302 * @cfg {String} valign Vertical aligns the content in a table row
3305 * Create a new TableRow
3306 * @param {Object} config The config object
3309 Roo.bootstrap.TableRow = function(config){
3310 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3313 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3321 getAutoCreate : function(){
3322 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3332 cfg.align = this.align;
3335 cfg.bgcolor = this.bgcolor;
3338 cfg.charoff = this.charoff;
3341 cfg.valign = this.valign;
3359 * @class Roo.bootstrap.TableBody
3360 * @extends Roo.bootstrap.Component
3361 * Bootstrap TableBody class
3362 * @cfg {String} cls element class
3363 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3364 * @cfg {String} align Aligns the content inside the element
3365 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3366 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3369 * Create a new TableBody
3370 * @param {Object} config The config object
3373 Roo.bootstrap.TableBody = function(config){
3374 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3377 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3385 getAutoCreate : function(){
3386 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3400 cfg.align = this.align;
3403 cfg.charoff = this.charoff;
3406 cfg.valign = this.valign;
3413 // initEvents : function()
3420 // this.store = Roo.factory(this.store, Roo.data);
3421 // this.store.on('load', this.onLoad, this);
3423 // this.store.load();
3427 // onLoad: function ()
3429 // this.fireEvent('load', this);
3439 * Ext JS Library 1.1.1
3440 * Copyright(c) 2006-2007, Ext JS, LLC.
3442 * Originally Released Under LGPL - original licence link has changed is not relivant.
3445 * <script type="text/javascript">
3448 // as we use this in bootstrap.
3449 Roo.namespace('Roo.form');
3451 * @class Roo.form.Action
3452 * Internal Class used to handle form actions
3454 * @param {Roo.form.BasicForm} el The form element or its id
3455 * @param {Object} config Configuration options
3460 // define the action interface
3461 Roo.form.Action = function(form, options){
3463 this.options = options || {};
3466 * Client Validation Failed
3469 Roo.form.Action.CLIENT_INVALID = 'client';
3471 * Server Validation Failed
3474 Roo.form.Action.SERVER_INVALID = 'server';
3476 * Connect to Server Failed
3479 Roo.form.Action.CONNECT_FAILURE = 'connect';
3481 * Reading Data from Server Failed
3484 Roo.form.Action.LOAD_FAILURE = 'load';
3486 Roo.form.Action.prototype = {
3488 failureType : undefined,
3489 response : undefined,
3493 run : function(options){
3498 success : function(response){
3503 handleResponse : function(response){
3507 // default connection failure
3508 failure : function(response){
3510 this.response = response;
3511 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3512 this.form.afterAction(this, false);
3515 processResponse : function(response){
3516 this.response = response;
3517 if(!response.responseText){
3520 this.result = this.handleResponse(response);
3524 // utility functions used internally
3525 getUrl : function(appendParams){
3526 var url = this.options.url || this.form.url || this.form.el.dom.action;
3528 var p = this.getParams();
3530 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3536 getMethod : function(){
3537 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3540 getParams : function(){
3541 var bp = this.form.baseParams;
3542 var p = this.options.params;
3544 if(typeof p == "object"){
3545 p = Roo.urlEncode(Roo.applyIf(p, bp));
3546 }else if(typeof p == 'string' && bp){
3547 p += '&' + Roo.urlEncode(bp);
3550 p = Roo.urlEncode(bp);
3555 createCallback : function(){
3557 success: this.success,
3558 failure: this.failure,
3560 timeout: (this.form.timeout*1000),
3561 upload: this.form.fileUpload ? this.success : undefined
3566 Roo.form.Action.Submit = function(form, options){
3567 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3570 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3573 haveProgress : false,
3574 uploadComplete : false,
3576 // uploadProgress indicator.
3577 uploadProgress : function()
3579 if (!this.form.progressUrl) {
3583 if (!this.haveProgress) {
3584 Roo.MessageBox.progress("Uploading", "Uploading");
3586 if (this.uploadComplete) {
3587 Roo.MessageBox.hide();
3591 this.haveProgress = true;
3593 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3595 var c = new Roo.data.Connection();
3597 url : this.form.progressUrl,
3602 success : function(req){
3603 //console.log(data);
3607 rdata = Roo.decode(req.responseText)
3609 Roo.log("Invalid data from server..");
3613 if (!rdata || !rdata.success) {
3615 Roo.MessageBox.alert(Roo.encode(rdata));
3618 var data = rdata.data;
3620 if (this.uploadComplete) {
3621 Roo.MessageBox.hide();
3626 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3627 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3630 this.uploadProgress.defer(2000,this);
3633 failure: function(data) {
3634 Roo.log('progress url failed ');
3645 // run get Values on the form, so it syncs any secondary forms.
3646 this.form.getValues();
3648 var o = this.options;
3649 var method = this.getMethod();
3650 var isPost = method == 'POST';
3651 if(o.clientValidation === false || this.form.isValid()){
3653 if (this.form.progressUrl) {
3654 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3655 (new Date() * 1) + '' + Math.random());
3660 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3661 form:this.form.el.dom,
3662 url:this.getUrl(!isPost),
3664 params:isPost ? this.getParams() : null,
3665 isUpload: this.form.fileUpload
3668 this.uploadProgress();
3670 }else if (o.clientValidation !== false){ // client validation failed
3671 this.failureType = Roo.form.Action.CLIENT_INVALID;
3672 this.form.afterAction(this, false);
3676 success : function(response)
3678 this.uploadComplete= true;
3679 if (this.haveProgress) {
3680 Roo.MessageBox.hide();
3684 var result = this.processResponse(response);
3685 if(result === true || result.success){
3686 this.form.afterAction(this, true);
3690 this.form.markInvalid(result.errors);
3691 this.failureType = Roo.form.Action.SERVER_INVALID;
3693 this.form.afterAction(this, false);
3695 failure : function(response)
3697 this.uploadComplete= true;
3698 if (this.haveProgress) {
3699 Roo.MessageBox.hide();
3702 this.response = response;
3703 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3704 this.form.afterAction(this, false);
3707 handleResponse : function(response){
3708 if(this.form.errorReader){
3709 var rs = this.form.errorReader.read(response);
3712 for(var i = 0, len = rs.records.length; i < len; i++) {
3713 var r = rs.records[i];
3717 if(errors.length < 1){
3721 success : rs.success,
3727 ret = Roo.decode(response.responseText);
3731 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3741 Roo.form.Action.Load = function(form, options){
3742 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3743 this.reader = this.form.reader;
3746 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3751 Roo.Ajax.request(Roo.apply(
3752 this.createCallback(), {
3753 method:this.getMethod(),
3754 url:this.getUrl(false),
3755 params:this.getParams()
3759 success : function(response){
3761 var result = this.processResponse(response);
3762 if(result === true || !result.success || !result.data){
3763 this.failureType = Roo.form.Action.LOAD_FAILURE;
3764 this.form.afterAction(this, false);
3767 this.form.clearInvalid();
3768 this.form.setValues(result.data);
3769 this.form.afterAction(this, true);
3772 handleResponse : function(response){
3773 if(this.form.reader){
3774 var rs = this.form.reader.read(response);
3775 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3777 success : rs.success,
3781 return Roo.decode(response.responseText);
3785 Roo.form.Action.ACTION_TYPES = {
3786 'load' : Roo.form.Action.Load,
3787 'submit' : Roo.form.Action.Submit
3796 * @class Roo.bootstrap.Form
3797 * @extends Roo.bootstrap.Component
3798 * Bootstrap Form class
3799 * @cfg {String} method GET | POST (default POST)
3800 * @cfg {String} labelAlign top | left (default top)
3801 * @cfg {String} align left | right - for navbars
3806 * @param {Object} config The config object
3810 Roo.bootstrap.Form = function(config){
3811 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3814 * @event clientvalidation
3815 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3816 * @param {Form} this
3817 * @param {Boolean} valid true if the form has passed client-side validation
3819 clientvalidation: true,
3821 * @event beforeaction
3822 * Fires before any action is performed. Return false to cancel the action.
3823 * @param {Form} this
3824 * @param {Action} action The action to be performed
3828 * @event actionfailed
3829 * Fires when an action fails.
3830 * @param {Form} this
3831 * @param {Action} action The action that failed
3833 actionfailed : true,
3835 * @event actioncomplete
3836 * Fires when an action is completed.
3837 * @param {Form} this
3838 * @param {Action} action The action that completed
3840 actioncomplete : true
3845 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3848 * @cfg {String} method
3849 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3854 * The URL to use for form actions if one isn't supplied in the action options.
3857 * @cfg {Boolean} fileUpload
3858 * Set to true if this form is a file upload.
3862 * @cfg {Object} baseParams
3863 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3867 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3871 * @cfg {Sting} align (left|right) for navbar forms
3876 activeAction : null,
3879 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3880 * element by passing it or its id or mask the form itself by passing in true.
3883 waitMsgTarget : false,
3888 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3889 * element by passing it or its id or mask the form itself by passing in true.
3893 getAutoCreate : function(){
3897 method : this.method || 'POST',
3898 id : this.id || Roo.id(),
3901 if (this.parent().xtype.match(/^Nav/)) {
3902 cfg.cls = 'navbar-form navbar-' + this.align;
3906 if (this.labelAlign == 'left' ) {
3907 cfg.cls += ' form-horizontal';
3913 initEvents : function()
3915 this.el.on('submit', this.onSubmit, this);
3920 onSubmit : function(e){
3925 * Returns true if client-side validation on the form is successful.
3928 isValid : function(){
3929 var items = this.getItems();
3931 items.each(function(f){
3940 * Returns true if any fields in this form have changed since their original load.
3943 isDirty : function(){
3945 var items = this.getItems();
3946 items.each(function(f){
3956 * Performs a predefined action (submit or load) or custom actions you define on this form.
3957 * @param {String} actionName The name of the action type
3958 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3959 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3960 * accept other config options):
3962 Property Type Description
3963 ---------------- --------------- ----------------------------------------------------------------------------------
3964 url String The url for the action (defaults to the form's url)
3965 method String The form method to use (defaults to the form's method, or POST if not defined)
3966 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3967 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3968 validate the form on the client (defaults to false)
3970 * @return {BasicForm} this
3972 doAction : function(action, options){
3973 if(typeof action == 'string'){
3974 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3976 if(this.fireEvent('beforeaction', this, action) !== false){
3977 this.beforeAction(action);
3978 action.run.defer(100, action);
3984 beforeAction : function(action){
3985 var o = action.options;
3987 // not really supported yet.. ??
3989 //if(this.waitMsgTarget === true){
3990 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3991 //}else if(this.waitMsgTarget){
3992 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3993 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3995 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4001 afterAction : function(action, success){
4002 this.activeAction = null;
4003 var o = action.options;
4005 //if(this.waitMsgTarget === true){
4007 //}else if(this.waitMsgTarget){
4008 // this.waitMsgTarget.unmask();
4010 // Roo.MessageBox.updateProgress(1);
4011 // Roo.MessageBox.hide();
4018 Roo.callback(o.success, o.scope, [this, action]);
4019 this.fireEvent('actioncomplete', this, action);
4023 // failure condition..
4024 // we have a scenario where updates need confirming.
4025 // eg. if a locking scenario exists..
4026 // we look for { errors : { needs_confirm : true }} in the response.
4028 (typeof(action.result) != 'undefined') &&
4029 (typeof(action.result.errors) != 'undefined') &&
4030 (typeof(action.result.errors.needs_confirm) != 'undefined')
4033 Roo.log("not supported yet");
4036 Roo.MessageBox.confirm(
4037 "Change requires confirmation",
4038 action.result.errorMsg,
4043 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4053 Roo.callback(o.failure, o.scope, [this, action]);
4054 // show an error message if no failed handler is set..
4055 if (!this.hasListener('actionfailed')) {
4056 Roo.log("need to add dialog support");
4058 Roo.MessageBox.alert("Error",
4059 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4060 action.result.errorMsg :
4061 "Saving Failed, please check your entries or try again"
4066 this.fireEvent('actionfailed', this, action);
4071 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4072 * @param {String} id The value to search for
4075 findField : function(id){
4076 var items = this.getItems();
4077 var field = items.get(id);
4079 items.each(function(f){
4080 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4087 return field || null;
4090 * Mark fields in this form invalid in bulk.
4091 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4092 * @return {BasicForm} this
4094 markInvalid : function(errors){
4095 if(errors instanceof Array){
4096 for(var i = 0, len = errors.length; i < len; i++){
4097 var fieldError = errors[i];
4098 var f = this.findField(fieldError.id);
4100 f.markInvalid(fieldError.msg);
4106 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4107 field.markInvalid(errors[id]);
4111 //Roo.each(this.childForms || [], function (f) {
4112 // f.markInvalid(errors);
4119 * Set values for fields in this form in bulk.
4120 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4121 * @return {BasicForm} this
4123 setValues : function(values){
4124 if(values instanceof Array){ // array of objects
4125 for(var i = 0, len = values.length; i < len; i++){
4127 var f = this.findField(v.id);
4129 f.setValue(v.value);
4130 if(this.trackResetOnLoad){
4131 f.originalValue = f.getValue();
4135 }else{ // object hash
4138 if(typeof values[id] != 'function' && (field = this.findField(id))){
4140 if (field.setFromData &&
4142 field.displayField &&
4143 // combos' with local stores can
4144 // be queried via setValue()
4145 // to set their value..
4146 (field.store && !field.store.isLocal)
4150 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4151 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4152 field.setFromData(sd);
4155 field.setValue(values[id]);
4159 if(this.trackResetOnLoad){
4160 field.originalValue = field.getValue();
4166 //Roo.each(this.childForms || [], function (f) {
4167 // f.setValues(values);
4174 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4175 * they are returned as an array.
4176 * @param {Boolean} asString
4179 getValues : function(asString){
4180 //if (this.childForms) {
4181 // copy values from the child forms
4182 // Roo.each(this.childForms, function (f) {
4183 // this.setValues(f.getValues());
4189 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4190 if(asString === true){
4193 return Roo.urlDecode(fs);
4197 * Returns the fields in this form as an object with key/value pairs.
4198 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4201 getFieldValues : function(with_hidden)
4203 var items = this.getItems();
4205 items.each(function(f){
4209 var v = f.getValue();
4210 if (f.inputType =='radio') {
4211 if (typeof(ret[f.getName()]) == 'undefined') {
4212 ret[f.getName()] = ''; // empty..
4215 if (!f.el.dom.checked) {
4223 // not sure if this supported any more..
4224 if ((typeof(v) == 'object') && f.getRawValue) {
4225 v = f.getRawValue() ; // dates..
4227 // combo boxes where name != hiddenName...
4228 if (f.name != f.getName()) {
4229 ret[f.name] = f.getRawValue();
4231 ret[f.getName()] = v;
4238 * Clears all invalid messages in this form.
4239 * @return {BasicForm} this
4241 clearInvalid : function(){
4242 var items = this.getItems();
4244 items.each(function(f){
4255 * @return {BasicForm} this
4258 var items = this.getItems();
4259 items.each(function(f){
4263 Roo.each(this.childForms || [], function (f) {
4270 getItems : function()
4272 var r=new Roo.util.MixedCollection(false, function(o){
4273 return o.id || (o.id = Roo.id());
4275 var iter = function(el) {
4282 Roo.each(el.items,function(e) {
4301 * Ext JS Library 1.1.1
4302 * Copyright(c) 2006-2007, Ext JS, LLC.
4304 * Originally Released Under LGPL - original licence link has changed is not relivant.
4307 * <script type="text/javascript">
4310 * @class Roo.form.VTypes
4311 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4314 Roo.form.VTypes = function(){
4315 // closure these in so they are only created once.
4316 var alpha = /^[a-zA-Z_]+$/;
4317 var alphanum = /^[a-zA-Z0-9_]+$/;
4318 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4319 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4321 // All these messages and functions are configurable
4324 * The function used to validate email addresses
4325 * @param {String} value The email address
4327 'email' : function(v){
4328 return email.test(v);
4331 * The error text to display when the email validation function returns false
4334 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4336 * The keystroke filter mask to be applied on email input
4339 'emailMask' : /[a-z0-9_\.\-@]/i,
4342 * The function used to validate URLs
4343 * @param {String} value The URL
4345 'url' : function(v){
4349 * The error text to display when the url validation function returns false
4352 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4355 * The function used to validate alpha values
4356 * @param {String} value The value
4358 'alpha' : function(v){
4359 return alpha.test(v);
4362 * The error text to display when the alpha validation function returns false
4365 'alphaText' : 'This field should only contain letters and _',
4367 * The keystroke filter mask to be applied on alpha input
4370 'alphaMask' : /[a-z_]/i,
4373 * The function used to validate alphanumeric values
4374 * @param {String} value The value
4376 'alphanum' : function(v){
4377 return alphanum.test(v);
4380 * The error text to display when the alphanumeric validation function returns false
4383 'alphanumText' : 'This field should only contain letters, numbers and _',
4385 * The keystroke filter mask to be applied on alphanumeric input
4388 'alphanumMask' : /[a-z0-9_]/i
4398 * @class Roo.bootstrap.Input
4399 * @extends Roo.bootstrap.Component
4400 * Bootstrap Input class
4401 * @cfg {Boolean} disabled is it disabled
4402 * @cfg {String} fieldLabel - the label associated
4403 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4404 * @cfg {String} name name of the input
4405 * @cfg {string} fieldLabel - the label associated
4406 * @cfg {string} inputType - input / file submit ...
4407 * @cfg {string} placeholder - placeholder to put in text.
4408 * @cfg {string} before - input group add on before
4409 * @cfg {string} after - input group add on after
4410 * @cfg {string} size - (lg|sm) or leave empty..
4411 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4412 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4413 * @cfg {Number} md colspan out of 12 for computer-sized screens
4414 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4415 * @cfg {string} value default value of the input
4416 * @cfg {Number} labelWidth set the width of label (0-12)
4417 * @cfg {String} labelAlign (top|left)
4418 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4422 * Create a new Input
4423 * @param {Object} config The config object
4426 Roo.bootstrap.Input = function(config){
4427 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4432 * Fires when this field receives input focus.
4433 * @param {Roo.form.Field} this
4438 * Fires when this field loses input focus.
4439 * @param {Roo.form.Field} this
4444 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4445 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4446 * @param {Roo.form.Field} this
4447 * @param {Roo.EventObject} e The event object
4452 * Fires just before the field blurs if the field value has changed.
4453 * @param {Roo.form.Field} this
4454 * @param {Mixed} newValue The new value
4455 * @param {Mixed} oldValue The original value
4460 * Fires after the field has been marked as invalid.
4461 * @param {Roo.form.Field} this
4462 * @param {String} msg The validation message
4467 * Fires after the field has been validated with no errors.
4468 * @param {Roo.form.Field} this
4473 * Fires after the key up
4474 * @param {Roo.form.Field} this
4475 * @param {Roo.EventObject} e The event Object
4481 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4483 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4484 automatic validation (defaults to "keyup").
4486 validationEvent : "keyup",
4488 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4490 validateOnBlur : true,
4492 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4494 validationDelay : 250,
4496 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4498 focusClass : "x-form-focus", // not needed???
4502 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4504 invalidClass : "has-error",
4507 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4509 selectOnFocus : false,
4512 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4516 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4521 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4523 disableKeyFilter : false,
4526 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4530 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4534 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4536 blankText : "This field is required",
4539 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4543 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4545 maxLength : Number.MAX_VALUE,
4547 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4549 minLengthText : "The minimum length for this field is {0}",
4551 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4553 maxLengthText : "The maximum length for this field is {0}",
4557 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4558 * If available, this function will be called only after the basic validators all return true, and will be passed the
4559 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4563 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4564 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4565 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4569 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4592 parentLabelAlign : function()
4595 while (parent.parent()) {
4596 parent = parent.parent();
4597 if (typeof(parent.labelAlign) !='undefined') {
4598 return parent.labelAlign;
4605 getAutoCreate : function(){
4607 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4613 if(this.inputType != 'hidden'){
4614 cfg.cls = 'form-group' //input-group
4620 type : this.inputType,
4622 cls : 'form-control',
4623 placeholder : this.placeholder || ''
4627 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4628 input.maxLength = this.maxLength;
4631 if (this.disabled) {
4632 input.disabled=true;
4635 if (this.readOnly) {
4636 input.readonly=true;
4640 input.name = this.name;
4643 input.cls += ' input-' + this.size;
4646 ['xs','sm','md','lg'].map(function(size){
4647 if (settings[size]) {
4648 cfg.cls += ' col-' + size + '-' + settings[size];
4652 var inputblock = input;
4654 if (this.before || this.after) {
4657 cls : 'input-group',
4661 inputblock.cn.push({
4663 cls : 'input-group-addon',
4667 inputblock.cn.push(input);
4669 inputblock.cn.push({
4671 cls : 'input-group-addon',
4678 if (align ==='left' && this.fieldLabel.length) {
4679 Roo.log("left and has label");
4685 cls : 'control-label col-sm-' + this.labelWidth,
4686 html : this.fieldLabel
4690 cls : "col-sm-" + (12 - this.labelWidth),
4697 } else if ( this.fieldLabel.length) {
4703 //cls : 'input-group-addon',
4704 html : this.fieldLabel
4714 Roo.log(" no label && no align");
4723 Roo.log('input-parentType: ' + this.parentType);
4725 if (this.parentType === 'Navbar' && this.parent().bar) {
4726 cfg.cls += ' navbar-form';
4734 * return the real input element.
4736 inputEl: function ()
4738 return this.el.select('input.form-control',true).first();
4740 setDisabled : function(v)
4742 var i = this.inputEl().dom;
4744 i.removeAttribute('disabled');
4748 i.setAttribute('disabled','true');
4750 initEvents : function()
4753 this.inputEl().on("keydown" , this.fireKey, this);
4754 this.inputEl().on("focus", this.onFocus, this);
4755 this.inputEl().on("blur", this.onBlur, this);
4757 this.inputEl().relayEvent('keyup', this);
4759 // reference to original value for reset
4760 this.originalValue = this.getValue();
4761 //Roo.form.TextField.superclass.initEvents.call(this);
4762 if(this.validationEvent == 'keyup'){
4763 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4764 this.inputEl().on('keyup', this.filterValidation, this);
4766 else if(this.validationEvent !== false){
4767 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4770 if(this.selectOnFocus){
4771 this.on("focus", this.preFocus, this);
4774 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4775 this.inputEl().on("keypress", this.filterKeys, this);
4778 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4779 this.el.on("click", this.autoSize, this);
4782 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4783 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4787 filterValidation : function(e){
4788 if(!e.isNavKeyPress()){
4789 this.validationTask.delay(this.validationDelay);
4793 * Validates the field value
4794 * @return {Boolean} True if the value is valid, else false
4796 validate : function(){
4797 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4798 if(this.disabled || this.validateValue(this.getRawValue())){
4799 this.clearInvalid();
4807 * Validates a value according to the field's validation rules and marks the field as invalid
4808 * if the validation fails
4809 * @param {Mixed} value The value to validate
4810 * @return {Boolean} True if the value is valid, else false
4812 validateValue : function(value){
4813 if(value.length < 1) { // if it's blank
4814 if(this.allowBlank){
4815 this.clearInvalid();
4818 this.markInvalid(this.blankText);
4822 if(value.length < this.minLength){
4823 this.markInvalid(String.format(this.minLengthText, this.minLength));
4826 if(value.length > this.maxLength){
4827 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4831 var vt = Roo.form.VTypes;
4832 if(!vt[this.vtype](value, this)){
4833 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4837 if(typeof this.validator == "function"){
4838 var msg = this.validator(value);
4840 this.markInvalid(msg);
4844 if(this.regex && !this.regex.test(value)){
4845 this.markInvalid(this.regexText);
4854 fireKey : function(e){
4855 //Roo.log('field ' + e.getKey());
4856 if(e.isNavKeyPress()){
4857 this.fireEvent("specialkey", this, e);
4860 focus : function (selectText){
4862 this.inputEl().focus();
4863 if(selectText === true){
4864 this.inputEl().dom.select();
4870 onFocus : function(){
4871 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4872 // this.el.addClass(this.focusClass);
4875 this.hasFocus = true;
4876 this.startValue = this.getValue();
4877 this.fireEvent("focus", this);
4881 beforeBlur : Roo.emptyFn,
4885 onBlur : function(){
4887 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4888 //this.el.removeClass(this.focusClass);
4890 this.hasFocus = false;
4891 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4894 var v = this.getValue();
4895 if(String(v) !== String(this.startValue)){
4896 this.fireEvent('change', this, v, this.startValue);
4898 this.fireEvent("blur", this);
4902 * Resets the current field value to the originally loaded value and clears any validation messages
4905 this.setValue(this.originalValue);
4906 this.clearInvalid();
4909 * Returns the name of the field
4910 * @return {Mixed} name The name field
4912 getName: function(){
4916 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4917 * @return {Mixed} value The field value
4919 getValue : function(){
4920 return this.inputEl().getValue();
4923 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4924 * @return {Mixed} value The field value
4926 getRawValue : function(){
4927 var v = this.inputEl().getValue();
4933 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4934 * @param {Mixed} value The value to set
4936 setRawValue : function(v){
4937 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4940 selectText : function(start, end){
4941 var v = this.getRawValue();
4943 start = start === undefined ? 0 : start;
4944 end = end === undefined ? v.length : end;
4945 var d = this.inputEl().dom;
4946 if(d.setSelectionRange){
4947 d.setSelectionRange(start, end);
4948 }else if(d.createTextRange){
4949 var range = d.createTextRange();
4950 range.moveStart("character", start);
4951 range.moveEnd("character", v.length-end);
4958 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4959 * @param {Mixed} value The value to set
4961 setValue : function(v){
4964 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4970 processValue : function(value){
4971 if(this.stripCharsRe){
4972 var newValue = value.replace(this.stripCharsRe, '');
4973 if(newValue !== value){
4974 this.setRawValue(newValue);
4981 preFocus : function(){
4983 if(this.selectOnFocus){
4984 this.inputEl().dom.select();
4987 filterKeys : function(e){
4989 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4992 var c = e.getCharCode(), cc = String.fromCharCode(c);
4993 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4996 if(!this.maskRe.test(cc)){
5001 * Clear any invalid styles/messages for this field
5003 clearInvalid : function(){
5005 if(!this.el || this.preventMark){ // not rendered
5008 this.el.removeClass(this.invalidClass);
5010 switch(this.msgTarget){
5012 this.el.dom.qtip = '';
5015 this.el.dom.title = '';
5019 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5024 this.errorIcon.dom.qtip = '';
5025 this.errorIcon.hide();
5026 this.un('resize', this.alignErrorIcon, this);
5030 var t = Roo.getDom(this.msgTarget);
5032 t.style.display = 'none';
5036 this.fireEvent('valid', this);
5039 * Mark this field as invalid
5040 * @param {String} msg The validation message
5042 markInvalid : function(msg){
5043 if(!this.el || this.preventMark){ // not rendered
5046 this.el.addClass(this.invalidClass);
5048 msg = msg || this.invalidText;
5049 switch(this.msgTarget){
5051 this.el.dom.qtip = msg;
5052 this.el.dom.qclass = 'x-form-invalid-tip';
5053 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5054 Roo.QuickTips.enable();
5058 this.el.dom.title = msg;
5062 var elp = this.el.findParent('.x-form-element', 5, true);
5063 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5064 this.errorEl.setWidth(elp.getWidth(true)-20);
5066 this.errorEl.update(msg);
5067 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5070 if(!this.errorIcon){
5071 var elp = this.el.findParent('.x-form-element', 5, true);
5072 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5074 this.alignErrorIcon();
5075 this.errorIcon.dom.qtip = msg;
5076 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5077 this.errorIcon.show();
5078 this.on('resize', this.alignErrorIcon, this);
5081 var t = Roo.getDom(this.msgTarget);
5083 t.style.display = this.msgDisplay;
5087 this.fireEvent('invalid', this, msg);
5090 SafariOnKeyDown : function(event)
5092 // this is a workaround for a password hang bug on chrome/ webkit.
5094 var isSelectAll = false;
5096 if(this.inputEl().dom.selectionEnd > 0){
5097 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5099 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5100 event.preventDefault();
5105 if(isSelectAll){ // backspace and delete key
5107 event.preventDefault();
5108 // this is very hacky as keydown always get's upper case.
5110 var cc = String.fromCharCode(event.getCharCode());
5111 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5115 adjustWidth : function(tag, w){
5116 tag = tag.toLowerCase();
5117 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5118 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5122 if(tag == 'textarea'){
5125 }else if(Roo.isOpera){
5129 if(tag == 'textarea'){
5148 * @class Roo.bootstrap.TextArea
5149 * @extends Roo.bootstrap.Input
5150 * Bootstrap TextArea class
5151 * @cfg {Number} cols Specifies the visible width of a text area
5152 * @cfg {Number} rows Specifies the visible number of lines in a text area
5153 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5154 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5155 * @cfg {string} html text
5158 * Create a new TextArea
5159 * @param {Object} config The config object
5162 Roo.bootstrap.TextArea = function(config){
5163 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5167 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5177 getAutoCreate : function(){
5179 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5190 value : this.value || '',
5191 html: this.html || '',
5192 cls : 'form-control',
5193 placeholder : this.placeholder || ''
5197 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5198 input.maxLength = this.maxLength;
5202 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5206 input.cols = this.cols;
5209 if (this.readOnly) {
5210 input.readonly = true;
5214 input.name = this.name;
5218 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5222 ['xs','sm','md','lg'].map(function(size){
5223 if (settings[size]) {
5224 cfg.cls += ' col-' + size + '-' + settings[size];
5228 var inputblock = input;
5230 if (this.before || this.after) {
5233 cls : 'input-group',
5237 inputblock.cn.push({
5239 cls : 'input-group-addon',
5243 inputblock.cn.push(input);
5245 inputblock.cn.push({
5247 cls : 'input-group-addon',
5254 if (align ==='left' && this.fieldLabel.length) {
5255 Roo.log("left and has label");
5261 cls : 'control-label col-sm-' + this.labelWidth,
5262 html : this.fieldLabel
5266 cls : "col-sm-" + (12 - this.labelWidth),
5273 } else if ( this.fieldLabel.length) {
5279 //cls : 'input-group-addon',
5280 html : this.fieldLabel
5290 Roo.log(" no label && no align");
5300 if (this.disabled) {
5301 input.disabled=true;
5308 * return the real textarea element.
5310 inputEl: function ()
5312 return this.el.select('textarea.form-control',true).first();
5320 * trigger field - base class for combo..
5325 * @class Roo.bootstrap.TriggerField
5326 * @extends Roo.bootstrap.Input
5327 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5328 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5329 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5330 * for which you can provide a custom implementation. For example:
5332 var trigger = new Roo.bootstrap.TriggerField();
5333 trigger.onTriggerClick = myTriggerFn;
5334 trigger.applyTo('my-field');
5337 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5338 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5339 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5340 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5342 * Create a new TriggerField.
5343 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5344 * to the base TextField)
5346 Roo.bootstrap.TriggerField = function(config){
5347 this.mimicing = false;
5348 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5351 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5353 * @cfg {String} triggerClass A CSS class to apply to the trigger
5356 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5360 /** @cfg {Boolean} grow @hide */
5361 /** @cfg {Number} growMin @hide */
5362 /** @cfg {Number} growMax @hide */
5368 autoSize: Roo.emptyFn,
5375 actionMode : 'wrap',
5379 getAutoCreate : function(){
5381 var parent = this.parent();
5383 var align = this.parentLabelAlign();
5388 cls: 'form-group' //input-group
5395 type : this.inputType,
5396 cls : 'form-control',
5397 autocomplete: 'off',
5398 placeholder : this.placeholder || ''
5402 input.name = this.name;
5405 input.cls += ' input-' + this.size;
5408 if (this.disabled) {
5409 input.disabled=true;
5412 var inputblock = input;
5414 if (this.before || this.after) {
5417 cls : 'input-group',
5421 inputblock.cn.push({
5423 cls : 'input-group-addon',
5427 inputblock.cn.push(input);
5429 inputblock.cn.push({
5431 cls : 'input-group-addon',
5444 cls: 'form-hidden-field'
5452 Roo.log('multiple');
5460 cls: 'form-hidden-field'
5464 cls: 'select2-choices',
5468 cls: 'select2-search-field',
5481 cls: 'select2-container input-group',
5486 cls: 'typeahead typeahead-long dropdown-menu',
5487 style: 'display:none'
5495 cls : 'input-group-addon btn dropdown-toggle',
5503 cls: 'combobox-clear',
5517 combobox.cls += ' select2-container-multi';
5520 if (align ==='left' && this.fieldLabel.length) {
5522 Roo.log("left and has label");
5528 cls : 'control-label col-sm-' + this.labelWidth,
5529 html : this.fieldLabel
5533 cls : "col-sm-" + (12 - this.labelWidth),
5540 } else if ( this.fieldLabel.length) {
5546 //cls : 'input-group-addon',
5547 html : this.fieldLabel
5557 Roo.log(" no label && no align");
5564 ['xs','sm','md','lg'].map(function(size){
5565 if (settings[size]) {
5566 cfg.cls += ' col-' + size + '-' + settings[size];
5577 onResize : function(w, h){
5578 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5579 // if(typeof w == 'number'){
5580 // var x = w - this.trigger.getWidth();
5581 // this.inputEl().setWidth(this.adjustWidth('input', x));
5582 // this.trigger.setStyle('left', x+'px');
5587 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5590 getResizeEl : function(){
5591 return this.inputEl();
5595 getPositionEl : function(){
5596 return this.inputEl();
5600 alignErrorIcon : function(){
5601 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5605 initEvents : function(){
5607 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5608 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5610 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5611 if(this.hideTrigger){
5612 this.trigger.setDisplayed(false);
5614 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5618 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5621 //this.trigger.addClassOnOver('x-form-trigger-over');
5622 //this.trigger.addClassOnClick('x-form-trigger-click');
5625 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5630 initTrigger : function(){
5635 onDestroy : function(){
5637 this.trigger.removeAllListeners();
5638 // this.trigger.remove();
5641 // this.wrap.remove();
5643 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5647 onFocus : function(){
5648 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5651 this.wrap.addClass('x-trigger-wrap-focus');
5652 this.mimicing = true;
5653 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5654 if(this.monitorTab){
5655 this.el.on("keydown", this.checkTab, this);
5662 checkTab : function(e){
5663 if(e.getKey() == e.TAB){
5669 onBlur : function(){
5674 mimicBlur : function(e, t){
5676 if(!this.wrap.contains(t) && this.validateBlur()){
5683 triggerBlur : function(){
5684 this.mimicing = false;
5685 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5686 if(this.monitorTab){
5687 this.el.un("keydown", this.checkTab, this);
5689 //this.wrap.removeClass('x-trigger-wrap-focus');
5690 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5694 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5695 validateBlur : function(e, t){
5700 onDisable : function(){
5701 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5703 // this.wrap.addClass('x-item-disabled');
5708 onEnable : function(){
5709 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5711 // this.el.removeClass('x-item-disabled');
5716 onShow : function(){
5717 var ae = this.getActionEl();
5720 ae.dom.style.display = '';
5721 ae.dom.style.visibility = 'visible';
5727 onHide : function(){
5728 var ae = this.getActionEl();
5729 ae.dom.style.display = 'none';
5733 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5734 * by an implementing function.
5736 * @param {EventObject} e
5738 onTriggerClick : Roo.emptyFn
5742 * Ext JS Library 1.1.1
5743 * Copyright(c) 2006-2007, Ext JS, LLC.
5745 * Originally Released Under LGPL - original licence link has changed is not relivant.
5748 * <script type="text/javascript">
5753 * @class Roo.data.SortTypes
5755 * Defines the default sorting (casting?) comparison functions used when sorting data.
5757 Roo.data.SortTypes = {
5759 * Default sort that does nothing
5760 * @param {Mixed} s The value being converted
5761 * @return {Mixed} The comparison value
5768 * The regular expression used to strip tags
5772 stripTagsRE : /<\/?[^>]+>/gi,
5775 * Strips all HTML tags to sort on text only
5776 * @param {Mixed} s The value being converted
5777 * @return {String} The comparison value
5779 asText : function(s){
5780 return String(s).replace(this.stripTagsRE, "");
5784 * Strips all HTML tags to sort on text only - Case insensitive
5785 * @param {Mixed} s The value being converted
5786 * @return {String} The comparison value
5788 asUCText : function(s){
5789 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5793 * Case insensitive string
5794 * @param {Mixed} s The value being converted
5795 * @return {String} The comparison value
5797 asUCString : function(s) {
5798 return String(s).toUpperCase();
5803 * @param {Mixed} s The value being converted
5804 * @return {Number} The comparison value
5806 asDate : function(s) {
5810 if(s instanceof Date){
5813 return Date.parse(String(s));
5818 * @param {Mixed} s The value being converted
5819 * @return {Float} The comparison value
5821 asFloat : function(s) {
5822 var val = parseFloat(String(s).replace(/,/g, ""));
5823 if(isNaN(val)) val = 0;
5829 * @param {Mixed} s The value being converted
5830 * @return {Number} The comparison value
5832 asInt : function(s) {
5833 var val = parseInt(String(s).replace(/,/g, ""));
5834 if(isNaN(val)) val = 0;
5839 * Ext JS Library 1.1.1
5840 * Copyright(c) 2006-2007, Ext JS, LLC.
5842 * Originally Released Under LGPL - original licence link has changed is not relivant.
5845 * <script type="text/javascript">
5849 * @class Roo.data.Record
5850 * Instances of this class encapsulate both record <em>definition</em> information, and record
5851 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5852 * to access Records cached in an {@link Roo.data.Store} object.<br>
5854 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5855 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5858 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5860 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5861 * {@link #create}. The parameters are the same.
5862 * @param {Array} data An associative Array of data values keyed by the field name.
5863 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5864 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5865 * not specified an integer id is generated.
5867 Roo.data.Record = function(data, id){
5868 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5873 * Generate a constructor for a specific record layout.
5874 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5875 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5876 * Each field definition object may contain the following properties: <ul>
5877 * <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,
5878 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5879 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5880 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5881 * is being used, then this is a string containing the javascript expression to reference the data relative to
5882 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5883 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5884 * this may be omitted.</p></li>
5885 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5886 * <ul><li>auto (Default, implies no conversion)</li>
5891 * <li>date</li></ul></p></li>
5892 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5893 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5894 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5895 * by the Reader into an object that will be stored in the Record. It is passed the
5896 * following parameters:<ul>
5897 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5899 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5901 * <br>usage:<br><pre><code>
5902 var TopicRecord = Roo.data.Record.create(
5903 {name: 'title', mapping: 'topic_title'},
5904 {name: 'author', mapping: 'username'},
5905 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5906 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5907 {name: 'lastPoster', mapping: 'user2'},
5908 {name: 'excerpt', mapping: 'post_text'}
5911 var myNewRecord = new TopicRecord({
5912 title: 'Do my job please',
5915 lastPost: new Date(),
5916 lastPoster: 'Animal',
5917 excerpt: 'No way dude!'
5919 myStore.add(myNewRecord);
5924 Roo.data.Record.create = function(o){
5926 f.superclass.constructor.apply(this, arguments);
5928 Roo.extend(f, Roo.data.Record);
5929 var p = f.prototype;
5930 p.fields = new Roo.util.MixedCollection(false, function(field){
5933 for(var i = 0, len = o.length; i < len; i++){
5934 p.fields.add(new Roo.data.Field(o[i]));
5936 f.getField = function(name){
5937 return p.fields.get(name);
5942 Roo.data.Record.AUTO_ID = 1000;
5943 Roo.data.Record.EDIT = 'edit';
5944 Roo.data.Record.REJECT = 'reject';
5945 Roo.data.Record.COMMIT = 'commit';
5947 Roo.data.Record.prototype = {
5949 * Readonly flag - true if this record has been modified.
5958 join : function(store){
5963 * Set the named field to the specified value.
5964 * @param {String} name The name of the field to set.
5965 * @param {Object} value The value to set the field to.
5967 set : function(name, value){
5968 if(this.data[name] == value){
5975 if(typeof this.modified[name] == 'undefined'){
5976 this.modified[name] = this.data[name];
5978 this.data[name] = value;
5979 if(!this.editing && this.store){
5980 this.store.afterEdit(this);
5985 * Get the value of the named field.
5986 * @param {String} name The name of the field to get the value of.
5987 * @return {Object} The value of the field.
5989 get : function(name){
5990 return this.data[name];
5994 beginEdit : function(){
5995 this.editing = true;
6000 cancelEdit : function(){
6001 this.editing = false;
6002 delete this.modified;
6006 endEdit : function(){
6007 this.editing = false;
6008 if(this.dirty && this.store){
6009 this.store.afterEdit(this);
6014 * Usually called by the {@link Roo.data.Store} which owns the Record.
6015 * Rejects all changes made to the Record since either creation, or the last commit operation.
6016 * Modified fields are reverted to their original values.
6018 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6019 * of reject operations.
6021 reject : function(){
6022 var m = this.modified;
6024 if(typeof m[n] != "function"){
6025 this.data[n] = m[n];
6029 delete this.modified;
6030 this.editing = false;
6032 this.store.afterReject(this);
6037 * Usually called by the {@link Roo.data.Store} which owns the Record.
6038 * Commits all changes made to the Record since either creation, or the last commit operation.
6040 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6041 * of commit operations.
6043 commit : function(){
6045 delete this.modified;
6046 this.editing = false;
6048 this.store.afterCommit(this);
6053 hasError : function(){
6054 return this.error != null;
6058 clearError : function(){
6063 * Creates a copy of this record.
6064 * @param {String} id (optional) A new record id if you don't want to use this record's id
6067 copy : function(newId) {
6068 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6072 * Ext JS Library 1.1.1
6073 * Copyright(c) 2006-2007, Ext JS, LLC.
6075 * Originally Released Under LGPL - original licence link has changed is not relivant.
6078 * <script type="text/javascript">
6084 * @class Roo.data.Store
6085 * @extends Roo.util.Observable
6086 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6087 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6089 * 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
6090 * has no knowledge of the format of the data returned by the Proxy.<br>
6092 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6093 * instances from the data object. These records are cached and made available through accessor functions.
6095 * Creates a new Store.
6096 * @param {Object} config A config object containing the objects needed for the Store to access data,
6097 * and read the data into Records.
6099 Roo.data.Store = function(config){
6100 this.data = new Roo.util.MixedCollection(false);
6101 this.data.getKey = function(o){
6104 this.baseParams = {};
6111 "multisort" : "_multisort"
6114 if(config && config.data){
6115 this.inlineData = config.data;
6119 Roo.apply(this, config);
6121 if(this.reader){ // reader passed
6122 this.reader = Roo.factory(this.reader, Roo.data);
6123 this.reader.xmodule = this.xmodule || false;
6124 if(!this.recordType){
6125 this.recordType = this.reader.recordType;
6127 if(this.reader.onMetaChange){
6128 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6132 if(this.recordType){
6133 this.fields = this.recordType.prototype.fields;
6139 * @event datachanged
6140 * Fires when the data cache has changed, and a widget which is using this Store
6141 * as a Record cache should refresh its view.
6142 * @param {Store} this
6147 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6148 * @param {Store} this
6149 * @param {Object} meta The JSON metadata
6154 * Fires when Records have been added to the Store
6155 * @param {Store} this
6156 * @param {Roo.data.Record[]} records The array of Records added
6157 * @param {Number} index The index at which the record(s) were added
6162 * Fires when a Record has been removed from the Store
6163 * @param {Store} this
6164 * @param {Roo.data.Record} record The Record that was removed
6165 * @param {Number} index The index at which the record was removed
6170 * Fires when a Record has been updated
6171 * @param {Store} this
6172 * @param {Roo.data.Record} record The Record that was updated
6173 * @param {String} operation The update operation being performed. Value may be one of:
6175 Roo.data.Record.EDIT
6176 Roo.data.Record.REJECT
6177 Roo.data.Record.COMMIT
6183 * Fires when the data cache has been cleared.
6184 * @param {Store} this
6189 * Fires before a request is made for a new data object. If the beforeload handler returns false
6190 * the load action will be canceled.
6191 * @param {Store} this
6192 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6196 * @event beforeloadadd
6197 * Fires after a new set of Records has been loaded.
6198 * @param {Store} this
6199 * @param {Roo.data.Record[]} records The Records that were loaded
6200 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6202 beforeloadadd : true,
6205 * Fires after a new set of Records has been loaded, before they are added to the store.
6206 * @param {Store} this
6207 * @param {Roo.data.Record[]} records The Records that were loaded
6208 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6209 * @params {Object} return from reader
6213 * @event loadexception
6214 * Fires if an exception occurs in the Proxy during loading.
6215 * Called with the signature of the Proxy's "loadexception" event.
6216 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6219 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6220 * @param {Object} load options
6221 * @param {Object} jsonData from your request (normally this contains the Exception)
6223 loadexception : true
6227 this.proxy = Roo.factory(this.proxy, Roo.data);
6228 this.proxy.xmodule = this.xmodule || false;
6229 this.relayEvents(this.proxy, ["loadexception"]);
6231 this.sortToggle = {};
6232 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6234 Roo.data.Store.superclass.constructor.call(this);
6236 if(this.inlineData){
6237 this.loadData(this.inlineData);
6238 delete this.inlineData;
6242 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6244 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6245 * without a remote query - used by combo/forms at present.
6249 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6252 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6255 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6256 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6259 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6260 * on any HTTP request
6263 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6266 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6270 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6271 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6276 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6277 * loaded or when a record is removed. (defaults to false).
6279 pruneModifiedRecords : false,
6285 * Add Records to the Store and fires the add event.
6286 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6288 add : function(records){
6289 records = [].concat(records);
6290 for(var i = 0, len = records.length; i < len; i++){
6291 records[i].join(this);
6293 var index = this.data.length;
6294 this.data.addAll(records);
6295 this.fireEvent("add", this, records, index);
6299 * Remove a Record from the Store and fires the remove event.
6300 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6302 remove : function(record){
6303 var index = this.data.indexOf(record);
6304 this.data.removeAt(index);
6305 if(this.pruneModifiedRecords){
6306 this.modified.remove(record);
6308 this.fireEvent("remove", this, record, index);
6312 * Remove all Records from the Store and fires the clear event.
6314 removeAll : function(){
6316 if(this.pruneModifiedRecords){
6319 this.fireEvent("clear", this);
6323 * Inserts Records to the Store at the given index and fires the add event.
6324 * @param {Number} index The start index at which to insert the passed Records.
6325 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6327 insert : function(index, records){
6328 records = [].concat(records);
6329 for(var i = 0, len = records.length; i < len; i++){
6330 this.data.insert(index, records[i]);
6331 records[i].join(this);
6333 this.fireEvent("add", this, records, index);
6337 * Get the index within the cache of the passed Record.
6338 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6339 * @return {Number} The index of the passed Record. Returns -1 if not found.
6341 indexOf : function(record){
6342 return this.data.indexOf(record);
6346 * Get the index within the cache of the Record with the passed id.
6347 * @param {String} id The id of the Record to find.
6348 * @return {Number} The index of the Record. Returns -1 if not found.
6350 indexOfId : function(id){
6351 return this.data.indexOfKey(id);
6355 * Get the Record with the specified id.
6356 * @param {String} id The id of the Record to find.
6357 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6359 getById : function(id){
6360 return this.data.key(id);
6364 * Get the Record at the specified index.
6365 * @param {Number} index The index of the Record to find.
6366 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6368 getAt : function(index){
6369 return this.data.itemAt(index);
6373 * Returns a range of Records between specified indices.
6374 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6375 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6376 * @return {Roo.data.Record[]} An array of Records
6378 getRange : function(start, end){
6379 return this.data.getRange(start, end);
6383 storeOptions : function(o){
6384 o = Roo.apply({}, o);
6387 this.lastOptions = o;
6391 * Loads the Record cache from the configured Proxy using the configured Reader.
6393 * If using remote paging, then the first load call must specify the <em>start</em>
6394 * and <em>limit</em> properties in the options.params property to establish the initial
6395 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6397 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6398 * and this call will return before the new data has been loaded. Perform any post-processing
6399 * in a callback function, or in a "load" event handler.</strong>
6401 * @param {Object} options An object containing properties which control loading options:<ul>
6402 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6403 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6404 * passed the following arguments:<ul>
6405 * <li>r : Roo.data.Record[]</li>
6406 * <li>options: Options object from the load call</li>
6407 * <li>success: Boolean success indicator</li></ul></li>
6408 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6409 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6412 load : function(options){
6413 options = options || {};
6414 if(this.fireEvent("beforeload", this, options) !== false){
6415 this.storeOptions(options);
6416 var p = Roo.apply(options.params || {}, this.baseParams);
6417 // if meta was not loaded from remote source.. try requesting it.
6418 if (!this.reader.metaFromRemote) {
6421 if(this.sortInfo && this.remoteSort){
6422 var pn = this.paramNames;
6423 p[pn["sort"]] = this.sortInfo.field;
6424 p[pn["dir"]] = this.sortInfo.direction;
6426 if (this.multiSort) {
6427 var pn = this.paramNames;
6428 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6431 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6436 * Reloads the Record cache from the configured Proxy using the configured Reader and
6437 * the options from the last load operation performed.
6438 * @param {Object} options (optional) An object containing properties which may override the options
6439 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6440 * the most recently used options are reused).
6442 reload : function(options){
6443 this.load(Roo.applyIf(options||{}, this.lastOptions));
6447 // Called as a callback by the Reader during a load operation.
6448 loadRecords : function(o, options, success){
6449 if(!o || success === false){
6450 if(success !== false){
6451 this.fireEvent("load", this, [], options, o);
6453 if(options.callback){
6454 options.callback.call(options.scope || this, [], options, false);
6458 // if data returned failure - throw an exception.
6459 if (o.success === false) {
6460 // show a message if no listener is registered.
6461 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6462 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6464 // loadmask wil be hooked into this..
6465 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6468 var r = o.records, t = o.totalRecords || r.length;
6470 this.fireEvent("beforeloadadd", this, r, options, o);
6472 if(!options || options.add !== true){
6473 if(this.pruneModifiedRecords){
6476 for(var i = 0, len = r.length; i < len; i++){
6480 this.data = this.snapshot;
6481 delete this.snapshot;
6484 this.data.addAll(r);
6485 this.totalLength = t;
6487 this.fireEvent("datachanged", this);
6489 this.totalLength = Math.max(t, this.data.length+r.length);
6492 this.fireEvent("load", this, r, options, o);
6493 if(options.callback){
6494 options.callback.call(options.scope || this, r, options, true);
6500 * Loads data from a passed data block. A Reader which understands the format of the data
6501 * must have been configured in the constructor.
6502 * @param {Object} data The data block from which to read the Records. The format of the data expected
6503 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6504 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6506 loadData : function(o, append){
6507 var r = this.reader.readRecords(o);
6508 this.loadRecords(r, {add: append}, true);
6512 * Gets the number of cached records.
6514 * <em>If using paging, this may not be the total size of the dataset. If the data object
6515 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6516 * the data set size</em>
6518 getCount : function(){
6519 return this.data.length || 0;
6523 * Gets the total number of records in the dataset as returned by the server.
6525 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6526 * the dataset size</em>
6528 getTotalCount : function(){
6529 return this.totalLength || 0;
6533 * Returns the sort state of the Store as an object with two properties:
6535 field {String} The name of the field by which the Records are sorted
6536 direction {String} The sort order, "ASC" or "DESC"
6539 getSortState : function(){
6540 return this.sortInfo;
6544 applySort : function(){
6545 if(this.sortInfo && !this.remoteSort){
6546 var s = this.sortInfo, f = s.field;
6547 var st = this.fields.get(f).sortType;
6548 var fn = function(r1, r2){
6549 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6550 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6552 this.data.sort(s.direction, fn);
6553 if(this.snapshot && this.snapshot != this.data){
6554 this.snapshot.sort(s.direction, fn);
6560 * Sets the default sort column and order to be used by the next load operation.
6561 * @param {String} fieldName The name of the field to sort by.
6562 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6564 setDefaultSort : function(field, dir){
6565 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6570 * If remote sorting is used, the sort is performed on the server, and the cache is
6571 * reloaded. If local sorting is used, the cache is sorted internally.
6572 * @param {String} fieldName The name of the field to sort by.
6573 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6575 sort : function(fieldName, dir){
6576 var f = this.fields.get(fieldName);
6578 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6580 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6581 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6586 this.sortToggle[f.name] = dir;
6587 this.sortInfo = {field: f.name, direction: dir};
6588 if(!this.remoteSort){
6590 this.fireEvent("datachanged", this);
6592 this.load(this.lastOptions);
6597 * Calls the specified function for each of the Records in the cache.
6598 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6599 * Returning <em>false</em> aborts and exits the iteration.
6600 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6602 each : function(fn, scope){
6603 this.data.each(fn, scope);
6607 * Gets all records modified since the last commit. Modified records are persisted across load operations
6608 * (e.g., during paging).
6609 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6611 getModifiedRecords : function(){
6612 return this.modified;
6616 createFilterFn : function(property, value, anyMatch){
6617 if(!value.exec){ // not a regex
6618 value = String(value);
6619 if(value.length == 0){
6622 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6625 return value.test(r.data[property]);
6630 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6631 * @param {String} property A field on your records
6632 * @param {Number} start The record index to start at (defaults to 0)
6633 * @param {Number} end The last record index to include (defaults to length - 1)
6634 * @return {Number} The sum
6636 sum : function(property, start, end){
6637 var rs = this.data.items, v = 0;
6639 end = (end || end === 0) ? end : rs.length-1;
6641 for(var i = start; i <= end; i++){
6642 v += (rs[i].data[property] || 0);
6648 * Filter the records by a specified property.
6649 * @param {String} field A field on your records
6650 * @param {String/RegExp} value Either a string that the field
6651 * should start with or a RegExp to test against the field
6652 * @param {Boolean} anyMatch True to match any part not just the beginning
6654 filter : function(property, value, anyMatch){
6655 var fn = this.createFilterFn(property, value, anyMatch);
6656 return fn ? this.filterBy(fn) : this.clearFilter();
6660 * Filter by a function. The specified function will be called with each
6661 * record in this data source. If the function returns true the record is included,
6662 * otherwise it is filtered.
6663 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6664 * @param {Object} scope (optional) The scope of the function (defaults to this)
6666 filterBy : function(fn, scope){
6667 this.snapshot = this.snapshot || this.data;
6668 this.data = this.queryBy(fn, scope||this);
6669 this.fireEvent("datachanged", this);
6673 * Query the records by a specified property.
6674 * @param {String} field A field on your records
6675 * @param {String/RegExp} value Either a string that the field
6676 * should start with or a RegExp to test against the field
6677 * @param {Boolean} anyMatch True to match any part not just the beginning
6678 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6680 query : function(property, value, anyMatch){
6681 var fn = this.createFilterFn(property, value, anyMatch);
6682 return fn ? this.queryBy(fn) : this.data.clone();
6686 * Query by a function. The specified function will be called with each
6687 * record in this data source. If the function returns true the record is included
6689 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6690 * @param {Object} scope (optional) The scope of the function (defaults to this)
6691 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6693 queryBy : function(fn, scope){
6694 var data = this.snapshot || this.data;
6695 return data.filterBy(fn, scope||this);
6699 * Collects unique values for a particular dataIndex from this store.
6700 * @param {String} dataIndex The property to collect
6701 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6702 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6703 * @return {Array} An array of the unique values
6705 collect : function(dataIndex, allowNull, bypassFilter){
6706 var d = (bypassFilter === true && this.snapshot) ?
6707 this.snapshot.items : this.data.items;
6708 var v, sv, r = [], l = {};
6709 for(var i = 0, len = d.length; i < len; i++){
6710 v = d[i].data[dataIndex];
6712 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6721 * Revert to a view of the Record cache with no filtering applied.
6722 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6724 clearFilter : function(suppressEvent){
6725 if(this.snapshot && this.snapshot != this.data){
6726 this.data = this.snapshot;
6727 delete this.snapshot;
6728 if(suppressEvent !== true){
6729 this.fireEvent("datachanged", this);
6735 afterEdit : function(record){
6736 if(this.modified.indexOf(record) == -1){
6737 this.modified.push(record);
6739 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6743 afterReject : function(record){
6744 this.modified.remove(record);
6745 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6749 afterCommit : function(record){
6750 this.modified.remove(record);
6751 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6755 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6756 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6758 commitChanges : function(){
6759 var m = this.modified.slice(0);
6761 for(var i = 0, len = m.length; i < len; i++){
6767 * Cancel outstanding changes on all changed records.
6769 rejectChanges : function(){
6770 var m = this.modified.slice(0);
6772 for(var i = 0, len = m.length; i < len; i++){
6777 onMetaChange : function(meta, rtype, o){
6778 this.recordType = rtype;
6779 this.fields = rtype.prototype.fields;
6780 delete this.snapshot;
6781 this.sortInfo = meta.sortInfo || this.sortInfo;
6783 this.fireEvent('metachange', this, this.reader.meta);
6786 moveIndex : function(data, type)
6788 var index = this.indexOf(data);
6790 var newIndex = index + type;
6794 this.insert(newIndex, data);
6799 * Ext JS Library 1.1.1
6800 * Copyright(c) 2006-2007, Ext JS, LLC.
6802 * Originally Released Under LGPL - original licence link has changed is not relivant.
6805 * <script type="text/javascript">
6809 * @class Roo.data.SimpleStore
6810 * @extends Roo.data.Store
6811 * Small helper class to make creating Stores from Array data easier.
6812 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6813 * @cfg {Array} fields An array of field definition objects, or field name strings.
6814 * @cfg {Array} data The multi-dimensional array of data
6816 * @param {Object} config
6818 Roo.data.SimpleStore = function(config){
6819 Roo.data.SimpleStore.superclass.constructor.call(this, {
6821 reader: new Roo.data.ArrayReader({
6824 Roo.data.Record.create(config.fields)
6826 proxy : new Roo.data.MemoryProxy(config.data)
6830 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6832 * Ext JS Library 1.1.1
6833 * Copyright(c) 2006-2007, Ext JS, LLC.
6835 * Originally Released Under LGPL - original licence link has changed is not relivant.
6838 * <script type="text/javascript">
6843 * @extends Roo.data.Store
6844 * @class Roo.data.JsonStore
6845 * Small helper class to make creating Stores for JSON data easier. <br/>
6847 var store = new Roo.data.JsonStore({
6848 url: 'get-images.php',
6850 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6853 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6854 * JsonReader and HttpProxy (unless inline data is provided).</b>
6855 * @cfg {Array} fields An array of field definition objects, or field name strings.
6857 * @param {Object} config
6859 Roo.data.JsonStore = function(c){
6860 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6861 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6862 reader: new Roo.data.JsonReader(c, c.fields)
6865 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6867 * Ext JS Library 1.1.1
6868 * Copyright(c) 2006-2007, Ext JS, LLC.
6870 * Originally Released Under LGPL - original licence link has changed is not relivant.
6873 * <script type="text/javascript">
6877 Roo.data.Field = function(config){
6878 if(typeof config == "string"){
6879 config = {name: config};
6881 Roo.apply(this, config);
6887 var st = Roo.data.SortTypes;
6888 // named sortTypes are supported, here we look them up
6889 if(typeof this.sortType == "string"){
6890 this.sortType = st[this.sortType];
6893 // set default sortType for strings and dates
6897 this.sortType = st.asUCString;
6900 this.sortType = st.asDate;
6903 this.sortType = st.none;
6908 var stripRe = /[\$,%]/g;
6910 // prebuilt conversion function for this field, instead of
6911 // switching every time we're reading a value
6913 var cv, dateFormat = this.dateFormat;
6918 cv = function(v){ return v; };
6921 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6925 return v !== undefined && v !== null && v !== '' ?
6926 parseInt(String(v).replace(stripRe, ""), 10) : '';
6931 return v !== undefined && v !== null && v !== '' ?
6932 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6937 cv = function(v){ return v === true || v === "true" || v == 1; };
6944 if(v instanceof Date){
6948 if(dateFormat == "timestamp"){
6949 return new Date(v*1000);
6951 return Date.parseDate(v, dateFormat);
6953 var parsed = Date.parse(v);
6954 return parsed ? new Date(parsed) : null;
6963 Roo.data.Field.prototype = {
6971 * Ext JS Library 1.1.1
6972 * Copyright(c) 2006-2007, Ext JS, LLC.
6974 * Originally Released Under LGPL - original licence link has changed is not relivant.
6977 * <script type="text/javascript">
6980 // Base class for reading structured data from a data source. This class is intended to be
6981 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6984 * @class Roo.data.DataReader
6985 * Base class for reading structured data from a data source. This class is intended to be
6986 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6989 Roo.data.DataReader = function(meta, recordType){
6993 this.recordType = recordType instanceof Array ?
6994 Roo.data.Record.create(recordType) : recordType;
6997 Roo.data.DataReader.prototype = {
6999 * Create an empty record
7000 * @param {Object} data (optional) - overlay some values
7001 * @return {Roo.data.Record} record created.
7003 newRow : function(d) {
7005 this.recordType.prototype.fields.each(function(c) {
7007 case 'int' : da[c.name] = 0; break;
7008 case 'date' : da[c.name] = new Date(); break;
7009 case 'float' : da[c.name] = 0.0; break;
7010 case 'boolean' : da[c.name] = false; break;
7011 default : da[c.name] = ""; break;
7015 return new this.recordType(Roo.apply(da, d));
7020 * Ext JS Library 1.1.1
7021 * Copyright(c) 2006-2007, Ext JS, LLC.
7023 * Originally Released Under LGPL - original licence link has changed is not relivant.
7026 * <script type="text/javascript">
7030 * @class Roo.data.DataProxy
7031 * @extends Roo.data.Observable
7032 * This class is an abstract base class for implementations which provide retrieval of
7033 * unformatted data objects.<br>
7035 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7036 * (of the appropriate type which knows how to parse the data object) to provide a block of
7037 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7039 * Custom implementations must implement the load method as described in
7040 * {@link Roo.data.HttpProxy#load}.
7042 Roo.data.DataProxy = function(){
7046 * Fires before a network request is made to retrieve a data object.
7047 * @param {Object} This DataProxy object.
7048 * @param {Object} params The params parameter to the load function.
7053 * Fires before the load method's callback is called.
7054 * @param {Object} This DataProxy object.
7055 * @param {Object} o The data object.
7056 * @param {Object} arg The callback argument object passed to the load function.
7060 * @event loadexception
7061 * Fires if an Exception occurs during data retrieval.
7062 * @param {Object} This DataProxy object.
7063 * @param {Object} o The data object.
7064 * @param {Object} arg The callback argument object passed to the load function.
7065 * @param {Object} e The Exception.
7067 loadexception : true
7069 Roo.data.DataProxy.superclass.constructor.call(this);
7072 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7075 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7079 * Ext JS Library 1.1.1
7080 * Copyright(c) 2006-2007, Ext JS, LLC.
7082 * Originally Released Under LGPL - original licence link has changed is not relivant.
7085 * <script type="text/javascript">
7088 * @class Roo.data.MemoryProxy
7089 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7090 * to the Reader when its load method is called.
7092 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7094 Roo.data.MemoryProxy = function(data){
7098 Roo.data.MemoryProxy.superclass.constructor.call(this);
7102 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7104 * Load data from the requested source (in this case an in-memory
7105 * data object passed to the constructor), read the data object into
7106 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7107 * process that block using the passed callback.
7108 * @param {Object} params This parameter is not used by the MemoryProxy class.
7109 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7110 * object into a block of Roo.data.Records.
7111 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7112 * The function must be passed <ul>
7113 * <li>The Record block object</li>
7114 * <li>The "arg" argument from the load function</li>
7115 * <li>A boolean success indicator</li>
7117 * @param {Object} scope The scope in which to call the callback
7118 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7120 load : function(params, reader, callback, scope, arg){
7121 params = params || {};
7124 result = reader.readRecords(this.data);
7126 this.fireEvent("loadexception", this, arg, null, e);
7127 callback.call(scope, null, arg, false);
7130 callback.call(scope, result, arg, true);
7134 update : function(params, records){
7139 * Ext JS Library 1.1.1
7140 * Copyright(c) 2006-2007, Ext JS, LLC.
7142 * Originally Released Under LGPL - original licence link has changed is not relivant.
7145 * <script type="text/javascript">
7148 * @class Roo.data.HttpProxy
7149 * @extends Roo.data.DataProxy
7150 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7151 * configured to reference a certain URL.<br><br>
7153 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7154 * from which the running page was served.<br><br>
7156 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7158 * Be aware that to enable the browser to parse an XML document, the server must set
7159 * the Content-Type header in the HTTP response to "text/xml".
7161 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7162 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7163 * will be used to make the request.
7165 Roo.data.HttpProxy = function(conn){
7166 Roo.data.HttpProxy.superclass.constructor.call(this);
7167 // is conn a conn config or a real conn?
7169 this.useAjax = !conn || !conn.events;
7173 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7174 // thse are take from connection...
7177 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7180 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7181 * extra parameters to each request made by this object. (defaults to undefined)
7184 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7185 * to each request made by this object. (defaults to undefined)
7188 * @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)
7191 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7194 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7200 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7204 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7205 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7206 * a finer-grained basis than the DataProxy events.
7208 getConnection : function(){
7209 return this.useAjax ? Roo.Ajax : this.conn;
7213 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7214 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7215 * process that block using the passed callback.
7216 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7217 * for the request to the remote server.
7218 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7219 * object into a block of Roo.data.Records.
7220 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7221 * The function must be passed <ul>
7222 * <li>The Record block object</li>
7223 * <li>The "arg" argument from the load function</li>
7224 * <li>A boolean success indicator</li>
7226 * @param {Object} scope The scope in which to call the callback
7227 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7229 load : function(params, reader, callback, scope, arg){
7230 if(this.fireEvent("beforeload", this, params) !== false){
7232 params : params || {},
7234 callback : callback,
7239 callback : this.loadResponse,
7243 Roo.applyIf(o, this.conn);
7244 if(this.activeRequest){
7245 Roo.Ajax.abort(this.activeRequest);
7247 this.activeRequest = Roo.Ajax.request(o);
7249 this.conn.request(o);
7252 callback.call(scope||this, null, arg, false);
7257 loadResponse : function(o, success, response){
7258 delete this.activeRequest;
7260 this.fireEvent("loadexception", this, o, response);
7261 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7266 result = o.reader.read(response);
7268 this.fireEvent("loadexception", this, o, response, e);
7269 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7273 this.fireEvent("load", this, o, o.request.arg);
7274 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7278 update : function(dataSet){
7283 updateResponse : function(dataSet){
7288 * Ext JS Library 1.1.1
7289 * Copyright(c) 2006-2007, Ext JS, LLC.
7291 * Originally Released Under LGPL - original licence link has changed is not relivant.
7294 * <script type="text/javascript">
7298 * @class Roo.data.ScriptTagProxy
7299 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7300 * other than the originating domain of the running page.<br><br>
7302 * <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
7303 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7305 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7306 * source code that is used as the source inside a <script> tag.<br><br>
7308 * In order for the browser to process the returned data, the server must wrap the data object
7309 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7310 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7311 * depending on whether the callback name was passed:
7314 boolean scriptTag = false;
7315 String cb = request.getParameter("callback");
7318 response.setContentType("text/javascript");
7320 response.setContentType("application/x-json");
7322 Writer out = response.getWriter();
7324 out.write(cb + "(");
7326 out.print(dataBlock.toJsonString());
7333 * @param {Object} config A configuration object.
7335 Roo.data.ScriptTagProxy = function(config){
7336 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7337 Roo.apply(this, config);
7338 this.head = document.getElementsByTagName("head")[0];
7341 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7343 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7345 * @cfg {String} url The URL from which to request the data object.
7348 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7352 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7353 * the server the name of the callback function set up by the load call to process the returned data object.
7354 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7355 * javascript output which calls this named function passing the data object as its only parameter.
7357 callbackParam : "callback",
7359 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7360 * name to the request.
7365 * Load data from the configured URL, read the data object into
7366 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7367 * process that block using the passed callback.
7368 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7369 * for the request to the remote server.
7370 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7371 * object into a block of Roo.data.Records.
7372 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7373 * The function must be passed <ul>
7374 * <li>The Record block object</li>
7375 * <li>The "arg" argument from the load function</li>
7376 * <li>A boolean success indicator</li>
7378 * @param {Object} scope The scope in which to call the callback
7379 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7381 load : function(params, reader, callback, scope, arg){
7382 if(this.fireEvent("beforeload", this, params) !== false){
7384 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7387 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7389 url += "&_dc=" + (new Date().getTime());
7391 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7394 cb : "stcCallback"+transId,
7395 scriptId : "stcScript"+transId,
7399 callback : callback,
7405 window[trans.cb] = function(o){
7406 conn.handleResponse(o, trans);
7409 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7411 if(this.autoAbort !== false){
7415 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7417 var script = document.createElement("script");
7418 script.setAttribute("src", url);
7419 script.setAttribute("type", "text/javascript");
7420 script.setAttribute("id", trans.scriptId);
7421 this.head.appendChild(script);
7425 callback.call(scope||this, null, arg, false);
7430 isLoading : function(){
7431 return this.trans ? true : false;
7435 * Abort the current server request.
7438 if(this.isLoading()){
7439 this.destroyTrans(this.trans);
7444 destroyTrans : function(trans, isLoaded){
7445 this.head.removeChild(document.getElementById(trans.scriptId));
7446 clearTimeout(trans.timeoutId);
7448 window[trans.cb] = undefined;
7450 delete window[trans.cb];
7453 // if hasn't been loaded, wait for load to remove it to prevent script error
7454 window[trans.cb] = function(){
7455 window[trans.cb] = undefined;
7457 delete window[trans.cb];
7464 handleResponse : function(o, trans){
7466 this.destroyTrans(trans, true);
7469 result = trans.reader.readRecords(o);
7471 this.fireEvent("loadexception", this, o, trans.arg, e);
7472 trans.callback.call(trans.scope||window, null, trans.arg, false);
7475 this.fireEvent("load", this, o, trans.arg);
7476 trans.callback.call(trans.scope||window, result, trans.arg, true);
7480 handleFailure : function(trans){
7482 this.destroyTrans(trans, false);
7483 this.fireEvent("loadexception", this, null, trans.arg);
7484 trans.callback.call(trans.scope||window, null, trans.arg, false);
7488 * Ext JS Library 1.1.1
7489 * Copyright(c) 2006-2007, Ext JS, LLC.
7491 * Originally Released Under LGPL - original licence link has changed is not relivant.
7494 * <script type="text/javascript">
7498 * @class Roo.data.JsonReader
7499 * @extends Roo.data.DataReader
7500 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7501 * based on mappings in a provided Roo.data.Record constructor.
7503 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7504 * in the reply previously.
7509 var RecordDef = Roo.data.Record.create([
7510 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7511 {name: 'occupation'} // This field will use "occupation" as the mapping.
7513 var myReader = new Roo.data.JsonReader({
7514 totalProperty: "results", // The property which contains the total dataset size (optional)
7515 root: "rows", // The property which contains an Array of row objects
7516 id: "id" // The property within each row object that provides an ID for the record (optional)
7520 * This would consume a JSON file like this:
7522 { 'results': 2, 'rows': [
7523 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7524 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7527 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7528 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7529 * paged from the remote server.
7530 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7531 * @cfg {String} root name of the property which contains the Array of row objects.
7532 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7534 * Create a new JsonReader
7535 * @param {Object} meta Metadata configuration options
7536 * @param {Object} recordType Either an Array of field definition objects,
7537 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7539 Roo.data.JsonReader = function(meta, recordType){
7542 // set some defaults:
7544 totalProperty: 'total',
7545 successProperty : 'success',
7550 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7552 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7555 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7556 * Used by Store query builder to append _requestMeta to params.
7559 metaFromRemote : false,
7561 * This method is only used by a DataProxy which has retrieved data from a remote server.
7562 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7563 * @return {Object} data A data block which is used by an Roo.data.Store object as
7564 * a cache of Roo.data.Records.
7566 read : function(response){
7567 var json = response.responseText;
7569 var o = /* eval:var:o */ eval("("+json+")");
7571 throw {message: "JsonReader.read: Json object not found"};
7577 this.metaFromRemote = true;
7578 this.meta = o.metaData;
7579 this.recordType = Roo.data.Record.create(o.metaData.fields);
7580 this.onMetaChange(this.meta, this.recordType, o);
7582 return this.readRecords(o);
7585 // private function a store will implement
7586 onMetaChange : function(meta, recordType, o){
7593 simpleAccess: function(obj, subsc) {
7600 getJsonAccessor: function(){
7602 return function(expr) {
7604 return(re.test(expr))
7605 ? new Function("obj", "return obj." + expr)
7615 * Create a data block containing Roo.data.Records from an XML document.
7616 * @param {Object} o An object which contains an Array of row objects in the property specified
7617 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7618 * which contains the total size of the dataset.
7619 * @return {Object} data A data block which is used by an Roo.data.Store object as
7620 * a cache of Roo.data.Records.
7622 readRecords : function(o){
7624 * After any data loads, the raw JSON data is available for further custom processing.
7628 var s = this.meta, Record = this.recordType,
7629 f = Record.prototype.fields, fi = f.items, fl = f.length;
7631 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7633 if(s.totalProperty) {
7634 this.getTotal = this.getJsonAccessor(s.totalProperty);
7636 if(s.successProperty) {
7637 this.getSuccess = this.getJsonAccessor(s.successProperty);
7639 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7641 var g = this.getJsonAccessor(s.id);
7642 this.getId = function(rec) {
7644 return (r === undefined || r === "") ? null : r;
7647 this.getId = function(){return null;};
7650 for(var jj = 0; jj < fl; jj++){
7652 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7653 this.ef[jj] = this.getJsonAccessor(map);
7657 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7658 if(s.totalProperty){
7659 var vt = parseInt(this.getTotal(o), 10);
7664 if(s.successProperty){
7665 var vs = this.getSuccess(o);
7666 if(vs === false || vs === 'false'){
7671 for(var i = 0; i < c; i++){
7674 var id = this.getId(n);
7675 for(var j = 0; j < fl; j++){
7677 var v = this.ef[j](n);
7679 Roo.log('missing convert for ' + f.name);
7683 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7685 var record = new Record(values, id);
7687 records[i] = record;
7693 totalRecords : totalRecords
7698 * Ext JS Library 1.1.1
7699 * Copyright(c) 2006-2007, Ext JS, LLC.
7701 * Originally Released Under LGPL - original licence link has changed is not relivant.
7704 * <script type="text/javascript">
7708 * @class Roo.data.ArrayReader
7709 * @extends Roo.data.DataReader
7710 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7711 * Each element of that Array represents a row of data fields. The
7712 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7713 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7717 var RecordDef = Roo.data.Record.create([
7718 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7719 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7721 var myReader = new Roo.data.ArrayReader({
7722 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7726 * This would consume an Array like this:
7728 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7730 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7732 * Create a new JsonReader
7733 * @param {Object} meta Metadata configuration options.
7734 * @param {Object} recordType Either an Array of field definition objects
7735 * as specified to {@link Roo.data.Record#create},
7736 * or an {@link Roo.data.Record} object
7737 * created using {@link Roo.data.Record#create}.
7739 Roo.data.ArrayReader = function(meta, recordType){
7740 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7743 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7745 * Create a data block containing Roo.data.Records from an XML document.
7746 * @param {Object} o An Array of row objects which represents the dataset.
7747 * @return {Object} data A data block which is used by an Roo.data.Store object as
7748 * a cache of Roo.data.Records.
7750 readRecords : function(o){
7751 var sid = this.meta ? this.meta.id : null;
7752 var recordType = this.recordType, fields = recordType.prototype.fields;
7755 for(var i = 0; i < root.length; i++){
7758 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7759 for(var j = 0, jlen = fields.length; j < jlen; j++){
7760 var f = fields.items[j];
7761 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7762 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7766 var record = new recordType(values, id);
7768 records[records.length] = record;
7772 totalRecords : records.length
7781 * @class Roo.bootstrap.ComboBox
7782 * @extends Roo.bootstrap.TriggerField
7783 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7784 * @cfg {Boolean} append (true|false) default false
7786 * Create a new ComboBox.
7787 * @param {Object} config Configuration options
7789 Roo.bootstrap.ComboBox = function(config){
7790 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7794 * Fires when the dropdown list is expanded
7795 * @param {Roo.bootstrap.ComboBox} combo This combo box
7800 * Fires when the dropdown list is collapsed
7801 * @param {Roo.bootstrap.ComboBox} combo This combo box
7805 * @event beforeselect
7806 * Fires before a list item is selected. Return false to cancel the selection.
7807 * @param {Roo.bootstrap.ComboBox} combo This combo box
7808 * @param {Roo.data.Record} record The data record returned from the underlying store
7809 * @param {Number} index The index of the selected item in the dropdown list
7811 'beforeselect' : true,
7814 * Fires when a list item is selected
7815 * @param {Roo.bootstrap.ComboBox} combo This combo box
7816 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7817 * @param {Number} index The index of the selected item in the dropdown list
7821 * @event beforequery
7822 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7823 * The event object passed has these properties:
7824 * @param {Roo.bootstrap.ComboBox} combo This combo box
7825 * @param {String} query The query
7826 * @param {Boolean} forceAll true to force "all" query
7827 * @param {Boolean} cancel true to cancel the query
7828 * @param {Object} e The query event object
7830 'beforequery': true,
7833 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7834 * @param {Roo.bootstrap.ComboBox} combo This combo box
7839 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7840 * @param {Roo.bootstrap.ComboBox} combo This combo box
7841 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7846 * Fires when the remove value from the combobox array
7847 * @param {Roo.bootstrap.ComboBox} combo This combo box
7854 this.selectedIndex = -1;
7855 if(this.mode == 'local'){
7856 if(config.queryDelay === undefined){
7857 this.queryDelay = 10;
7859 if(config.minChars === undefined){
7865 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7868 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7869 * rendering into an Roo.Editor, defaults to false)
7872 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7873 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7876 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7879 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7880 * the dropdown list (defaults to undefined, with no header element)
7884 * @cfg {String/Roo.Template} tpl The template to use to render the output
7888 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7890 listWidth: undefined,
7892 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7893 * mode = 'remote' or 'text' if mode = 'local')
7895 displayField: undefined,
7897 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7898 * mode = 'remote' or 'value' if mode = 'local').
7899 * Note: use of a valueField requires the user make a selection
7900 * in order for a value to be mapped.
7902 valueField: undefined,
7906 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7907 * field's data value (defaults to the underlying DOM element's name)
7909 hiddenName: undefined,
7911 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7915 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7917 selectedClass: 'active',
7920 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7924 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7925 * anchor positions (defaults to 'tl-bl')
7927 listAlign: 'tl-bl?',
7929 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7933 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7934 * query specified by the allQuery config option (defaults to 'query')
7936 triggerAction: 'query',
7938 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7939 * (defaults to 4, does not apply if editable = false)
7943 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7944 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7948 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7949 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7953 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7954 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7958 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7959 * when editable = true (defaults to false)
7961 selectOnFocus:false,
7963 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7965 queryParam: 'query',
7967 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7968 * when mode = 'remote' (defaults to 'Loading...')
7970 loadingText: 'Loading...',
7972 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7976 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7980 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7981 * traditional select (defaults to true)
7985 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7989 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7993 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7994 * listWidth has a higher value)
7998 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7999 * allow the user to set arbitrary text into the field (defaults to false)
8001 forceSelection:false,
8003 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8004 * if typeAhead = true (defaults to 250)
8006 typeAheadDelay : 250,
8008 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8009 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8011 valueNotFoundText : undefined,
8013 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8018 * @cfg {Boolean} disableClear Disable showing of clear button.
8020 disableClear : false,
8022 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
8024 alwaysQuery : false,
8027 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8041 // element that contains real text value.. (when hidden is used..)
8044 initEvents: function(){
8047 throw "can not find store for combo";
8049 this.store = Roo.factory(this.store, Roo.data);
8053 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8056 if(this.hiddenName){
8058 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8060 this.hiddenField.dom.value =
8061 this.hiddenValue !== undefined ? this.hiddenValue :
8062 this.value !== undefined ? this.value : '';
8064 // prevent input submission
8065 this.el.dom.removeAttribute('name');
8066 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8071 // this.el.dom.setAttribute('autocomplete', 'off');
8074 var cls = 'x-combo-list';
8075 this.list = this.el.select('ul.dropdown-menu',true).first();
8077 //this.list = new Roo.Layer({
8078 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8081 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8082 this.list.setWidth(lw);
8084 this.list.on('mouseover', this.onViewOver, this);
8085 this.list.on('mousemove', this.onViewMove, this);
8087 this.list.on('scroll', this.onViewScroll, this);
8090 this.list.swallowEvent('mousewheel');
8091 this.assetHeight = 0;
8094 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8095 this.assetHeight += this.header.getHeight();
8098 this.innerList = this.list.createChild({cls:cls+'-inner'});
8099 this.innerList.on('mouseover', this.onViewOver, this);
8100 this.innerList.on('mousemove', this.onViewMove, this);
8101 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8103 if(this.allowBlank && !this.pageSize && !this.disableClear){
8104 this.footer = this.list.createChild({cls:cls+'-ft'});
8105 this.pageTb = new Roo.Toolbar(this.footer);
8109 this.footer = this.list.createChild({cls:cls+'-ft'});
8110 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8111 {pageSize: this.pageSize});
8115 if (this.pageTb && this.allowBlank && !this.disableClear) {
8117 this.pageTb.add(new Roo.Toolbar.Fill(), {
8118 cls: 'x-btn-icon x-btn-clear',
8124 _this.onSelect(false, -1);
8129 this.assetHeight += this.footer.getHeight();
8134 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8137 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8138 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8140 //this.view.wrapEl.setDisplayed(false);
8141 this.view.on('click', this.onViewClick, this);
8145 this.store.on('beforeload', this.onBeforeLoad, this);
8146 this.store.on('load', this.onLoad, this);
8147 this.store.on('loadexception', this.onLoadException, this);
8150 this.resizer = new Roo.Resizable(this.list, {
8151 pinned:true, handles:'se'
8153 this.resizer.on('resize', function(r, w, h){
8154 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8156 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8157 this.restrictHeight();
8159 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8163 this.editable = true;
8164 this.setEditable(false);
8169 if (typeof(this.events.add.listeners) != 'undefined') {
8171 this.addicon = this.wrap.createChild(
8172 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8174 this.addicon.on('click', function(e) {
8175 this.fireEvent('add', this);
8178 if (typeof(this.events.edit.listeners) != 'undefined') {
8180 this.editicon = this.wrap.createChild(
8181 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8183 this.editicon.setStyle('margin-left', '40px');
8185 this.editicon.on('click', function(e) {
8187 // we fire even if inothing is selected..
8188 this.fireEvent('edit', this, this.lastData );
8194 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8196 this.inKeyMode = true;
8200 "down" : function(e){
8201 if(!this.isExpanded()){
8202 this.onTriggerClick();
8204 this.inKeyMode = true;
8209 "enter" : function(e){
8214 "esc" : function(e){
8218 "tab" : function(e){
8221 if(this.fireEvent("specialkey", this, e)){
8222 this.onViewClick(false);
8230 doRelay : function(foo, bar, hname){
8231 if(hname == 'down' || this.scope.isExpanded()){
8232 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8241 this.queryDelay = Math.max(this.queryDelay || 10,
8242 this.mode == 'local' ? 10 : 250);
8245 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8248 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8250 if(this.editable !== false){
8251 this.inputEl().on("keyup", this.onKeyUp, this);
8253 if(this.forceSelection){
8254 this.on('blur', this.doForce, this);
8258 this.choices = this.el.select('ul.select2-choices', true).first();
8259 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8263 onDestroy : function(){
8265 this.view.setStore(null);
8266 this.view.el.removeAllListeners();
8267 this.view.el.remove();
8268 this.view.purgeListeners();
8271 this.list.dom.innerHTML = '';
8274 this.store.un('beforeload', this.onBeforeLoad, this);
8275 this.store.un('load', this.onLoad, this);
8276 this.store.un('loadexception', this.onLoadException, this);
8278 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8282 fireKey : function(e){
8283 if(e.isNavKeyPress() && !this.list.isVisible()){
8284 this.fireEvent("specialkey", this, e);
8289 onResize: function(w, h){
8290 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8292 // if(typeof w != 'number'){
8293 // // we do not handle it!?!?
8296 // var tw = this.trigger.getWidth();
8297 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8298 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8300 // this.inputEl().setWidth( this.adjustWidth('input', x));
8302 // //this.trigger.setStyle('left', x+'px');
8304 // if(this.list && this.listWidth === undefined){
8305 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8306 // this.list.setWidth(lw);
8307 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8315 * Allow or prevent the user from directly editing the field text. If false is passed,
8316 * the user will only be able to select from the items defined in the dropdown list. This method
8317 * is the runtime equivalent of setting the 'editable' config option at config time.
8318 * @param {Boolean} value True to allow the user to directly edit the field text
8320 setEditable : function(value){
8321 if(value == this.editable){
8324 this.editable = value;
8326 this.inputEl().dom.setAttribute('readOnly', true);
8327 this.inputEl().on('mousedown', this.onTriggerClick, this);
8328 this.inputEl().addClass('x-combo-noedit');
8330 this.inputEl().dom.setAttribute('readOnly', false);
8331 this.inputEl().un('mousedown', this.onTriggerClick, this);
8332 this.inputEl().removeClass('x-combo-noedit');
8338 onBeforeLoad : function(combo,opts){
8343 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8345 this.restrictHeight();
8346 this.selectedIndex = -1;
8350 onLoad : function(){
8352 this.hasQuery = false;
8358 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8359 this.loading.hide();
8362 if(this.store.getCount() > 0){
8364 this.restrictHeight();
8365 if(this.lastQuery == this.allQuery){
8367 this.inputEl().dom.select();
8369 if(!this.selectByValue(this.value, true)){
8370 this.select(0, true);
8374 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8375 this.taTask.delay(this.typeAheadDelay);
8379 this.onEmptyResults();
8385 onLoadException : function()
8387 this.hasQuery = false;
8389 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8390 this.loading.hide();
8394 Roo.log(this.store.reader.jsonData);
8395 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8397 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8403 onTypeAhead : function(){
8404 if(this.store.getCount() > 0){
8405 var r = this.store.getAt(0);
8406 var newValue = r.data[this.displayField];
8407 var len = newValue.length;
8408 var selStart = this.getRawValue().length;
8410 if(selStart != len){
8411 this.setRawValue(newValue);
8412 this.selectText(selStart, newValue.length);
8418 onSelect : function(record, index){
8420 if(this.fireEvent('beforeselect', this, record, index) !== false){
8422 this.setFromData(index > -1 ? record.data : false);
8425 this.fireEvent('select', this, record, index);
8430 * Returns the currently selected field value or empty string if no value is set.
8431 * @return {String} value The selected value
8433 getValue : function(){
8436 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8439 if(this.valueField){
8440 return typeof this.value != 'undefined' ? this.value : '';
8442 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8447 * Clears any text/value currently set in the field
8449 clearValue : function(){
8450 if(this.hiddenField){
8451 this.hiddenField.dom.value = '';
8454 this.setRawValue('');
8455 this.lastSelectionText = '';
8460 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8461 * will be displayed in the field. If the value does not match the data value of an existing item,
8462 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8463 * Otherwise the field will be blank (although the value will still be set).
8464 * @param {String} value The value to match
8466 setValue : function(v){
8473 if(this.valueField){
8474 var r = this.findRecord(this.valueField, v);
8476 text = r.data[this.displayField];
8477 }else if(this.valueNotFoundText !== undefined){
8478 text = this.valueNotFoundText;
8481 this.lastSelectionText = text;
8482 if(this.hiddenField){
8483 this.hiddenField.dom.value = v;
8485 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8489 * @property {Object} the last set data for the element
8494 * Sets the value of the field based on a object which is related to the record format for the store.
8495 * @param {Object} value the value to set as. or false on reset?
8497 setFromData : function(o){
8504 var dv = ''; // display value
8505 var vv = ''; // value value..
8507 if (this.displayField) {
8508 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8510 // this is an error condition!!!
8511 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8514 if(this.valueField){
8515 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8518 if(this.hiddenField){
8519 this.hiddenField.dom.value = vv;
8521 this.lastSelectionText = dv;
8522 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8526 // no hidden field.. - we store the value in 'value', but still display
8527 // display field!!!!
8528 this.lastSelectionText = dv;
8529 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8536 // overridden so that last data is reset..
8537 this.setValue(this.originalValue);
8538 this.clearInvalid();
8539 this.lastData = false;
8541 this.view.clearSelections();
8545 findRecord : function(prop, value){
8547 if(this.store.getCount() > 0){
8548 this.store.each(function(r){
8549 if(r.data[prop] == value){
8561 // returns hidden if it's set..
8562 if (!this.rendered) {return ''};
8563 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8567 onViewMove : function(e, t){
8568 this.inKeyMode = false;
8572 onViewOver : function(e, t){
8573 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8576 var item = this.view.findItemFromChild(t);
8578 var index = this.view.indexOf(item);
8579 this.select(index, false);
8584 onViewClick : function(doFocus)
8586 var index = this.view.getSelectedIndexes()[0];
8587 var r = this.store.getAt(index);
8589 this.onSelect(r, index);
8591 if(doFocus !== false && !this.blockFocus){
8592 this.inputEl().focus();
8597 restrictHeight : function(){
8598 //this.innerList.dom.style.height = '';
8599 //var inner = this.innerList.dom;
8600 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8601 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8602 //this.list.beginUpdate();
8603 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8604 this.list.alignTo(this.inputEl(), this.listAlign);
8605 //this.list.endUpdate();
8609 onEmptyResults : function(){
8614 * Returns true if the dropdown list is expanded, else false.
8616 isExpanded : function(){
8617 return this.list.isVisible();
8621 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8622 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8623 * @param {String} value The data value of the item to select
8624 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8625 * selected item if it is not currently in view (defaults to true)
8626 * @return {Boolean} True if the value matched an item in the list, else false
8628 selectByValue : function(v, scrollIntoView){
8629 if(v !== undefined && v !== null){
8630 var r = this.findRecord(this.valueField || this.displayField, v);
8632 this.select(this.store.indexOf(r), scrollIntoView);
8640 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8641 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8642 * @param {Number} index The zero-based index of the list item to select
8643 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8644 * selected item if it is not currently in view (defaults to true)
8646 select : function(index, scrollIntoView){
8647 this.selectedIndex = index;
8648 this.view.select(index);
8649 if(scrollIntoView !== false){
8650 var el = this.view.getNode(index);
8652 //this.innerList.scrollChildIntoView(el, false);
8659 selectNext : function(){
8660 var ct = this.store.getCount();
8662 if(this.selectedIndex == -1){
8664 }else if(this.selectedIndex < ct-1){
8665 this.select(this.selectedIndex+1);
8671 selectPrev : function(){
8672 var ct = this.store.getCount();
8674 if(this.selectedIndex == -1){
8676 }else if(this.selectedIndex != 0){
8677 this.select(this.selectedIndex-1);
8683 onKeyUp : function(e){
8684 if(this.editable !== false && !e.isSpecialKey()){
8685 this.lastKey = e.getKey();
8686 this.dqTask.delay(this.queryDelay);
8691 validateBlur : function(){
8692 return !this.list || !this.list.isVisible();
8696 initQuery : function(){
8697 this.doQuery(this.getRawValue());
8701 doForce : function(){
8702 if(this.el.dom.value.length > 0){
8704 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8710 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8711 * query allowing the query action to be canceled if needed.
8712 * @param {String} query The SQL query to execute
8713 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8714 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8715 * saved in the current store (defaults to false)
8717 doQuery : function(q, forceAll){
8719 if(q === undefined || q === null){
8728 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8733 forceAll = qe.forceAll;
8734 if(forceAll === true || (q.length >= this.minChars)){
8736 this.hasQuery = true;
8738 if(this.lastQuery != q || this.alwaysQuery){
8740 if(this.mode == 'local'){
8741 this.selectedIndex = -1;
8743 this.store.clearFilter();
8745 this.store.filter(this.displayField, q);
8749 this.store.baseParams[this.queryParam] = q;
8751 var options = {params : this.getParams(q)};
8755 options.params.start = this.page * this.pageSize;
8758 this.store.load(options);
8762 this.selectedIndex = -1;
8767 this.loadNext = false;
8771 getParams : function(q){
8773 //p[this.queryParam] = q;
8777 p.limit = this.pageSize;
8783 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8785 collapse : function(){
8786 if(!this.isExpanded()){
8791 Roo.get(document).un('mousedown', this.collapseIf, this);
8792 Roo.get(document).un('mousewheel', this.collapseIf, this);
8793 if (!this.editable) {
8794 Roo.get(document).un('keydown', this.listKeyPress, this);
8796 this.fireEvent('collapse', this);
8800 collapseIf : function(e){
8801 var in_combo = e.within(this.el);
8802 var in_list = e.within(this.list);
8804 if (in_combo || in_list) {
8805 //e.stopPropagation();
8814 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8816 expand : function(){
8818 if(this.isExpanded() || !this.hasFocus){
8822 this.list.alignTo(this.inputEl(), this.listAlign);
8824 Roo.get(document).on('mousedown', this.collapseIf, this);
8825 Roo.get(document).on('mousewheel', this.collapseIf, this);
8826 if (!this.editable) {
8827 Roo.get(document).on('keydown', this.listKeyPress, this);
8830 this.fireEvent('expand', this);
8834 // Implements the default empty TriggerField.onTriggerClick function
8835 onTriggerClick : function()
8837 Roo.log('trigger click');
8844 this.loadNext = false;
8846 if(this.isExpanded()){
8848 if (!this.blockFocus) {
8849 this.inputEl().focus();
8853 this.hasFocus = true;
8854 if(this.triggerAction == 'all') {
8855 this.doQuery(this.allQuery, true);
8857 this.doQuery(this.getRawValue());
8859 if (!this.blockFocus) {
8860 this.inputEl().focus();
8864 listKeyPress : function(e)
8866 //Roo.log('listkeypress');
8867 // scroll to first matching element based on key pres..
8868 if (e.isSpecialKey()) {
8871 var k = String.fromCharCode(e.getKey()).toUpperCase();
8874 var csel = this.view.getSelectedNodes();
8875 var cselitem = false;
8877 var ix = this.view.indexOf(csel[0]);
8878 cselitem = this.store.getAt(ix);
8879 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8885 this.store.each(function(v) {
8887 // start at existing selection.
8888 if (cselitem.id == v.id) {
8894 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8895 match = this.store.indexOf(v);
8901 if (match === false) {
8902 return true; // no more action?
8905 this.view.select(match);
8906 var sn = Roo.get(this.view.getSelectedNodes()[0])
8907 //sn.scrollIntoView(sn.dom.parentNode, false);
8910 onViewScroll : function(e, t){
8912 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8916 this.hasQuery = true;
8918 this.loading = this.list.select('.loading', true).first();
8920 if(this.loading === null){
8921 this.list.createChild({
8923 cls: 'loading select2-more-results select2-active',
8924 html: 'Loading more results...'
8927 this.loading = this.list.select('.loading', true).first();
8929 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8931 this.loading.hide();
8934 this.loading.show();
8939 this.loadNext = true;
8941 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8946 addItem : function(o)
8948 var dv = ''; // display value
8950 if (this.displayField) {
8951 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8953 // this is an error condition!!!
8954 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8961 var choice = this.choices.createChild({
8963 cls: 'select2-search-choice',
8972 cls: 'select2-search-choice-close',
8977 }, this.searchField);
8979 var close = choice.select('a.select2-search-choice-close', true).first()
8981 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8988 this.inputEl().dom.value = '';
8992 onRemoveItem : function(e, _self, o)
8994 Roo.log('remove item');
8995 var index = this.item.indexOf(o.data) * 1;
8998 Roo.log('not this item?!');
9002 this.item.splice(index, 1);
9007 this.fireEvent('remove', this);
9011 syncValue : function()
9013 if(!this.item.length){
9020 Roo.each(this.item, function(i){
9021 if(_this.valueField){
9022 value.push(i[_this.valueField]);
9029 this.value = value.join(',');
9031 if(this.hiddenField){
9032 this.hiddenField.dom.value = this.value;
9036 clearItem : function()
9044 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9054 * @cfg {Boolean} grow
9058 * @cfg {Number} growMin
9062 * @cfg {Number} growMax
9072 * Ext JS Library 1.1.1
9073 * Copyright(c) 2006-2007, Ext JS, LLC.
9075 * Originally Released Under LGPL - original licence link has changed is not relivant.
9078 * <script type="text/javascript">
9083 * @extends Roo.util.Observable
9084 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9085 * This class also supports single and multi selection modes. <br>
9086 * Create a data model bound view:
9088 var store = new Roo.data.Store(...);
9090 var view = new Roo.View({
9092 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9095 selectedClass: "ydataview-selected",
9099 // listen for node click?
9100 view.on("click", function(vw, index, node, e){
9101 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9105 dataModel.load("foobar.xml");
9107 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9109 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9110 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9112 * Note: old style constructor is still suported (container, template, config)
9116 * @param {Object} config The config object
9119 Roo.View = function(config, depreciated_tpl, depreciated_config){
9121 if (typeof(depreciated_tpl) == 'undefined') {
9122 // new way.. - universal constructor.
9123 Roo.apply(this, config);
9124 this.el = Roo.get(this.el);
9127 this.el = Roo.get(config);
9128 this.tpl = depreciated_tpl;
9129 Roo.apply(this, depreciated_config);
9131 this.wrapEl = this.el.wrap().wrap();
9132 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9135 if(typeof(this.tpl) == "string"){
9136 this.tpl = new Roo.Template(this.tpl);
9138 // support xtype ctors..
9139 this.tpl = new Roo.factory(this.tpl, Roo);
9151 * @event beforeclick
9152 * Fires before a click is processed. Returns false to cancel the default action.
9153 * @param {Roo.View} this
9154 * @param {Number} index The index of the target node
9155 * @param {HTMLElement} node The target node
9156 * @param {Roo.EventObject} e The raw event object
9158 "beforeclick" : true,
9161 * Fires when a template node is clicked.
9162 * @param {Roo.View} this
9163 * @param {Number} index The index of the target node
9164 * @param {HTMLElement} node The target node
9165 * @param {Roo.EventObject} e The raw event object
9170 * Fires when a template node is double clicked.
9171 * @param {Roo.View} this
9172 * @param {Number} index The index of the target node
9173 * @param {HTMLElement} node The target node
9174 * @param {Roo.EventObject} e The raw event object
9178 * @event contextmenu
9179 * Fires when a template node is right clicked.
9180 * @param {Roo.View} this
9181 * @param {Number} index The index of the target node
9182 * @param {HTMLElement} node The target node
9183 * @param {Roo.EventObject} e The raw event object
9185 "contextmenu" : true,
9187 * @event selectionchange
9188 * Fires when the selected nodes change.
9189 * @param {Roo.View} this
9190 * @param {Array} selections Array of the selected nodes
9192 "selectionchange" : true,
9195 * @event beforeselect
9196 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9197 * @param {Roo.View} this
9198 * @param {HTMLElement} node The node to be selected
9199 * @param {Array} selections Array of currently selected nodes
9201 "beforeselect" : true,
9203 * @event preparedata
9204 * Fires on every row to render, to allow you to change the data.
9205 * @param {Roo.View} this
9206 * @param {Object} data to be rendered (change this)
9208 "preparedata" : true
9216 "click": this.onClick,
9217 "dblclick": this.onDblClick,
9218 "contextmenu": this.onContextMenu,
9222 this.selections = [];
9224 this.cmp = new Roo.CompositeElementLite([]);
9226 this.store = Roo.factory(this.store, Roo.data);
9227 this.setStore(this.store, true);
9230 if ( this.footer && this.footer.xtype) {
9232 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9234 this.footer.dataSource = this.store
9235 this.footer.container = fctr;
9236 this.footer = Roo.factory(this.footer, Roo);
9237 fctr.insertFirst(this.el);
9239 // this is a bit insane - as the paging toolbar seems to detach the el..
9240 // dom.parentNode.parentNode.parentNode
9241 // they get detached?
9245 Roo.View.superclass.constructor.call(this);
9250 Roo.extend(Roo.View, Roo.util.Observable, {
9253 * @cfg {Roo.data.Store} store Data store to load data from.
9258 * @cfg {String|Roo.Element} el The container element.
9263 * @cfg {String|Roo.Template} tpl The template used by this View
9267 * @cfg {String} dataName the named area of the template to use as the data area
9268 * Works with domtemplates roo-name="name"
9272 * @cfg {String} selectedClass The css class to add to selected nodes
9274 selectedClass : "x-view-selected",
9276 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9281 * @cfg {String} text to display on mask (default Loading)
9285 * @cfg {Boolean} multiSelect Allow multiple selection
9287 multiSelect : false,
9289 * @cfg {Boolean} singleSelect Allow single selection
9291 singleSelect: false,
9294 * @cfg {Boolean} toggleSelect - selecting
9296 toggleSelect : false,
9299 * Returns the element this view is bound to.
9300 * @return {Roo.Element}
9309 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9311 refresh : function(){
9315 // if we are using something like 'domtemplate', then
9316 // the what gets used is:
9317 // t.applySubtemplate(NAME, data, wrapping data..)
9318 // the outer template then get' applied with
9319 // the store 'extra data'
9320 // and the body get's added to the
9321 // roo-name="data" node?
9322 // <span class='roo-tpl-{name}'></span> ?????
9326 this.clearSelections();
9329 var records = this.store.getRange();
9330 if(records.length < 1) {
9332 // is this valid?? = should it render a template??
9334 this.el.update(this.emptyText);
9338 if (this.dataName) {
9339 this.el.update(t.apply(this.store.meta)); //????
9340 el = this.el.child('.roo-tpl-' + this.dataName);
9343 for(var i = 0, len = records.length; i < len; i++){
9344 var data = this.prepareData(records[i].data, i, records[i]);
9345 this.fireEvent("preparedata", this, data, i, records[i]);
9346 html[html.length] = Roo.util.Format.trim(
9348 t.applySubtemplate(this.dataName, data, this.store.meta) :
9355 el.update(html.join(""));
9356 this.nodes = el.dom.childNodes;
9357 this.updateIndexes(0);
9362 * Function to override to reformat the data that is sent to
9363 * the template for each node.
9364 * DEPRICATED - use the preparedata event handler.
9365 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9366 * a JSON object for an UpdateManager bound view).
9368 prepareData : function(data, index, record)
9370 this.fireEvent("preparedata", this, data, index, record);
9374 onUpdate : function(ds, record){
9375 Roo.log('on update');
9376 this.clearSelections();
9377 var index = this.store.indexOf(record);
9378 var n = this.nodes[index];
9379 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9380 n.parentNode.removeChild(n);
9381 this.updateIndexes(index, index);
9387 onAdd : function(ds, records, index)
9389 Roo.log(['on Add', ds, records, index] );
9390 this.clearSelections();
9391 if(this.nodes.length == 0){
9395 var n = this.nodes[index];
9396 for(var i = 0, len = records.length; i < len; i++){
9397 var d = this.prepareData(records[i].data, i, records[i]);
9399 this.tpl.insertBefore(n, d);
9402 this.tpl.append(this.el, d);
9405 this.updateIndexes(index);
9408 onRemove : function(ds, record, index){
9409 Roo.log('onRemove');
9410 this.clearSelections();
9411 var el = this.dataName ?
9412 this.el.child('.roo-tpl-' + this.dataName) :
9415 el.dom.removeChild(this.nodes[index]);
9416 this.updateIndexes(index);
9420 * Refresh an individual node.
9421 * @param {Number} index
9423 refreshNode : function(index){
9424 this.onUpdate(this.store, this.store.getAt(index));
9427 updateIndexes : function(startIndex, endIndex){
9428 var ns = this.nodes;
9429 startIndex = startIndex || 0;
9430 endIndex = endIndex || ns.length - 1;
9431 for(var i = startIndex; i <= endIndex; i++){
9432 ns[i].nodeIndex = i;
9437 * Changes the data store this view uses and refresh the view.
9438 * @param {Store} store
9440 setStore : function(store, initial){
9441 if(!initial && this.store){
9442 this.store.un("datachanged", this.refresh);
9443 this.store.un("add", this.onAdd);
9444 this.store.un("remove", this.onRemove);
9445 this.store.un("update", this.onUpdate);
9446 this.store.un("clear", this.refresh);
9447 this.store.un("beforeload", this.onBeforeLoad);
9448 this.store.un("load", this.onLoad);
9449 this.store.un("loadexception", this.onLoad);
9453 store.on("datachanged", this.refresh, this);
9454 store.on("add", this.onAdd, this);
9455 store.on("remove", this.onRemove, this);
9456 store.on("update", this.onUpdate, this);
9457 store.on("clear", this.refresh, this);
9458 store.on("beforeload", this.onBeforeLoad, this);
9459 store.on("load", this.onLoad, this);
9460 store.on("loadexception", this.onLoad, this);
9468 * onbeforeLoad - masks the loading area.
9471 onBeforeLoad : function(store,opts)
9473 Roo.log('onBeforeLoad');
9477 this.el.mask(this.mask ? this.mask : "Loading" );
9479 onLoad : function ()
9486 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9487 * @param {HTMLElement} node
9488 * @return {HTMLElement} The template node
9490 findItemFromChild : function(node){
9491 var el = this.dataName ?
9492 this.el.child('.roo-tpl-' + this.dataName,true) :
9495 if(!node || node.parentNode == el){
9498 var p = node.parentNode;
9499 while(p && p != el){
9500 if(p.parentNode == el){
9509 onClick : function(e){
9510 var item = this.findItemFromChild(e.getTarget());
9512 var index = this.indexOf(item);
9513 if(this.onItemClick(item, index, e) !== false){
9514 this.fireEvent("click", this, index, item, e);
9517 this.clearSelections();
9522 onContextMenu : function(e){
9523 var item = this.findItemFromChild(e.getTarget());
9525 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9530 onDblClick : function(e){
9531 var item = this.findItemFromChild(e.getTarget());
9533 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9537 onItemClick : function(item, index, e)
9539 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9542 if (this.toggleSelect) {
9543 var m = this.isSelected(item) ? 'unselect' : 'select';
9546 _t[m](item, true, false);
9549 if(this.multiSelect || this.singleSelect){
9550 if(this.multiSelect && e.shiftKey && this.lastSelection){
9551 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9553 this.select(item, this.multiSelect && e.ctrlKey);
9554 this.lastSelection = item;
9562 * Get the number of selected nodes.
9565 getSelectionCount : function(){
9566 return this.selections.length;
9570 * Get the currently selected nodes.
9571 * @return {Array} An array of HTMLElements
9573 getSelectedNodes : function(){
9574 return this.selections;
9578 * Get the indexes of the selected nodes.
9581 getSelectedIndexes : function(){
9582 var indexes = [], s = this.selections;
9583 for(var i = 0, len = s.length; i < len; i++){
9584 indexes.push(s[i].nodeIndex);
9590 * Clear all selections
9591 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9593 clearSelections : function(suppressEvent){
9594 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9595 this.cmp.elements = this.selections;
9596 this.cmp.removeClass(this.selectedClass);
9597 this.selections = [];
9599 this.fireEvent("selectionchange", this, this.selections);
9605 * Returns true if the passed node is selected
9606 * @param {HTMLElement/Number} node The node or node index
9609 isSelected : function(node){
9610 var s = this.selections;
9614 node = this.getNode(node);
9615 return s.indexOf(node) !== -1;
9620 * @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
9621 * @param {Boolean} keepExisting (optional) true to keep existing selections
9622 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9624 select : function(nodeInfo, keepExisting, suppressEvent){
9625 if(nodeInfo instanceof Array){
9627 this.clearSelections(true);
9629 for(var i = 0, len = nodeInfo.length; i < len; i++){
9630 this.select(nodeInfo[i], true, true);
9634 var node = this.getNode(nodeInfo);
9635 if(!node || this.isSelected(node)){
9636 return; // already selected.
9639 this.clearSelections(true);
9641 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9642 Roo.fly(node).addClass(this.selectedClass);
9643 this.selections.push(node);
9645 this.fireEvent("selectionchange", this, this.selections);
9653 * @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
9654 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9655 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9657 unselect : function(nodeInfo, keepExisting, suppressEvent)
9659 if(nodeInfo instanceof Array){
9660 Roo.each(this.selections, function(s) {
9661 this.unselect(s, nodeInfo);
9665 var node = this.getNode(nodeInfo);
9666 if(!node || !this.isSelected(node)){
9667 Roo.log("not selected");
9668 return; // not selected.
9672 Roo.each(this.selections, function(s) {
9674 Roo.fly(node).removeClass(this.selectedClass);
9681 this.selections= ns;
9682 this.fireEvent("selectionchange", this, this.selections);
9686 * Gets a template node.
9687 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9688 * @return {HTMLElement} The node or null if it wasn't found
9690 getNode : function(nodeInfo){
9691 if(typeof nodeInfo == "string"){
9692 return document.getElementById(nodeInfo);
9693 }else if(typeof nodeInfo == "number"){
9694 return this.nodes[nodeInfo];
9700 * Gets a range template nodes.
9701 * @param {Number} startIndex
9702 * @param {Number} endIndex
9703 * @return {Array} An array of nodes
9705 getNodes : function(start, end){
9706 var ns = this.nodes;
9708 end = typeof end == "undefined" ? ns.length - 1 : end;
9711 for(var i = start; i <= end; i++){
9715 for(var i = start; i >= end; i--){
9723 * Finds the index of the passed node
9724 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9725 * @return {Number} The index of the node or -1
9727 indexOf : function(node){
9728 node = this.getNode(node);
9729 if(typeof node.nodeIndex == "number"){
9730 return node.nodeIndex;
9732 var ns = this.nodes;
9733 for(var i = 0, len = ns.length; i < len; i++){
9744 * based on jquery fullcalendar
9748 Roo.bootstrap = Roo.bootstrap || {};
9750 * @class Roo.bootstrap.Calendar
9751 * @extends Roo.bootstrap.Component
9752 * Bootstrap Calendar class
9753 * @cfg {Boolean} loadMask (true|false) default false
9756 * Create a new Container
9757 * @param {Object} config The config object
9762 Roo.bootstrap.Calendar = function(config){
9763 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9767 * Fires when a date is selected
9768 * @param {DatePicker} this
9769 * @param {Date} date The selected date
9773 * @event monthchange
9774 * Fires when the displayed month changes
9775 * @param {DatePicker} this
9776 * @param {Date} date The selected month
9778 'monthchange': true,
9781 * Fires when mouse over an event
9782 * @param {Calendar} this
9783 * @param {event} Event
9788 * Fires when the mouse leaves an
9789 * @param {Calendar} this
9795 * Fires when the mouse click an
9796 * @param {Calendar} this
9805 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9808 * @cfg {Number} startDay
9809 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9815 getAutoCreate : function(){
9818 var fc_button = function(name, corner, style, content ) {
9819 return Roo.apply({},{
9821 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9823 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9826 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9834 style : 'width:100%',
9841 cls : 'fc-header-left',
9843 fc_button('prev', 'left', 'arrow', '‹' ),
9844 fc_button('next', 'right', 'arrow', '›' ),
9845 { tag: 'span', cls: 'fc-header-space' },
9846 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9854 cls : 'fc-header-center',
9858 cls: 'fc-header-title',
9861 html : 'month / year'
9869 cls : 'fc-header-right',
9871 /* fc_button('month', 'left', '', 'month' ),
9872 fc_button('week', '', '', 'week' ),
9873 fc_button('day', 'right', '', 'day' )
9885 var cal_heads = function() {
9887 // fixme - handle this.
9889 for (var i =0; i < Date.dayNames.length; i++) {
9890 var d = Date.dayNames[i];
9893 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9894 html : d.substring(0,3)
9898 ret[0].cls += ' fc-first';
9899 ret[6].cls += ' fc-last';
9902 var cal_cell = function(n) {
9905 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9910 cls: 'fc-day-number',
9914 cls: 'fc-day-content',
9918 style: 'position: relative;' // height: 17px;
9930 var cal_rows = function() {
9933 for (var r = 0; r < 6; r++) {
9940 for (var i =0; i < Date.dayNames.length; i++) {
9941 var d = Date.dayNames[i];
9942 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9945 row.cn[0].cls+=' fc-first';
9946 row.cn[0].cn[0].style = 'min-height:90px';
9947 row.cn[6].cls+=' fc-last';
9951 ret[0].cls += ' fc-first';
9952 ret[4].cls += ' fc-prev-last';
9953 ret[5].cls += ' fc-last';
9960 cls: 'fc-border-separate',
9961 style : 'width:100%',
9969 cls : 'fc-first fc-last',
9988 style : "position: relative;",
9991 cls : 'fc-view fc-view-month fc-grid',
9992 style : 'position: relative',
9993 unselectable : 'on',
9996 cls : 'fc-event-container',
9997 style : 'position:absolute;z-index:8;top:0;left:0;'
10015 initEvents : function()
10018 throw "can not find store for calendar";
10024 style: "text-align:center",
10028 style: "background-color:white;width:50%;margin:250 auto",
10032 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10043 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10045 var size = this.el.select('.fc-content', true).first().getSize();
10046 this.maskEl.setSize(size.width, size.height);
10047 this.maskEl.enableDisplayMode("block");
10048 if(!this.loadMask){
10049 this.maskEl.hide();
10052 this.store = Roo.factory(this.store, Roo.data);
10053 this.store.on('load', this.onLoad, this);
10054 this.store.on('beforeload', this.onBeforeLoad, this);
10058 this.cells = this.el.select('.fc-day',true);
10059 //Roo.log(this.cells);
10060 this.textNodes = this.el.query('.fc-day-number');
10061 this.cells.addClassOnOver('fc-state-hover');
10063 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10064 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10065 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10066 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10068 this.on('monthchange', this.onMonthChange, this);
10070 this.update(new Date().clearTime());
10073 resize : function() {
10074 var sz = this.el.getSize();
10076 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10077 this.el.select('.fc-day-content div',true).setHeight(34);
10082 showPrevMonth : function(e){
10083 this.update(this.activeDate.add("mo", -1));
10085 showToday : function(e){
10086 this.update(new Date().clearTime());
10089 showNextMonth : function(e){
10090 this.update(this.activeDate.add("mo", 1));
10094 showPrevYear : function(){
10095 this.update(this.activeDate.add("y", -1));
10099 showNextYear : function(){
10100 this.update(this.activeDate.add("y", 1));
10105 update : function(date)
10107 var vd = this.activeDate;
10108 this.activeDate = date;
10109 // if(vd && this.el){
10110 // var t = date.getTime();
10111 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10112 // Roo.log('using add remove');
10114 // this.fireEvent('monthchange', this, date);
10116 // this.cells.removeClass("fc-state-highlight");
10117 // this.cells.each(function(c){
10118 // if(c.dateValue == t){
10119 // c.addClass("fc-state-highlight");
10120 // setTimeout(function(){
10121 // try{c.dom.firstChild.focus();}catch(e){}
10131 var days = date.getDaysInMonth();
10133 var firstOfMonth = date.getFirstDateOfMonth();
10134 var startingPos = firstOfMonth.getDay()-this.startDay;
10136 if(startingPos < this.startDay){
10140 var pm = date.add(Date.MONTH, -1);
10141 var prevStart = pm.getDaysInMonth()-startingPos;
10143 this.cells = this.el.select('.fc-day',true);
10144 this.textNodes = this.el.query('.fc-day-number');
10145 this.cells.addClassOnOver('fc-state-hover');
10147 var cells = this.cells.elements;
10148 var textEls = this.textNodes;
10150 Roo.each(cells, function(cell){
10151 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10154 days += startingPos;
10156 // convert everything to numbers so it's fast
10157 var day = 86400000;
10158 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10161 //Roo.log(prevStart);
10163 var today = new Date().clearTime().getTime();
10164 var sel = date.clearTime().getTime();
10165 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10166 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10167 var ddMatch = this.disabledDatesRE;
10168 var ddText = this.disabledDatesText;
10169 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10170 var ddaysText = this.disabledDaysText;
10171 var format = this.format;
10173 var setCellClass = function(cal, cell){
10175 //Roo.log('set Cell Class');
10177 var t = d.getTime();
10181 cell.dateValue = t;
10183 cell.className += " fc-today";
10184 cell.className += " fc-state-highlight";
10185 cell.title = cal.todayText;
10188 // disable highlight in other month..
10189 //cell.className += " fc-state-highlight";
10194 cell.className = " fc-state-disabled";
10195 cell.title = cal.minText;
10199 cell.className = " fc-state-disabled";
10200 cell.title = cal.maxText;
10204 if(ddays.indexOf(d.getDay()) != -1){
10205 cell.title = ddaysText;
10206 cell.className = " fc-state-disabled";
10209 if(ddMatch && format){
10210 var fvalue = d.dateFormat(format);
10211 if(ddMatch.test(fvalue)){
10212 cell.title = ddText.replace("%0", fvalue);
10213 cell.className = " fc-state-disabled";
10217 if (!cell.initialClassName) {
10218 cell.initialClassName = cell.dom.className;
10221 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10226 for(; i < startingPos; i++) {
10227 textEls[i].innerHTML = (++prevStart);
10228 d.setDate(d.getDate()+1);
10230 cells[i].className = "fc-past fc-other-month";
10231 setCellClass(this, cells[i]);
10236 for(; i < days; i++){
10237 intDay = i - startingPos + 1;
10238 textEls[i].innerHTML = (intDay);
10239 d.setDate(d.getDate()+1);
10241 cells[i].className = ''; // "x-date-active";
10242 setCellClass(this, cells[i]);
10246 for(; i < 42; i++) {
10247 textEls[i].innerHTML = (++extraDays);
10248 d.setDate(d.getDate()+1);
10250 cells[i].className = "fc-future fc-other-month";
10251 setCellClass(this, cells[i]);
10254 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10256 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10258 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10259 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10261 if(totalRows != 6){
10262 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10263 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10266 this.fireEvent('monthchange', this, date);
10270 if(!this.internalRender){
10271 var main = this.el.dom.firstChild;
10272 var w = main.offsetWidth;
10273 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10274 Roo.fly(main).setWidth(w);
10275 this.internalRender = true;
10276 // opera does not respect the auto grow header center column
10277 // then, after it gets a width opera refuses to recalculate
10278 // without a second pass
10279 if(Roo.isOpera && !this.secondPass){
10280 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10281 this.secondPass = true;
10282 this.update.defer(10, this, [date]);
10289 findCell : function(dt) {
10290 dt = dt.clearTime().getTime();
10292 this.cells.each(function(c){
10293 //Roo.log("check " +c.dateValue + '?=' + dt);
10294 if(c.dateValue == dt){
10304 findCells : function(ev) {
10305 var s = ev.start.clone().clearTime().getTime();
10307 var e= ev.end.clone().clearTime().getTime();
10310 this.cells.each(function(c){
10311 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10313 if(c.dateValue > e){
10316 if(c.dateValue < s){
10325 findBestRow: function(cells)
10329 for (var i =0 ; i < cells.length;i++) {
10330 ret = Math.max(cells[i].rows || 0,ret);
10337 addItem : function(ev)
10339 // look for vertical location slot in
10340 var cells = this.findCells(ev);
10342 ev.row = this.findBestRow(cells);
10344 // work out the location.
10348 for(var i =0; i < cells.length; i++) {
10356 if (crow.start.getY() == cells[i].getY()) {
10358 crow.end = cells[i];
10374 for (var i = 0; i < cells.length;i++) {
10375 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10379 this.calevents.push(ev);
10382 clearEvents: function() {
10384 if(!this.calevents){
10388 Roo.each(this.cells.elements, function(c){
10392 Roo.each(this.calevents, function(e) {
10393 Roo.each(e.els, function(el) {
10394 el.un('mouseenter' ,this.onEventEnter, this);
10395 el.un('mouseleave' ,this.onEventLeave, this);
10402 renderEvents: function()
10404 // first make sure there is enough space..
10406 this.cells.each(function(c) {
10408 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10411 for (var e = 0; e < this.calevents.length; e++) {
10412 var ev = this.calevents[e];
10413 var cells = ev.cells;
10414 var rows = ev.rows;
10416 for(var i =0; i < rows.length; i++) {
10419 // how many rows should it span..
10422 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10423 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10425 unselectable : "on",
10428 cls: 'fc-event-inner',
10432 // cls: 'fc-event-time',
10433 // html : cells.length > 1 ? '' : ev.time
10437 cls: 'fc-event-title',
10438 html : String.format('{0}', ev.title)
10445 cls: 'ui-resizable-handle ui-resizable-e',
10446 html : '  '
10452 cfg.cls += ' fc-event-start';
10454 if ((i+1) == rows.length) {
10455 cfg.cls += ' fc-event-end';
10458 var ctr = this.el.select('.fc-event-container',true).first();
10459 var cg = ctr.createChild(cfg);
10461 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10462 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10463 cg.on('click', this.onEventClick, this, ev);
10467 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10468 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10470 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10471 cg.setWidth(ebox.right - sbox.x -2);
10479 onEventEnter: function (e, el,event,d) {
10480 this.fireEvent('evententer', this, el, event);
10483 onEventLeave: function (e, el,event,d) {
10484 this.fireEvent('eventleave', this, el, event);
10487 onEventClick: function (e, el,event,d) {
10488 this.fireEvent('eventclick', this, el, event);
10491 onMonthChange: function () {
10495 onLoad: function ()
10497 this.calevents = [];
10500 if(this.store.getCount() > 0){
10501 this.store.data.each(function(d){
10504 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10505 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10506 time : d.data.start_time,
10507 title : d.data.title,
10508 description : d.data.description,
10509 venue : d.data.venue
10514 this.renderEvents();
10517 this.maskEl.hide();
10521 onBeforeLoad: function()
10523 this.clearEvents();
10526 this.maskEl.show();
10540 * @class Roo.bootstrap.Popover
10541 * @extends Roo.bootstrap.Component
10542 * Bootstrap Popover class
10543 * @cfg {String} html contents of the popover (or false to use children..)
10544 * @cfg {String} title of popover (or false to hide)
10545 * @cfg {String} placement how it is placed
10546 * @cfg {String} trigger click || hover (or false to trigger manually)
10547 * @cfg {String} over what (parent or false to trigger manually.)
10550 * Create a new Popover
10551 * @param {Object} config The config object
10554 Roo.bootstrap.Popover = function(config){
10555 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10558 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10560 title: 'Fill in a title',
10563 placement : 'right',
10564 trigger : 'hover', // hover
10568 can_build_overlaid : false,
10570 getChildContainer : function()
10572 return this.el.select('.popover-content',true).first();
10575 getAutoCreate : function(){
10576 Roo.log('make popover?');
10578 cls : 'popover roo-dynamic',
10579 style: 'display:block',
10585 cls : 'popover-inner',
10589 cls: 'popover-title',
10593 cls : 'popover-content',
10604 setTitle: function(str)
10606 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10608 setContent: function(str)
10610 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10612 // as it get's added to the bottom of the page.
10613 onRender : function(ct, position)
10615 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10617 var cfg = Roo.apply({}, this.getAutoCreate());
10621 cfg.cls += ' ' + this.cls;
10624 cfg.style = this.style;
10626 Roo.log("adding to ")
10627 this.el = Roo.get(document.body).createChild(cfg, position);
10633 initEvents : function()
10635 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10636 this.el.enableDisplayMode('block');
10638 if (this.over === false) {
10641 if (this.triggers === false) {
10644 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10645 var triggers = this.trigger ? this.trigger.split(' ') : [];
10646 Roo.each(triggers, function(trigger) {
10648 if (trigger == 'click') {
10649 on_el.on('click', this.toggle, this);
10650 } else if (trigger != 'manual') {
10651 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10652 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10654 on_el.on(eventIn ,this.enter, this);
10655 on_el.on(eventOut, this.leave, this);
10666 toggle : function () {
10667 this.hoverState == 'in' ? this.leave() : this.enter();
10670 enter : function () {
10673 clearTimeout(this.timeout);
10675 this.hoverState = 'in'
10677 if (!this.delay || !this.delay.show) {
10682 this.timeout = setTimeout(function () {
10683 if (_t.hoverState == 'in') {
10686 }, this.delay.show)
10688 leave : function() {
10689 clearTimeout(this.timeout);
10691 this.hoverState = 'out'
10693 if (!this.delay || !this.delay.hide) {
10698 this.timeout = setTimeout(function () {
10699 if (_t.hoverState == 'out') {
10702 }, this.delay.hide)
10705 show : function (on_el)
10708 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10711 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10712 if (this.html !== false) {
10713 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10715 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10716 if (!this.title.length) {
10717 this.el.select('.popover-title',true).hide();
10720 var placement = typeof this.placement == 'function' ?
10721 this.placement.call(this, this.el, on_el) :
10724 var autoToken = /\s?auto?\s?/i;
10725 var autoPlace = autoToken.test(placement);
10727 placement = placement.replace(autoToken, '') || 'top';
10731 //this.el.setXY([0,0]);
10733 this.el.dom.style.display='block';
10734 this.el.addClass(placement);
10736 //this.el.appendTo(on_el);
10738 var p = this.getPosition();
10739 var box = this.el.getBox();
10744 var align = Roo.bootstrap.Popover.alignment[placement]
10745 this.el.alignTo(on_el, align[0],align[1]);
10746 //var arrow = this.el.select('.arrow',true).first();
10747 //arrow.set(align[2],
10749 this.el.addClass('in');
10750 this.hoverState = null;
10752 if (this.el.hasClass('fade')) {
10759 this.el.setXY([0,0]);
10760 this.el.removeClass('in');
10767 Roo.bootstrap.Popover.alignment = {
10768 'left' : ['r-l', [-10,0], 'right'],
10769 'right' : ['l-r', [10,0], 'left'],
10770 'bottom' : ['t-b', [0,10], 'top'],
10771 'top' : [ 'b-t', [0,-10], 'bottom']
10782 * @class Roo.bootstrap.Progress
10783 * @extends Roo.bootstrap.Component
10784 * Bootstrap Progress class
10785 * @cfg {Boolean} striped striped of the progress bar
10786 * @cfg {Boolean} active animated of the progress bar
10790 * Create a new Progress
10791 * @param {Object} config The config object
10794 Roo.bootstrap.Progress = function(config){
10795 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10798 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10803 getAutoCreate : function(){
10811 cfg.cls += ' progress-striped';
10815 cfg.cls += ' active';
10834 * @class Roo.bootstrap.ProgressBar
10835 * @extends Roo.bootstrap.Component
10836 * Bootstrap ProgressBar class
10837 * @cfg {Number} aria_valuenow aria-value now
10838 * @cfg {Number} aria_valuemin aria-value min
10839 * @cfg {Number} aria_valuemax aria-value max
10840 * @cfg {String} label label for the progress bar
10841 * @cfg {String} panel (success | info | warning | danger )
10842 * @cfg {String} role role of the progress bar
10843 * @cfg {String} sr_only text
10847 * Create a new ProgressBar
10848 * @param {Object} config The config object
10851 Roo.bootstrap.ProgressBar = function(config){
10852 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10855 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10859 aria_valuemax : 100,
10865 getAutoCreate : function()
10870 cls: 'progress-bar',
10871 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10883 cfg.role = this.role;
10886 if(this.aria_valuenow){
10887 cfg['aria-valuenow'] = this.aria_valuenow;
10890 if(this.aria_valuemin){
10891 cfg['aria-valuemin'] = this.aria_valuemin;
10894 if(this.aria_valuemax){
10895 cfg['aria-valuemax'] = this.aria_valuemax;
10898 if(this.label && !this.sr_only){
10899 cfg.html = this.label;
10903 cfg.cls += ' progress-bar-' + this.panel;
10909 update : function(aria_valuenow)
10911 this.aria_valuenow = aria_valuenow;
10913 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10928 * @class Roo.bootstrap.TabPanel
10929 * @extends Roo.bootstrap.Component
10930 * Bootstrap TabPanel class
10931 * @cfg {Boolean} active panel active
10932 * @cfg {String} html panel content
10933 * @cfg {String} tabId tab relate id
10937 * Create a new TabPanel
10938 * @param {Object} config The config object
10941 Roo.bootstrap.TabPanel = function(config){
10942 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10945 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10951 getAutoCreate : function(){
10955 html: this.html || ''
10959 cfg.cls += ' active';
10963 cfg.tabId = this.tabId;
10981 * @class Roo.bootstrap.DateField
10982 * @extends Roo.bootstrap.Input
10983 * Bootstrap DateField class
10984 * @cfg {Number} weekStart default 0
10985 * @cfg {Number} weekStart default 0
10986 * @cfg {Number} viewMode default empty, (months|years)
10987 * @cfg {Number} minViewMode default empty, (months|years)
10988 * @cfg {Number} startDate default -Infinity
10989 * @cfg {Number} endDate default Infinity
10990 * @cfg {Boolean} todayHighlight default false
10991 * @cfg {Boolean} todayBtn default false
10992 * @cfg {Boolean} calendarWeeks default false
10993 * @cfg {Object} daysOfWeekDisabled default empty
10995 * @cfg {Boolean} keyboardNavigation default true
10996 * @cfg {String} language default en
10999 * Create a new DateField
11000 * @param {Object} config The config object
11003 Roo.bootstrap.DateField = function(config){
11004 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11008 * Fires when this field show.
11009 * @param {Roo.bootstrap.DateField} this
11010 * @param {Mixed} date The date value
11015 * Fires when this field hide.
11016 * @param {Roo.bootstrap.DateField} this
11017 * @param {Mixed} date The date value
11022 * Fires when select a date.
11023 * @param {Roo.bootstrap.DateField} this
11024 * @param {Mixed} date The date value
11030 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
11033 * @cfg {String} format
11034 * The default date format string which can be overriden for localization support. The format must be
11035 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11039 * @cfg {String} altFormats
11040 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11041 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11043 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11051 todayHighlight : false,
11057 keyboardNavigation: true,
11059 calendarWeeks: false,
11061 startDate: -Infinity,
11065 daysOfWeekDisabled: [],
11069 UTCDate: function()
11071 return new Date(Date.UTC.apply(Date, arguments));
11074 UTCToday: function()
11076 var today = new Date();
11077 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11080 getDate: function() {
11081 var d = this.getUTCDate();
11082 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11085 getUTCDate: function() {
11089 setDate: function(d) {
11090 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11093 setUTCDate: function(d) {
11095 this.setValue(this.formatDate(this.date));
11098 onRender: function(ct, position)
11101 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11103 this.language = this.language || 'en';
11104 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11105 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11107 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11108 this.format = this.format || 'm/d/y';
11109 this.isInline = false;
11110 this.isInput = true;
11111 this.component = this.el.select('.add-on', true).first() || false;
11112 this.component = (this.component && this.component.length === 0) ? false : this.component;
11113 this.hasInput = this.component && this.inputEL().length;
11115 if (typeof(this.minViewMode === 'string')) {
11116 switch (this.minViewMode) {
11118 this.minViewMode = 1;
11121 this.minViewMode = 2;
11124 this.minViewMode = 0;
11129 if (typeof(this.viewMode === 'string')) {
11130 switch (this.viewMode) {
11143 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11145 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11147 this.picker().on('mousedown', this.onMousedown, this);
11148 this.picker().on('click', this.onClick, this);
11150 this.picker().addClass('datepicker-dropdown');
11152 this.startViewMode = this.viewMode;
11155 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11156 if(!this.calendarWeeks){
11161 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11162 v.attr('colspan', function(i, val){
11163 return parseInt(val) + 1;
11168 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11170 this.setStartDate(this.startDate);
11171 this.setEndDate(this.endDate);
11173 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11180 if(this.isInline) {
11185 picker : function()
11187 return this.el.select('.datepicker', true).first();
11190 fillDow: function()
11192 var dowCnt = this.weekStart;
11201 if(this.calendarWeeks){
11209 while (dowCnt < this.weekStart + 7) {
11213 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11217 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11220 fillMonths: function()
11223 var months = this.picker().select('>.datepicker-months td', true).first();
11225 months.dom.innerHTML = '';
11231 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11234 months.createChild(month);
11239 update: function(){
11241 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11243 if (this.date < this.startDate) {
11244 this.viewDate = new Date(this.startDate);
11245 } else if (this.date > this.endDate) {
11246 this.viewDate = new Date(this.endDate);
11248 this.viewDate = new Date(this.date);
11255 var d = new Date(this.viewDate),
11256 year = d.getUTCFullYear(),
11257 month = d.getUTCMonth(),
11258 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11259 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11260 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11261 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11262 currentDate = this.date && this.date.valueOf(),
11263 today = this.UTCToday();
11265 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11267 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11269 // this.picker.select('>tfoot th.today').
11270 // .text(dates[this.language].today)
11271 // .toggle(this.todayBtn !== false);
11273 this.updateNavArrows();
11276 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11278 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11280 prevMonth.setUTCDate(day);
11282 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11284 var nextMonth = new Date(prevMonth);
11286 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11288 nextMonth = nextMonth.valueOf();
11290 var fillMonths = false;
11292 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11294 while(prevMonth.valueOf() < nextMonth) {
11297 if (prevMonth.getUTCDay() === this.weekStart) {
11299 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11307 if(this.calendarWeeks){
11308 // ISO 8601: First week contains first thursday.
11309 // ISO also states week starts on Monday, but we can be more abstract here.
11311 // Start of current week: based on weekstart/current date
11312 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11313 // Thursday of this week
11314 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11315 // First Thursday of year, year from thursday
11316 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11317 // Calendar week: ms between thursdays, div ms per day, div 7 days
11318 calWeek = (th - yth) / 864e5 / 7 + 1;
11320 fillMonths.cn.push({
11328 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11330 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11333 if (this.todayHighlight &&
11334 prevMonth.getUTCFullYear() == today.getFullYear() &&
11335 prevMonth.getUTCMonth() == today.getMonth() &&
11336 prevMonth.getUTCDate() == today.getDate()) {
11337 clsName += ' today';
11340 if (currentDate && prevMonth.valueOf() === currentDate) {
11341 clsName += ' active';
11344 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11345 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11346 clsName += ' disabled';
11349 fillMonths.cn.push({
11351 cls: 'day ' + clsName,
11352 html: prevMonth.getDate()
11355 prevMonth.setDate(prevMonth.getDate()+1);
11358 var currentYear = this.date && this.date.getUTCFullYear();
11359 var currentMonth = this.date && this.date.getUTCMonth();
11361 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11363 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11364 v.removeClass('active');
11366 if(currentYear === year && k === currentMonth){
11367 v.addClass('active');
11370 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11371 v.addClass('disabled');
11377 year = parseInt(year/10, 10) * 10;
11379 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11381 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11384 for (var i = -1; i < 11; i++) {
11385 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11387 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11395 showMode: function(dir) {
11397 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11399 Roo.each(this.picker().select('>div',true).elements, function(v){
11400 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11403 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11408 if(this.isInline) return;
11410 this.picker().removeClass(['bottom', 'top']);
11412 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11414 * place to the top of element!
11418 this.picker().addClass('top');
11419 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11424 this.picker().addClass('bottom');
11426 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11429 parseDate : function(value){
11430 if(!value || value instanceof Date){
11433 var v = Date.parseDate(value, this.format);
11434 if (!v && this.useIso) {
11435 v = Date.parseDate(value, 'Y-m-d');
11437 if(!v && this.altFormats){
11438 if(!this.altFormatsArray){
11439 this.altFormatsArray = this.altFormats.split("|");
11441 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11442 v = Date.parseDate(value, this.altFormatsArray[i]);
11448 formatDate : function(date, fmt){
11449 return (!date || !(date instanceof Date)) ?
11450 date : date.dateFormat(fmt || this.format);
11453 onFocus : function()
11455 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11459 onBlur : function()
11461 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11467 this.picker().show();
11471 this.fireEvent('show', this, this.date);
11476 if(this.isInline) return;
11477 this.picker().hide();
11478 this.viewMode = this.startViewMode;
11481 this.fireEvent('hide', this, this.date);
11485 onMousedown: function(e){
11486 e.stopPropagation();
11487 e.preventDefault();
11490 keyup: function(e){
11491 Roo.bootstrap.DateField.superclass.keyup.call(this);
11496 setValue: function(v){
11497 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11499 this.fireEvent('select', this, this.date);
11503 fireKey: function(e){
11504 if (!this.picker().isVisible()){
11505 if (e.keyCode == 27) // allow escape to hide and re-show picker
11509 var dateChanged = false,
11511 newDate, newViewDate;
11515 e.preventDefault();
11519 if (!this.keyboardNavigation) break;
11520 dir = e.keyCode == 37 ? -1 : 1;
11523 newDate = this.moveYear(this.date, dir);
11524 newViewDate = this.moveYear(this.viewDate, dir);
11525 } else if (e.shiftKey){
11526 newDate = this.moveMonth(this.date, dir);
11527 newViewDate = this.moveMonth(this.viewDate, dir);
11529 newDate = new Date(this.date);
11530 newDate.setUTCDate(this.date.getUTCDate() + dir);
11531 newViewDate = new Date(this.viewDate);
11532 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11534 if (this.dateWithinRange(newDate)){
11535 this.date = newDate;
11536 this.viewDate = newViewDate;
11537 this.setValue(this.formatDate(this.date));
11539 e.preventDefault();
11540 dateChanged = true;
11545 if (!this.keyboardNavigation) break;
11546 dir = e.keyCode == 38 ? -1 : 1;
11548 newDate = this.moveYear(this.date, dir);
11549 newViewDate = this.moveYear(this.viewDate, dir);
11550 } else if (e.shiftKey){
11551 newDate = this.moveMonth(this.date, dir);
11552 newViewDate = this.moveMonth(this.viewDate, dir);
11554 newDate = new Date(this.date);
11555 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11556 newViewDate = new Date(this.viewDate);
11557 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11559 if (this.dateWithinRange(newDate)){
11560 this.date = newDate;
11561 this.viewDate = newViewDate;
11562 this.setValue(this.formatDate(this.date));
11564 e.preventDefault();
11565 dateChanged = true;
11569 this.setValue(this.formatDate(this.date));
11571 e.preventDefault();
11574 this.setValue(this.formatDate(this.date));
11581 onClick: function(e) {
11582 e.stopPropagation();
11583 e.preventDefault();
11585 var target = e.getTarget();
11587 if(target.nodeName.toLowerCase() === 'i'){
11588 target = Roo.get(target).dom.parentNode;
11591 var nodeName = target.nodeName;
11592 var className = target.className;
11593 var html = target.innerHTML;
11595 switch(nodeName.toLowerCase()) {
11597 switch(className) {
11603 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11604 switch(this.viewMode){
11606 this.viewDate = this.moveMonth(this.viewDate, dir);
11610 this.viewDate = this.moveYear(this.viewDate, dir);
11616 var date = new Date();
11617 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11619 this.setValue(this.formatDate(this.date));
11625 if (className.indexOf('disabled') === -1) {
11626 this.viewDate.setUTCDate(1);
11627 if (className.indexOf('month') !== -1) {
11628 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11630 var year = parseInt(html, 10) || 0;
11631 this.viewDate.setUTCFullYear(year);
11640 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11641 var day = parseInt(html, 10) || 1;
11642 var year = this.viewDate.getUTCFullYear(),
11643 month = this.viewDate.getUTCMonth();
11645 if (className.indexOf('old') !== -1) {
11652 } else if (className.indexOf('new') !== -1) {
11660 this.date = this.UTCDate(year, month, day,0,0,0,0);
11661 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11663 this.setValue(this.formatDate(this.date));
11670 setStartDate: function(startDate){
11671 this.startDate = startDate || -Infinity;
11672 if (this.startDate !== -Infinity) {
11673 this.startDate = this.parseDate(this.startDate);
11676 this.updateNavArrows();
11679 setEndDate: function(endDate){
11680 this.endDate = endDate || Infinity;
11681 if (this.endDate !== Infinity) {
11682 this.endDate = this.parseDate(this.endDate);
11685 this.updateNavArrows();
11688 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11689 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11690 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11691 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11693 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11694 return parseInt(d, 10);
11697 this.updateNavArrows();
11700 updateNavArrows: function() {
11701 var d = new Date(this.viewDate),
11702 year = d.getUTCFullYear(),
11703 month = d.getUTCMonth();
11705 Roo.each(this.picker().select('.prev', true).elements, function(v){
11707 switch (this.viewMode) {
11710 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11716 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11723 Roo.each(this.picker().select('.next', true).elements, function(v){
11725 switch (this.viewMode) {
11728 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11734 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11742 moveMonth: function(date, dir){
11743 if (!dir) return date;
11744 var new_date = new Date(date.valueOf()),
11745 day = new_date.getUTCDate(),
11746 month = new_date.getUTCMonth(),
11747 mag = Math.abs(dir),
11749 dir = dir > 0 ? 1 : -1;
11752 // If going back one month, make sure month is not current month
11753 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11755 return new_date.getUTCMonth() == month;
11757 // If going forward one month, make sure month is as expected
11758 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11760 return new_date.getUTCMonth() != new_month;
11762 new_month = month + dir;
11763 new_date.setUTCMonth(new_month);
11764 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11765 if (new_month < 0 || new_month > 11)
11766 new_month = (new_month + 12) % 12;
11768 // For magnitudes >1, move one month at a time...
11769 for (var i=0; i<mag; i++)
11770 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11771 new_date = this.moveMonth(new_date, dir);
11772 // ...then reset the day, keeping it in the new month
11773 new_month = new_date.getUTCMonth();
11774 new_date.setUTCDate(day);
11776 return new_month != new_date.getUTCMonth();
11779 // Common date-resetting loop -- if date is beyond end of month, make it
11782 new_date.setUTCDate(--day);
11783 new_date.setUTCMonth(new_month);
11788 moveYear: function(date, dir){
11789 return this.moveMonth(date, dir*12);
11792 dateWithinRange: function(date){
11793 return date >= this.startDate && date <= this.endDate;
11797 remove: function() {
11798 this.picker().remove();
11803 Roo.apply(Roo.bootstrap.DateField, {
11814 html: '<i class="icon-arrow-left"/>'
11824 html: '<i class="icon-arrow-right"/>'
11866 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11867 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11868 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11869 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11870 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11883 navFnc: 'FullYear',
11888 navFnc: 'FullYear',
11893 Roo.apply(Roo.bootstrap.DateField, {
11897 cls: 'datepicker dropdown-menu',
11901 cls: 'datepicker-days',
11905 cls: 'table-condensed',
11907 Roo.bootstrap.DateField.head,
11911 Roo.bootstrap.DateField.footer
11918 cls: 'datepicker-months',
11922 cls: 'table-condensed',
11924 Roo.bootstrap.DateField.head,
11925 Roo.bootstrap.DateField.content,
11926 Roo.bootstrap.DateField.footer
11933 cls: 'datepicker-years',
11937 cls: 'table-condensed',
11939 Roo.bootstrap.DateField.head,
11940 Roo.bootstrap.DateField.content,
11941 Roo.bootstrap.DateField.footer
11960 * @class Roo.bootstrap.TimeField
11961 * @extends Roo.bootstrap.Input
11962 * Bootstrap DateField class
11966 * Create a new TimeField
11967 * @param {Object} config The config object
11970 Roo.bootstrap.TimeField = function(config){
11971 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11975 * Fires when this field show.
11976 * @param {Roo.bootstrap.DateField} this
11977 * @param {Mixed} date The date value
11982 * Fires when this field hide.
11983 * @param {Roo.bootstrap.DateField} this
11984 * @param {Mixed} date The date value
11989 * Fires when select a date.
11990 * @param {Roo.bootstrap.DateField} this
11991 * @param {Mixed} date The date value
11997 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
12000 * @cfg {String} format
12001 * The default time format string which can be overriden for localization support. The format must be
12002 * valid according to {@link Date#parseDate} (defaults to 'H:i').
12006 onRender: function(ct, position)
12009 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12011 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12013 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12015 this.pop = this.picker().select('>.datepicker-time',true).first();
12016 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
12018 this.picker().on('mousedown', this.onMousedown, this);
12019 this.picker().on('click', this.onClick, this);
12021 this.picker().addClass('datepicker-dropdown');
12026 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12027 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12028 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12029 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12030 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12031 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12035 fireKey: function(e){
12036 if (!this.picker().isVisible()){
12037 if (e.keyCode == 27) // allow escape to hide and re-show picker
12042 e.preventDefault();
12050 this.onTogglePeriod();
12053 this.onIncrementMinutes();
12056 this.onDecrementMinutes();
12065 onClick: function(e) {
12066 e.stopPropagation();
12067 e.preventDefault();
12070 picker : function()
12072 return this.el.select('.datepicker', true).first();
12075 fillTime: function()
12077 var time = this.pop.select('tbody', true).first();
12079 time.dom.innerHTML = '';
12094 cls: 'hours-up glyphicon glyphicon-chevron-up'
12114 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12135 cls: 'timepicker-hour',
12150 cls: 'timepicker-minute',
12165 cls: 'btn btn-primary period',
12187 cls: 'hours-down glyphicon glyphicon-chevron-down'
12207 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12225 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12232 var hours = this.time.getHours();
12233 var minutes = this.time.getMinutes();
12246 hours = hours - 12;
12250 hours = '0' + hours;
12254 minutes = '0' + minutes;
12257 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12258 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12259 this.pop.select('button', true).first().dom.innerHTML = period;
12265 this.picker().removeClass(['bottom', 'top']);
12267 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12269 * place to the top of element!
12273 this.picker().addClass('top');
12274 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12279 this.picker().addClass('bottom');
12281 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12284 onFocus : function()
12286 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12290 onBlur : function()
12292 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12298 this.picker().show();
12303 this.fireEvent('show', this, this.date);
12308 this.picker().hide();
12311 this.fireEvent('hide', this, this.date);
12314 setTime : function()
12317 this.setValue(this.time.format(this.format));
12319 this.fireEvent('select', this, this.date);
12324 onMousedown: function(e){
12325 e.stopPropagation();
12326 e.preventDefault();
12329 onIncrementHours: function()
12331 Roo.log('onIncrementHours');
12332 this.time = this.time.add(Date.HOUR, 1);
12337 onDecrementHours: function()
12339 Roo.log('onDecrementHours');
12340 this.time = this.time.add(Date.HOUR, -1);
12344 onIncrementMinutes: function()
12346 Roo.log('onIncrementMinutes');
12347 this.time = this.time.add(Date.MINUTE, 1);
12351 onDecrementMinutes: function()
12353 Roo.log('onDecrementMinutes');
12354 this.time = this.time.add(Date.MINUTE, -1);
12358 onTogglePeriod: function()
12360 Roo.log('onTogglePeriod');
12361 this.time = this.time.add(Date.HOUR, 12);
12368 Roo.apply(Roo.bootstrap.TimeField, {
12398 cls: 'btn btn-info ok',
12410 Roo.apply(Roo.bootstrap.TimeField, {
12414 cls: 'datepicker dropdown-menu',
12418 cls: 'datepicker-time',
12422 cls: 'table-condensed',
12424 Roo.bootstrap.TimeField.content,
12425 Roo.bootstrap.TimeField.footer
12444 * @class Roo.bootstrap.CheckBox
12445 * @extends Roo.bootstrap.Input
12446 * Bootstrap CheckBox class
12448 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12449 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12450 * @cfg {String} boxLabel The text that appears beside the checkbox
12451 * @cfg {Boolean} checked initnal the element
12454 * Create a new CheckBox
12455 * @param {Object} config The config object
12458 Roo.bootstrap.CheckBox = function(config){
12459 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12464 * Fires when the element is checked or unchecked.
12465 * @param {Roo.bootstrap.CheckBox} this This input
12466 * @param {Boolean} checked The new checked value
12472 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12474 inputType: 'checkbox',
12480 getAutoCreate : function()
12482 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12488 cfg.cls = 'form-group' //input-group
12493 type : this.inputType,
12494 value : (!this.checked) ? this.valueOff : this.inputValue,
12496 placeholder : this.placeholder || ''
12500 if (this.disabled) {
12501 input.disabled=true;
12505 input.checked = this.checked;
12509 input.name = this.name;
12513 input.cls += ' input-' + this.size;
12517 ['xs','sm','md','lg'].map(function(size){
12518 if (settings[size]) {
12519 cfg.cls += ' col-' + size + '-' + settings[size];
12523 var inputblock = input;
12525 if (this.before || this.after) {
12528 cls : 'input-group',
12532 inputblock.cn.push({
12534 cls : 'input-group-addon',
12538 inputblock.cn.push(input);
12540 inputblock.cn.push({
12542 cls : 'input-group-addon',
12549 if (align ==='left' && this.fieldLabel.length) {
12550 Roo.log("left and has label");
12556 cls : 'control-label col-md-' + this.labelWidth,
12557 html : this.fieldLabel
12561 cls : "col-md-" + (12 - this.labelWidth),
12568 } else if ( this.fieldLabel.length) {
12573 tag: this.boxLabel ? 'span' : 'label',
12575 cls: 'control-label box-input-label',
12576 //cls : 'input-group-addon',
12577 html : this.fieldLabel
12587 Roo.log(" no label && no align");
12602 html: this.boxLabel
12611 * return the real input element.
12613 inputEl: function ()
12615 return this.el.select('input.form-box',true).first();
12618 initEvents : function()
12620 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12622 this.inputEl().on('click', this.onClick, this);
12626 onClick : function()
12628 this.setChecked(!this.checked);
12631 setChecked : function(state,suppressEvent)
12633 this.checked = state;
12635 this.inputEl().dom.checked = state;
12637 if(suppressEvent !== true){
12638 this.fireEvent('check', this, state);
12641 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12645 setValue : function(v,suppressEvent)
12647 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12661 * @class Roo.bootstrap.Radio
12662 * @extends Roo.bootstrap.CheckBox
12663 * Bootstrap Radio class
12666 * Create a new Radio
12667 * @param {Object} config The config object
12670 Roo.bootstrap.Radio = function(config){
12671 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12675 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12677 inputType: 'radio',
12681 getAutoCreate : function()
12683 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12689 cfg.cls = 'form-group' //input-group
12694 type : this.inputType,
12695 value : (!this.checked) ? this.valueOff : this.inputValue,
12697 placeholder : this.placeholder || ''
12701 if (this.disabled) {
12702 input.disabled=true;
12706 input.checked = this.checked;
12710 input.name = this.name;
12714 input.cls += ' input-' + this.size;
12718 ['xs','sm','md','lg'].map(function(size){
12719 if (settings[size]) {
12720 cfg.cls += ' col-' + size + '-' + settings[size];
12724 var inputblock = input;
12726 if (this.before || this.after) {
12729 cls : 'input-group',
12733 inputblock.cn.push({
12735 cls : 'input-group-addon',
12739 inputblock.cn.push(input);
12741 inputblock.cn.push({
12743 cls : 'input-group-addon',
12750 if (align ==='left' && this.fieldLabel.length) {
12751 Roo.log("left and has label");
12757 cls : 'control-label col-md-' + this.labelWidth,
12758 html : this.fieldLabel
12762 cls : "col-md-" + (12 - this.labelWidth),
12769 } else if ( this.fieldLabel.length) {
12776 cls: 'control-label box-input-label',
12777 //cls : 'input-group-addon',
12778 html : this.fieldLabel
12788 Roo.log(" no label && no align");
12803 html: this.boxLabel
12811 onClick : function()
12813 this.setChecked(true);
12816 setChecked : function(state,suppressEvent)
12819 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12820 v.dom.checked = false;
12824 this.checked = state;
12825 this.inputEl().dom.checked = state;
12827 if(suppressEvent !== true){
12828 this.fireEvent('check', this, state);
12831 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12835 getGroupValue : function()
12838 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12839 if(v.dom.checked == true){
12840 value = v.dom.value;
12848 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12849 * @return {Mixed} value The field value
12851 getValue : function(){
12852 return this.getGroupValue();
12858 //<script type="text/javascript">
12861 * Based Ext JS Library 1.1.1
12862 * Copyright(c) 2006-2007, Ext JS, LLC.
12868 * @class Roo.HtmlEditorCore
12869 * @extends Roo.Component
12870 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12872 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12875 Roo.HtmlEditorCore = function(config){
12878 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12881 * @event initialize
12882 * Fires when the editor is fully initialized (including the iframe)
12883 * @param {Roo.HtmlEditorCore} this
12888 * Fires when the editor is first receives the focus. Any insertion must wait
12889 * until after this event.
12890 * @param {Roo.HtmlEditorCore} this
12894 * @event beforesync
12895 * Fires before the textarea is updated with content from the editor iframe. Return false
12896 * to cancel the sync.
12897 * @param {Roo.HtmlEditorCore} this
12898 * @param {String} html
12902 * @event beforepush
12903 * Fires before the iframe editor is updated with content from the textarea. Return false
12904 * to cancel the push.
12905 * @param {Roo.HtmlEditorCore} this
12906 * @param {String} html
12911 * Fires when the textarea is updated with content from the editor iframe.
12912 * @param {Roo.HtmlEditorCore} this
12913 * @param {String} html
12918 * Fires when the iframe editor is updated with content from the textarea.
12919 * @param {Roo.HtmlEditorCore} this
12920 * @param {String} html
12925 * @event editorevent
12926 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12927 * @param {Roo.HtmlEditorCore} this
12935 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12939 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12945 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12950 * @cfg {Number} height (in pixels)
12954 * @cfg {Number} width (in pixels)
12959 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12962 stylesheets: false,
12967 // private properties
12968 validationEvent : false,
12970 initialized : false,
12972 sourceEditMode : false,
12973 onFocus : Roo.emptyFn,
12975 hideMode:'offsets',
12983 * Protected method that will not generally be called directly. It
12984 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12985 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12987 getDocMarkup : function(){
12990 Roo.log(this.stylesheets);
12992 // inherit styels from page...??
12993 if (this.stylesheets === false) {
12995 Roo.get(document.head).select('style').each(function(node) {
12996 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12999 Roo.get(document.head).select('link').each(function(node) {
13000 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13003 } else if (!this.stylesheets.length) {
13005 st = '<style type="text/css">' +
13006 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13009 Roo.each(this.stylesheets, function(s) {
13010 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13015 st += '<style type="text/css">' +
13016 'IMG { cursor: pointer } ' +
13020 return '<html><head>' + st +
13021 //<style type="text/css">' +
13022 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13024 ' </head><body class="roo-htmleditor-body"></body></html>';
13028 onRender : function(ct, position)
13031 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13032 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13035 this.el.dom.style.border = '0 none';
13036 this.el.dom.setAttribute('tabIndex', -1);
13037 this.el.addClass('x-hidden hide');
13041 if(Roo.isIE){ // fix IE 1px bogus margin
13042 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13046 this.frameId = Roo.id();
13050 var iframe = this.owner.wrap.createChild({
13052 cls: 'form-control', // bootstrap..
13054 name: this.frameId,
13055 frameBorder : 'no',
13056 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13061 this.iframe = iframe.dom;
13063 this.assignDocWin();
13065 this.doc.designMode = 'on';
13068 this.doc.write(this.getDocMarkup());
13072 var task = { // must defer to wait for browser to be ready
13074 //console.log("run task?" + this.doc.readyState);
13075 this.assignDocWin();
13076 if(this.doc.body || this.doc.readyState == 'complete'){
13078 this.doc.designMode="on";
13082 Roo.TaskMgr.stop(task);
13083 this.initEditor.defer(10, this);
13090 Roo.TaskMgr.start(task);
13097 onResize : function(w, h)
13099 Roo.log('resize: ' +w + ',' + h );
13100 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13104 if(typeof w == 'number'){
13106 this.iframe.style.width = w + 'px';
13108 if(typeof h == 'number'){
13110 this.iframe.style.height = h + 'px';
13112 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13119 * Toggles the editor between standard and source edit mode.
13120 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13122 toggleSourceEdit : function(sourceEditMode){
13124 this.sourceEditMode = sourceEditMode === true;
13126 if(this.sourceEditMode){
13128 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13131 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13132 //this.iframe.className = '';
13135 //this.setSize(this.owner.wrap.getSize());
13136 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13143 * Protected method that will not generally be called directly. If you need/want
13144 * custom HTML cleanup, this is the method you should override.
13145 * @param {String} html The HTML to be cleaned
13146 * return {String} The cleaned HTML
13148 cleanHtml : function(html){
13149 html = String(html);
13150 if(html.length > 5){
13151 if(Roo.isSafari){ // strip safari nonsense
13152 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13155 if(html == ' '){
13162 * HTML Editor -> Textarea
13163 * Protected method that will not generally be called directly. Syncs the contents
13164 * of the editor iframe with the textarea.
13166 syncValue : function(){
13167 if(this.initialized){
13168 var bd = (this.doc.body || this.doc.documentElement);
13169 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13170 var html = bd.innerHTML;
13172 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13173 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13175 html = '<div style="'+m[0]+'">' + html + '</div>';
13178 html = this.cleanHtml(html);
13179 // fix up the special chars.. normaly like back quotes in word...
13180 // however we do not want to do this with chinese..
13181 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13182 var cc = b.charCodeAt();
13184 (cc >= 0x4E00 && cc < 0xA000 ) ||
13185 (cc >= 0x3400 && cc < 0x4E00 ) ||
13186 (cc >= 0xf900 && cc < 0xfb00 )
13192 if(this.owner.fireEvent('beforesync', this, html) !== false){
13193 this.el.dom.value = html;
13194 this.owner.fireEvent('sync', this, html);
13200 * Protected method that will not generally be called directly. Pushes the value of the textarea
13201 * into the iframe editor.
13203 pushValue : function(){
13204 if(this.initialized){
13205 var v = this.el.dom.value.trim();
13207 // if(v.length < 1){
13211 if(this.owner.fireEvent('beforepush', this, v) !== false){
13212 var d = (this.doc.body || this.doc.documentElement);
13214 this.cleanUpPaste();
13215 this.el.dom.value = d.innerHTML;
13216 this.owner.fireEvent('push', this, v);
13222 deferFocus : function(){
13223 this.focus.defer(10, this);
13227 focus : function(){
13228 if(this.win && !this.sourceEditMode){
13235 assignDocWin: function()
13237 var iframe = this.iframe;
13240 this.doc = iframe.contentWindow.document;
13241 this.win = iframe.contentWindow;
13243 if (!Roo.get(this.frameId)) {
13246 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13247 this.win = Roo.get(this.frameId).dom.contentWindow;
13252 initEditor : function(){
13253 //console.log("INIT EDITOR");
13254 this.assignDocWin();
13258 this.doc.designMode="on";
13260 this.doc.write(this.getDocMarkup());
13263 var dbody = (this.doc.body || this.doc.documentElement);
13264 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13265 // this copies styles from the containing element into thsi one..
13266 // not sure why we need all of this..
13267 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13268 ss['background-attachment'] = 'fixed'; // w3c
13269 dbody.bgProperties = 'fixed'; // ie
13270 Roo.DomHelper.applyStyles(dbody, ss);
13271 Roo.EventManager.on(this.doc, {
13272 //'mousedown': this.onEditorEvent,
13273 'mouseup': this.onEditorEvent,
13274 'dblclick': this.onEditorEvent,
13275 'click': this.onEditorEvent,
13276 'keyup': this.onEditorEvent,
13281 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13283 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13284 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13286 this.initialized = true;
13288 this.owner.fireEvent('initialize', this);
13293 onDestroy : function(){
13299 //for (var i =0; i < this.toolbars.length;i++) {
13300 // // fixme - ask toolbars for heights?
13301 // this.toolbars[i].onDestroy();
13304 //this.wrap.dom.innerHTML = '';
13305 //this.wrap.remove();
13310 onFirstFocus : function(){
13312 this.assignDocWin();
13315 this.activated = true;
13318 if(Roo.isGecko){ // prevent silly gecko errors
13320 var s = this.win.getSelection();
13321 if(!s.focusNode || s.focusNode.nodeType != 3){
13322 var r = s.getRangeAt(0);
13323 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13328 this.execCmd('useCSS', true);
13329 this.execCmd('styleWithCSS', false);
13332 this.owner.fireEvent('activate', this);
13336 adjustFont: function(btn){
13337 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13338 //if(Roo.isSafari){ // safari
13341 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13342 if(Roo.isSafari){ // safari
13343 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13344 v = (v < 10) ? 10 : v;
13345 v = (v > 48) ? 48 : v;
13346 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13351 v = Math.max(1, v+adjust);
13353 this.execCmd('FontSize', v );
13356 onEditorEvent : function(e){
13357 this.owner.fireEvent('editorevent', this, e);
13358 // this.updateToolbar();
13359 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13362 insertTag : function(tg)
13364 // could be a bit smarter... -> wrap the current selected tRoo..
13365 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13367 range = this.createRange(this.getSelection());
13368 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13369 wrappingNode.appendChild(range.extractContents());
13370 range.insertNode(wrappingNode);
13377 this.execCmd("formatblock", tg);
13381 insertText : function(txt)
13385 var range = this.createRange();
13386 range.deleteContents();
13387 //alert(Sender.getAttribute('label'));
13389 range.insertNode(this.doc.createTextNode(txt));
13395 * Executes a Midas editor command on the editor document and performs necessary focus and
13396 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13397 * @param {String} cmd The Midas command
13398 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13400 relayCmd : function(cmd, value){
13402 this.execCmd(cmd, value);
13403 this.owner.fireEvent('editorevent', this);
13404 //this.updateToolbar();
13405 this.owner.deferFocus();
13409 * Executes a Midas editor command directly on the editor document.
13410 * For visual commands, you should use {@link #relayCmd} instead.
13411 * <b>This should only be called after the editor is initialized.</b>
13412 * @param {String} cmd The Midas command
13413 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13415 execCmd : function(cmd, value){
13416 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13423 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13425 * @param {String} text | dom node..
13427 insertAtCursor : function(text)
13432 if(!this.activated){
13438 var r = this.doc.selection.createRange();
13449 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13453 // from jquery ui (MIT licenced)
13455 var win = this.win;
13457 if (win.getSelection && win.getSelection().getRangeAt) {
13458 range = win.getSelection().getRangeAt(0);
13459 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13460 range.insertNode(node);
13461 } else if (win.document.selection && win.document.selection.createRange) {
13462 // no firefox support
13463 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13464 win.document.selection.createRange().pasteHTML(txt);
13466 // no firefox support
13467 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13468 this.execCmd('InsertHTML', txt);
13477 mozKeyPress : function(e){
13479 var c = e.getCharCode(), cmd;
13482 c = String.fromCharCode(c).toLowerCase();
13496 this.cleanUpPaste.defer(100, this);
13504 e.preventDefault();
13512 fixKeys : function(){ // load time branching for fastest keydown performance
13514 return function(e){
13515 var k = e.getKey(), r;
13518 r = this.doc.selection.createRange();
13521 r.pasteHTML('    ');
13528 r = this.doc.selection.createRange();
13530 var target = r.parentElement();
13531 if(!target || target.tagName.toLowerCase() != 'li'){
13533 r.pasteHTML('<br />');
13539 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13540 this.cleanUpPaste.defer(100, this);
13546 }else if(Roo.isOpera){
13547 return function(e){
13548 var k = e.getKey();
13552 this.execCmd('InsertHTML','    ');
13555 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13556 this.cleanUpPaste.defer(100, this);
13561 }else if(Roo.isSafari){
13562 return function(e){
13563 var k = e.getKey();
13567 this.execCmd('InsertText','\t');
13571 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13572 this.cleanUpPaste.defer(100, this);
13580 getAllAncestors: function()
13582 var p = this.getSelectedNode();
13585 a.push(p); // push blank onto stack..
13586 p = this.getParentElement();
13590 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13594 a.push(this.doc.body);
13598 lastSelNode : false,
13601 getSelection : function()
13603 this.assignDocWin();
13604 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13607 getSelectedNode: function()
13609 // this may only work on Gecko!!!
13611 // should we cache this!!!!
13616 var range = this.createRange(this.getSelection()).cloneRange();
13619 var parent = range.parentElement();
13621 var testRange = range.duplicate();
13622 testRange.moveToElementText(parent);
13623 if (testRange.inRange(range)) {
13626 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13629 parent = parent.parentElement;
13634 // is ancestor a text element.
13635 var ac = range.commonAncestorContainer;
13636 if (ac.nodeType == 3) {
13637 ac = ac.parentNode;
13640 var ar = ac.childNodes;
13643 var other_nodes = [];
13644 var has_other_nodes = false;
13645 for (var i=0;i<ar.length;i++) {
13646 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13649 // fullly contained node.
13651 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13656 // probably selected..
13657 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13658 other_nodes.push(ar[i]);
13662 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13667 has_other_nodes = true;
13669 if (!nodes.length && other_nodes.length) {
13670 nodes= other_nodes;
13672 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13678 createRange: function(sel)
13680 // this has strange effects when using with
13681 // top toolbar - not sure if it's a great idea.
13682 //this.editor.contentWindow.focus();
13683 if (typeof sel != "undefined") {
13685 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13687 return this.doc.createRange();
13690 return this.doc.createRange();
13693 getParentElement: function()
13696 this.assignDocWin();
13697 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13699 var range = this.createRange(sel);
13702 var p = range.commonAncestorContainer;
13703 while (p.nodeType == 3) { // text node
13714 * Range intersection.. the hard stuff...
13718 * [ -- selected range --- ]
13722 * if end is before start or hits it. fail.
13723 * if start is after end or hits it fail.
13725 * if either hits (but other is outside. - then it's not
13731 // @see http://www.thismuchiknow.co.uk/?p=64.
13732 rangeIntersectsNode : function(range, node)
13734 var nodeRange = node.ownerDocument.createRange();
13736 nodeRange.selectNode(node);
13738 nodeRange.selectNodeContents(node);
13741 var rangeStartRange = range.cloneRange();
13742 rangeStartRange.collapse(true);
13744 var rangeEndRange = range.cloneRange();
13745 rangeEndRange.collapse(false);
13747 var nodeStartRange = nodeRange.cloneRange();
13748 nodeStartRange.collapse(true);
13750 var nodeEndRange = nodeRange.cloneRange();
13751 nodeEndRange.collapse(false);
13753 return rangeStartRange.compareBoundaryPoints(
13754 Range.START_TO_START, nodeEndRange) == -1 &&
13755 rangeEndRange.compareBoundaryPoints(
13756 Range.START_TO_START, nodeStartRange) == 1;
13760 rangeCompareNode : function(range, node)
13762 var nodeRange = node.ownerDocument.createRange();
13764 nodeRange.selectNode(node);
13766 nodeRange.selectNodeContents(node);
13770 range.collapse(true);
13772 nodeRange.collapse(true);
13774 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13775 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13777 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13779 var nodeIsBefore = ss == 1;
13780 var nodeIsAfter = ee == -1;
13782 if (nodeIsBefore && nodeIsAfter)
13784 if (!nodeIsBefore && nodeIsAfter)
13785 return 1; //right trailed.
13787 if (nodeIsBefore && !nodeIsAfter)
13788 return 2; // left trailed.
13793 // private? - in a new class?
13794 cleanUpPaste : function()
13796 // cleans up the whole document..
13797 Roo.log('cleanuppaste');
13799 this.cleanUpChildren(this.doc.body);
13800 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13801 if (clean != this.doc.body.innerHTML) {
13802 this.doc.body.innerHTML = clean;
13807 cleanWordChars : function(input) {// change the chars to hex code
13808 var he = Roo.HtmlEditorCore;
13810 var output = input;
13811 Roo.each(he.swapCodes, function(sw) {
13812 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13814 output = output.replace(swapper, sw[1]);
13821 cleanUpChildren : function (n)
13823 if (!n.childNodes.length) {
13826 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13827 this.cleanUpChild(n.childNodes[i]);
13834 cleanUpChild : function (node)
13837 //console.log(node);
13838 if (node.nodeName == "#text") {
13839 // clean up silly Windows -- stuff?
13842 if (node.nodeName == "#comment") {
13843 node.parentNode.removeChild(node);
13844 // clean up silly Windows -- stuff?
13848 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13850 node.parentNode.removeChild(node);
13855 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13857 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13858 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13860 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13861 // remove_keep_children = true;
13864 if (remove_keep_children) {
13865 this.cleanUpChildren(node);
13866 // inserts everything just before this node...
13867 while (node.childNodes.length) {
13868 var cn = node.childNodes[0];
13869 node.removeChild(cn);
13870 node.parentNode.insertBefore(cn, node);
13872 node.parentNode.removeChild(node);
13876 if (!node.attributes || !node.attributes.length) {
13877 this.cleanUpChildren(node);
13881 function cleanAttr(n,v)
13884 if (v.match(/^\./) || v.match(/^\//)) {
13887 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13890 if (v.match(/^#/)) {
13893 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13894 node.removeAttribute(n);
13898 function cleanStyle(n,v)
13900 if (v.match(/expression/)) { //XSS?? should we even bother..
13901 node.removeAttribute(n);
13904 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13905 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13908 var parts = v.split(/;/);
13911 Roo.each(parts, function(p) {
13912 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13916 var l = p.split(':').shift().replace(/\s+/g,'');
13917 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13919 if ( cblack.indexOf(l) > -1) {
13920 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13921 //node.removeAttribute(n);
13925 // only allow 'c whitelisted system attributes'
13926 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13927 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13928 //node.removeAttribute(n);
13938 if (clean.length) {
13939 node.setAttribute(n, clean.join(';'));
13941 node.removeAttribute(n);
13947 for (var i = node.attributes.length-1; i > -1 ; i--) {
13948 var a = node.attributes[i];
13951 if (a.name.toLowerCase().substr(0,2)=='on') {
13952 node.removeAttribute(a.name);
13955 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13956 node.removeAttribute(a.name);
13959 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13960 cleanAttr(a.name,a.value); // fixme..
13963 if (a.name == 'style') {
13964 cleanStyle(a.name,a.value);
13967 /// clean up MS crap..
13968 // tecnically this should be a list of valid class'es..
13971 if (a.name == 'class') {
13972 if (a.value.match(/^Mso/)) {
13973 node.className = '';
13976 if (a.value.match(/body/)) {
13977 node.className = '';
13988 this.cleanUpChildren(node);
13994 // hide stuff that is not compatible
14008 * @event specialkey
14012 * @cfg {String} fieldClass @hide
14015 * @cfg {String} focusClass @hide
14018 * @cfg {String} autoCreate @hide
14021 * @cfg {String} inputType @hide
14024 * @cfg {String} invalidClass @hide
14027 * @cfg {String} invalidText @hide
14030 * @cfg {String} msgFx @hide
14033 * @cfg {String} validateOnBlur @hide
14037 Roo.HtmlEditorCore.white = [
14038 'area', 'br', 'img', 'input', 'hr', 'wbr',
14040 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14041 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14042 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14043 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14044 'table', 'ul', 'xmp',
14046 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14049 'dir', 'menu', 'ol', 'ul', 'dl',
14055 Roo.HtmlEditorCore.black = [
14056 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14058 'base', 'basefont', 'bgsound', 'blink', 'body',
14059 'frame', 'frameset', 'head', 'html', 'ilayer',
14060 'iframe', 'layer', 'link', 'meta', 'object',
14061 'script', 'style' ,'title', 'xml' // clean later..
14063 Roo.HtmlEditorCore.clean = [
14064 'script', 'style', 'title', 'xml'
14066 Roo.HtmlEditorCore.remove = [
14071 Roo.HtmlEditorCore.ablack = [
14075 Roo.HtmlEditorCore.aclean = [
14076 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14080 Roo.HtmlEditorCore.pwhite= [
14081 'http', 'https', 'mailto'
14084 // white listed style attributes.
14085 Roo.HtmlEditorCore.cwhite= [
14086 // 'text-align', /// default is to allow most things..
14092 // black listed style attributes.
14093 Roo.HtmlEditorCore.cblack= [
14094 // 'font-size' -- this can be set by the project
14098 Roo.HtmlEditorCore.swapCodes =[
14117 * @class Roo.bootstrap.HtmlEditor
14118 * @extends Roo.bootstrap.TextArea
14119 * Bootstrap HtmlEditor class
14122 * Create a new HtmlEditor
14123 * @param {Object} config The config object
14126 Roo.bootstrap.HtmlEditor = function(config){
14127 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14128 if (!this.toolbars) {
14129 this.toolbars = [];
14131 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14134 * @event initialize
14135 * Fires when the editor is fully initialized (including the iframe)
14136 * @param {HtmlEditor} this
14141 * Fires when the editor is first receives the focus. Any insertion must wait
14142 * until after this event.
14143 * @param {HtmlEditor} this
14147 * @event beforesync
14148 * Fires before the textarea is updated with content from the editor iframe. Return false
14149 * to cancel the sync.
14150 * @param {HtmlEditor} this
14151 * @param {String} html
14155 * @event beforepush
14156 * Fires before the iframe editor is updated with content from the textarea. Return false
14157 * to cancel the push.
14158 * @param {HtmlEditor} this
14159 * @param {String} html
14164 * Fires when the textarea is updated with content from the editor iframe.
14165 * @param {HtmlEditor} this
14166 * @param {String} html
14171 * Fires when the iframe editor is updated with content from the textarea.
14172 * @param {HtmlEditor} this
14173 * @param {String} html
14177 * @event editmodechange
14178 * Fires when the editor switches edit modes
14179 * @param {HtmlEditor} this
14180 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14182 editmodechange: true,
14184 * @event editorevent
14185 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14186 * @param {HtmlEditor} this
14190 * @event firstfocus
14191 * Fires when on first focus - needed by toolbars..
14192 * @param {HtmlEditor} this
14197 * Auto save the htmlEditor value as a file into Events
14198 * @param {HtmlEditor} this
14202 * @event savedpreview
14203 * preview the saved version of htmlEditor
14204 * @param {HtmlEditor} this
14211 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14215 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14220 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14225 * @cfg {Number} height (in pixels)
14229 * @cfg {Number} width (in pixels)
14234 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14237 stylesheets: false,
14242 // private properties
14243 validationEvent : false,
14245 initialized : false,
14248 onFocus : Roo.emptyFn,
14250 hideMode:'offsets',
14253 tbContainer : false,
14255 toolbarContainer :function() {
14256 return this.wrap.select('.x-html-editor-tb',true).first();
14260 * Protected method that will not generally be called directly. It
14261 * is called when the editor creates its toolbar. Override this method if you need to
14262 * add custom toolbar buttons.
14263 * @param {HtmlEditor} editor
14265 createToolbar : function(){
14267 Roo.log("create toolbars");
14269 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14270 this.toolbars[0].render(this.toolbarContainer());
14274 // if (!editor.toolbars || !editor.toolbars.length) {
14275 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14278 // for (var i =0 ; i < editor.toolbars.length;i++) {
14279 // editor.toolbars[i] = Roo.factory(
14280 // typeof(editor.toolbars[i]) == 'string' ?
14281 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14282 // Roo.bootstrap.HtmlEditor);
14283 // editor.toolbars[i].init(editor);
14289 onRender : function(ct, position)
14291 // Roo.log("Call onRender: " + this.xtype);
14293 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14295 this.wrap = this.inputEl().wrap({
14296 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14299 this.editorcore.onRender(ct, position);
14301 if (this.resizable) {
14302 this.resizeEl = new Roo.Resizable(this.wrap, {
14306 minHeight : this.height,
14307 height: this.height,
14308 handles : this.resizable,
14311 resize : function(r, w, h) {
14312 _t.onResize(w,h); // -something
14318 this.createToolbar(this);
14321 if(!this.width && this.resizable){
14322 this.setSize(this.wrap.getSize());
14324 if (this.resizeEl) {
14325 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14326 // should trigger onReize..
14332 onResize : function(w, h)
14334 Roo.log('resize: ' +w + ',' + h );
14335 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14339 if(this.inputEl() ){
14340 if(typeof w == 'number'){
14341 var aw = w - this.wrap.getFrameWidth('lr');
14342 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14345 if(typeof h == 'number'){
14346 var tbh = -11; // fixme it needs to tool bar size!
14347 for (var i =0; i < this.toolbars.length;i++) {
14348 // fixme - ask toolbars for heights?
14349 tbh += this.toolbars[i].el.getHeight();
14350 //if (this.toolbars[i].footer) {
14351 // tbh += this.toolbars[i].footer.el.getHeight();
14359 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14360 ah -= 5; // knock a few pixes off for look..
14361 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14365 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14366 this.editorcore.onResize(ew,eh);
14371 * Toggles the editor between standard and source edit mode.
14372 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14374 toggleSourceEdit : function(sourceEditMode)
14376 this.editorcore.toggleSourceEdit(sourceEditMode);
14378 if(this.editorcore.sourceEditMode){
14379 Roo.log('editor - showing textarea');
14382 // Roo.log(this.syncValue());
14384 this.inputEl().removeClass('hide');
14385 this.inputEl().dom.removeAttribute('tabIndex');
14386 this.inputEl().focus();
14388 Roo.log('editor - hiding textarea');
14390 // Roo.log(this.pushValue());
14393 this.inputEl().addClass('hide');
14394 this.inputEl().dom.setAttribute('tabIndex', -1);
14395 //this.deferFocus();
14398 if(this.resizable){
14399 this.setSize(this.wrap.getSize());
14402 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14405 // private (for BoxComponent)
14406 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14408 // private (for BoxComponent)
14409 getResizeEl : function(){
14413 // private (for BoxComponent)
14414 getPositionEl : function(){
14419 initEvents : function(){
14420 this.originalValue = this.getValue();
14424 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14427 // markInvalid : Roo.emptyFn,
14429 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14432 // clearInvalid : Roo.emptyFn,
14434 setValue : function(v){
14435 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14436 this.editorcore.pushValue();
14441 deferFocus : function(){
14442 this.focus.defer(10, this);
14446 focus : function(){
14447 this.editorcore.focus();
14453 onDestroy : function(){
14459 for (var i =0; i < this.toolbars.length;i++) {
14460 // fixme - ask toolbars for heights?
14461 this.toolbars[i].onDestroy();
14464 this.wrap.dom.innerHTML = '';
14465 this.wrap.remove();
14470 onFirstFocus : function(){
14471 //Roo.log("onFirstFocus");
14472 this.editorcore.onFirstFocus();
14473 for (var i =0; i < this.toolbars.length;i++) {
14474 this.toolbars[i].onFirstFocus();
14480 syncValue : function()
14482 this.editorcore.syncValue();
14485 pushValue : function()
14487 this.editorcore.pushValue();
14491 // hide stuff that is not compatible
14505 * @event specialkey
14509 * @cfg {String} fieldClass @hide
14512 * @cfg {String} focusClass @hide
14515 * @cfg {String} autoCreate @hide
14518 * @cfg {String} inputType @hide
14521 * @cfg {String} invalidClass @hide
14524 * @cfg {String} invalidText @hide
14527 * @cfg {String} msgFx @hide
14530 * @cfg {String} validateOnBlur @hide
14541 * @class Roo.bootstrap.HtmlEditorToolbar1
14546 new Roo.bootstrap.HtmlEditor({
14549 new Roo.bootstrap.HtmlEditorToolbar1({
14550 disable : { fonts: 1 , format: 1, ..., ... , ...],
14556 * @cfg {Object} disable List of elements to disable..
14557 * @cfg {Array} btns List of additional buttons.
14561 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14564 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14567 Roo.apply(this, config);
14569 // default disabled, based on 'good practice'..
14570 this.disable = this.disable || {};
14571 Roo.applyIf(this.disable, {
14574 specialElements : true
14576 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14578 this.editor = config.editor;
14579 this.editorcore = config.editor.editorcore;
14581 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14583 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14584 // dont call parent... till later.
14586 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14592 editorcore : false,
14597 "h1","h2","h3","h4","h5","h6",
14599 "abbr", "acronym", "address", "cite", "samp", "var",
14603 onRender : function(ct, position)
14605 // Roo.log("Call onRender: " + this.xtype);
14607 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14609 this.el.dom.style.marginBottom = '0';
14611 var editorcore = this.editorcore;
14612 var editor= this.editor;
14615 var btn = function(id,cmd , toggle, handler){
14617 var event = toggle ? 'toggle' : 'click';
14622 xns: Roo.bootstrap,
14625 enableToggle:toggle !== false,
14627 pressed : toggle ? false : null,
14630 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14631 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14640 xns: Roo.bootstrap,
14641 glyphicon : 'font',
14645 xns: Roo.bootstrap,
14649 Roo.each(this.formats, function(f) {
14650 style.menu.items.push({
14652 xns: Roo.bootstrap,
14653 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14658 editorcore.insertTag(this.tagname);
14665 children.push(style);
14668 btn('bold',false,true);
14669 btn('italic',false,true);
14670 btn('align-left', 'justifyleft',true);
14671 btn('align-center', 'justifycenter',true);
14672 btn('align-right' , 'justifyright',true);
14673 btn('link', false, false, function(btn) {
14674 //Roo.log("create link?");
14675 var url = prompt(this.createLinkText, this.defaultLinkValue);
14676 if(url && url != 'http:/'+'/'){
14677 this.editorcore.relayCmd('createlink', url);
14680 btn('list','insertunorderedlist',true);
14681 btn('pencil', false,true, function(btn){
14684 this.toggleSourceEdit(btn.pressed);
14690 xns: Roo.bootstrap,
14695 xns: Roo.bootstrap,
14700 cog.menu.items.push({
14702 xns: Roo.bootstrap,
14703 html : Clean styles,
14708 editorcore.insertTag(this.tagname);
14717 this.xtype = 'Navbar';
14719 for(var i=0;i< children.length;i++) {
14721 this.buttons.add(this.addxtypeChild(children[i]));
14725 editor.on('editorevent', this.updateToolbar, this);
14727 onBtnClick : function(id)
14729 this.editorcore.relayCmd(id);
14730 this.editorcore.focus();
14734 * Protected method that will not generally be called directly. It triggers
14735 * a toolbar update by reading the markup state of the current selection in the editor.
14737 updateToolbar: function(){
14739 if(!this.editorcore.activated){
14740 this.editor.onFirstFocus(); // is this neeed?
14744 var btns = this.buttons;
14745 var doc = this.editorcore.doc;
14746 btns.get('bold').setActive(doc.queryCommandState('bold'));
14747 btns.get('italic').setActive(doc.queryCommandState('italic'));
14748 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14750 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14751 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14752 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14754 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14755 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14758 var ans = this.editorcore.getAllAncestors();
14759 if (this.formatCombo) {
14762 var store = this.formatCombo.store;
14763 this.formatCombo.setValue("");
14764 for (var i =0; i < ans.length;i++) {
14765 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14767 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14775 // hides menus... - so this cant be on a menu...
14776 Roo.bootstrap.MenuMgr.hideAll();
14778 Roo.bootstrap.MenuMgr.hideAll();
14779 //this.editorsyncValue();
14781 onFirstFocus: function() {
14782 this.buttons.each(function(item){
14786 toggleSourceEdit : function(sourceEditMode){
14789 if(sourceEditMode){
14790 Roo.log("disabling buttons");
14791 this.buttons.each( function(item){
14792 if(item.cmd != 'pencil'){
14798 Roo.log("enabling buttons");
14799 if(this.editorcore.initialized){
14800 this.buttons.each( function(item){
14806 Roo.log("calling toggole on editor");
14807 // tell the editor that it's been pressed..
14808 this.editor.toggleSourceEdit(sourceEditMode);
14818 * @class Roo.bootstrap.Table.AbstractSelectionModel
14819 * @extends Roo.util.Observable
14820 * Abstract base class for grid SelectionModels. It provides the interface that should be
14821 * implemented by descendant classes. This class should not be directly instantiated.
14824 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14825 this.locked = false;
14826 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14830 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14831 /** @ignore Called by the grid automatically. Do not call directly. */
14832 init : function(grid){
14838 * Locks the selections.
14841 this.locked = true;
14845 * Unlocks the selections.
14847 unlock : function(){
14848 this.locked = false;
14852 * Returns true if the selections are locked.
14853 * @return {Boolean}
14855 isLocked : function(){
14856 return this.locked;
14860 * @class Roo.bootstrap.Table.ColumnModel
14861 * @extends Roo.util.Observable
14862 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14863 * the columns in the table.
14866 * @param {Object} config An Array of column config objects. See this class's
14867 * config objects for details.
14869 Roo.bootstrap.Table.ColumnModel = function(config){
14871 * The config passed into the constructor
14873 this.config = config;
14876 // if no id, create one
14877 // if the column does not have a dataIndex mapping,
14878 // map it to the order it is in the config
14879 for(var i = 0, len = config.length; i < len; i++){
14881 if(typeof c.dataIndex == "undefined"){
14884 if(typeof c.renderer == "string"){
14885 c.renderer = Roo.util.Format[c.renderer];
14887 if(typeof c.id == "undefined"){
14890 // if(c.editor && c.editor.xtype){
14891 // c.editor = Roo.factory(c.editor, Roo.grid);
14893 // if(c.editor && c.editor.isFormField){
14894 // c.editor = new Roo.grid.GridEditor(c.editor);
14897 this.lookup[c.id] = c;
14901 * The width of columns which have no width specified (defaults to 100)
14904 this.defaultWidth = 100;
14907 * Default sortable of columns which have no sortable specified (defaults to false)
14910 this.defaultSortable = false;
14914 * @event widthchange
14915 * Fires when the width of a column changes.
14916 * @param {ColumnModel} this
14917 * @param {Number} columnIndex The column index
14918 * @param {Number} newWidth The new width
14920 "widthchange": true,
14922 * @event headerchange
14923 * Fires when the text of a header changes.
14924 * @param {ColumnModel} this
14925 * @param {Number} columnIndex The column index
14926 * @param {Number} newText The new header text
14928 "headerchange": true,
14930 * @event hiddenchange
14931 * Fires when a column is hidden or "unhidden".
14932 * @param {ColumnModel} this
14933 * @param {Number} columnIndex The column index
14934 * @param {Boolean} hidden true if hidden, false otherwise
14936 "hiddenchange": true,
14938 * @event columnmoved
14939 * Fires when a column is moved.
14940 * @param {ColumnModel} this
14941 * @param {Number} oldIndex
14942 * @param {Number} newIndex
14944 "columnmoved" : true,
14946 * @event columlockchange
14947 * Fires when a column's locked state is changed
14948 * @param {ColumnModel} this
14949 * @param {Number} colIndex
14950 * @param {Boolean} locked true if locked
14952 "columnlockchange" : true
14954 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14956 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14958 * @cfg {String} header The header text to display in the Grid view.
14961 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14962 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14963 * specified, the column's index is used as an index into the Record's data Array.
14966 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14967 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14970 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14971 * Defaults to the value of the {@link #defaultSortable} property.
14972 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14975 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14978 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14981 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14984 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14987 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14988 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14989 * default renderer uses the raw data value.
14992 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14996 * Returns the id of the column at the specified index.
14997 * @param {Number} index The column index
14998 * @return {String} the id
15000 getColumnId : function(index){
15001 return this.config[index].id;
15005 * Returns the column for a specified id.
15006 * @param {String} id The column id
15007 * @return {Object} the column
15009 getColumnById : function(id){
15010 return this.lookup[id];
15015 * Returns the column for a specified dataIndex.
15016 * @param {String} dataIndex The column dataIndex
15017 * @return {Object|Boolean} the column or false if not found
15019 getColumnByDataIndex: function(dataIndex){
15020 var index = this.findColumnIndex(dataIndex);
15021 return index > -1 ? this.config[index] : false;
15025 * Returns the index for a specified column id.
15026 * @param {String} id The column id
15027 * @return {Number} the index, or -1 if not found
15029 getIndexById : function(id){
15030 for(var i = 0, len = this.config.length; i < len; i++){
15031 if(this.config[i].id == id){
15039 * Returns the index for a specified column dataIndex.
15040 * @param {String} dataIndex The column dataIndex
15041 * @return {Number} the index, or -1 if not found
15044 findColumnIndex : function(dataIndex){
15045 for(var i = 0, len = this.config.length; i < len; i++){
15046 if(this.config[i].dataIndex == dataIndex){
15054 moveColumn : function(oldIndex, newIndex){
15055 var c = this.config[oldIndex];
15056 this.config.splice(oldIndex, 1);
15057 this.config.splice(newIndex, 0, c);
15058 this.dataMap = null;
15059 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15062 isLocked : function(colIndex){
15063 return this.config[colIndex].locked === true;
15066 setLocked : function(colIndex, value, suppressEvent){
15067 if(this.isLocked(colIndex) == value){
15070 this.config[colIndex].locked = value;
15071 if(!suppressEvent){
15072 this.fireEvent("columnlockchange", this, colIndex, value);
15076 getTotalLockedWidth : function(){
15077 var totalWidth = 0;
15078 for(var i = 0; i < this.config.length; i++){
15079 if(this.isLocked(i) && !this.isHidden(i)){
15080 this.totalWidth += this.getColumnWidth(i);
15086 getLockedCount : function(){
15087 for(var i = 0, len = this.config.length; i < len; i++){
15088 if(!this.isLocked(i)){
15095 * Returns the number of columns.
15098 getColumnCount : function(visibleOnly){
15099 if(visibleOnly === true){
15101 for(var i = 0, len = this.config.length; i < len; i++){
15102 if(!this.isHidden(i)){
15108 return this.config.length;
15112 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15113 * @param {Function} fn
15114 * @param {Object} scope (optional)
15115 * @return {Array} result
15117 getColumnsBy : function(fn, scope){
15119 for(var i = 0, len = this.config.length; i < len; i++){
15120 var c = this.config[i];
15121 if(fn.call(scope||this, c, i) === true){
15129 * Returns true if the specified column is sortable.
15130 * @param {Number} col The column index
15131 * @return {Boolean}
15133 isSortable : function(col){
15134 if(typeof this.config[col].sortable == "undefined"){
15135 return this.defaultSortable;
15137 return this.config[col].sortable;
15141 * Returns the rendering (formatting) function defined for the column.
15142 * @param {Number} col The column index.
15143 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15145 getRenderer : function(col){
15146 if(!this.config[col].renderer){
15147 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15149 return this.config[col].renderer;
15153 * Sets the rendering (formatting) function for a column.
15154 * @param {Number} col The column index
15155 * @param {Function} fn The function to use to process the cell's raw data
15156 * to return HTML markup for the grid view. The render function is called with
15157 * the following parameters:<ul>
15158 * <li>Data value.</li>
15159 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15160 * <li>css A CSS style string to apply to the table cell.</li>
15161 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15162 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15163 * <li>Row index</li>
15164 * <li>Column index</li>
15165 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15167 setRenderer : function(col, fn){
15168 this.config[col].renderer = fn;
15172 * Returns the width for the specified column.
15173 * @param {Number} col The column index
15176 getColumnWidth : function(col){
15177 return this.config[col].width * 1 || this.defaultWidth;
15181 * Sets the width for a column.
15182 * @param {Number} col The column index
15183 * @param {Number} width The new width
15185 setColumnWidth : function(col, width, suppressEvent){
15186 this.config[col].width = width;
15187 this.totalWidth = null;
15188 if(!suppressEvent){
15189 this.fireEvent("widthchange", this, col, width);
15194 * Returns the total width of all columns.
15195 * @param {Boolean} includeHidden True to include hidden column widths
15198 getTotalWidth : function(includeHidden){
15199 if(!this.totalWidth){
15200 this.totalWidth = 0;
15201 for(var i = 0, len = this.config.length; i < len; i++){
15202 if(includeHidden || !this.isHidden(i)){
15203 this.totalWidth += this.getColumnWidth(i);
15207 return this.totalWidth;
15211 * Returns the header for the specified column.
15212 * @param {Number} col The column index
15215 getColumnHeader : function(col){
15216 return this.config[col].header;
15220 * Sets the header for a column.
15221 * @param {Number} col The column index
15222 * @param {String} header The new header
15224 setColumnHeader : function(col, header){
15225 this.config[col].header = header;
15226 this.fireEvent("headerchange", this, col, header);
15230 * Returns the tooltip for the specified column.
15231 * @param {Number} col The column index
15234 getColumnTooltip : function(col){
15235 return this.config[col].tooltip;
15238 * Sets the tooltip for a column.
15239 * @param {Number} col The column index
15240 * @param {String} tooltip The new tooltip
15242 setColumnTooltip : function(col, tooltip){
15243 this.config[col].tooltip = tooltip;
15247 * Returns the dataIndex for the specified column.
15248 * @param {Number} col The column index
15251 getDataIndex : function(col){
15252 return this.config[col].dataIndex;
15256 * Sets the dataIndex for a column.
15257 * @param {Number} col The column index
15258 * @param {Number} dataIndex The new dataIndex
15260 setDataIndex : function(col, dataIndex){
15261 this.config[col].dataIndex = dataIndex;
15267 * Returns true if the cell is editable.
15268 * @param {Number} colIndex The column index
15269 * @param {Number} rowIndex The row index
15270 * @return {Boolean}
15272 isCellEditable : function(colIndex, rowIndex){
15273 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15277 * Returns the editor defined for the cell/column.
15278 * return false or null to disable editing.
15279 * @param {Number} colIndex The column index
15280 * @param {Number} rowIndex The row index
15283 getCellEditor : function(colIndex, rowIndex){
15284 return this.config[colIndex].editor;
15288 * Sets if a column is editable.
15289 * @param {Number} col The column index
15290 * @param {Boolean} editable True if the column is editable
15292 setEditable : function(col, editable){
15293 this.config[col].editable = editable;
15298 * Returns true if the column is hidden.
15299 * @param {Number} colIndex The column index
15300 * @return {Boolean}
15302 isHidden : function(colIndex){
15303 return this.config[colIndex].hidden;
15308 * Returns true if the column width cannot be changed
15310 isFixed : function(colIndex){
15311 return this.config[colIndex].fixed;
15315 * Returns true if the column can be resized
15316 * @return {Boolean}
15318 isResizable : function(colIndex){
15319 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15322 * Sets if a column is hidden.
15323 * @param {Number} colIndex The column index
15324 * @param {Boolean} hidden True if the column is hidden
15326 setHidden : function(colIndex, hidden){
15327 this.config[colIndex].hidden = hidden;
15328 this.totalWidth = null;
15329 this.fireEvent("hiddenchange", this, colIndex, hidden);
15333 * Sets the editor for a column.
15334 * @param {Number} col The column index
15335 * @param {Object} editor The editor object
15337 setEditor : function(col, editor){
15338 this.config[col].editor = editor;
15342 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15343 if(typeof value == "string" && value.length < 1){
15349 // Alias for backwards compatibility
15350 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15353 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15354 * @class Roo.bootstrap.Table.RowSelectionModel
15355 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15356 * It supports multiple selections and keyboard selection/navigation.
15358 * @param {Object} config
15361 Roo.bootstrap.Table.RowSelectionModel = function(config){
15362 Roo.apply(this, config);
15363 this.selections = new Roo.util.MixedCollection(false, function(o){
15368 this.lastActive = false;
15372 * @event selectionchange
15373 * Fires when the selection changes
15374 * @param {SelectionModel} this
15376 "selectionchange" : true,
15378 * @event afterselectionchange
15379 * Fires after the selection changes (eg. by key press or clicking)
15380 * @param {SelectionModel} this
15382 "afterselectionchange" : true,
15384 * @event beforerowselect
15385 * Fires when a row is selected being selected, return false to cancel.
15386 * @param {SelectionModel} this
15387 * @param {Number} rowIndex The selected index
15388 * @param {Boolean} keepExisting False if other selections will be cleared
15390 "beforerowselect" : true,
15393 * Fires when a row is selected.
15394 * @param {SelectionModel} this
15395 * @param {Number} rowIndex The selected index
15396 * @param {Roo.data.Record} r The record
15398 "rowselect" : true,
15400 * @event rowdeselect
15401 * Fires when a row is deselected.
15402 * @param {SelectionModel} this
15403 * @param {Number} rowIndex The selected index
15405 "rowdeselect" : true
15407 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15408 this.locked = false;
15411 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15413 * @cfg {Boolean} singleSelect
15414 * True to allow selection of only one row at a time (defaults to false)
15416 singleSelect : false,
15419 initEvents : function(){
15421 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15422 this.grid.on("mousedown", this.handleMouseDown, this);
15423 }else{ // allow click to work like normal
15424 this.grid.on("rowclick", this.handleDragableRowClick, this);
15427 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15428 "up" : function(e){
15430 this.selectPrevious(e.shiftKey);
15431 }else if(this.last !== false && this.lastActive !== false){
15432 var last = this.last;
15433 this.selectRange(this.last, this.lastActive-1);
15434 this.grid.getView().focusRow(this.lastActive);
15435 if(last !== false){
15439 this.selectFirstRow();
15441 this.fireEvent("afterselectionchange", this);
15443 "down" : function(e){
15445 this.selectNext(e.shiftKey);
15446 }else if(this.last !== false && this.lastActive !== false){
15447 var last = this.last;
15448 this.selectRange(this.last, this.lastActive+1);
15449 this.grid.getView().focusRow(this.lastActive);
15450 if(last !== false){
15454 this.selectFirstRow();
15456 this.fireEvent("afterselectionchange", this);
15461 var view = this.grid.view;
15462 view.on("refresh", this.onRefresh, this);
15463 view.on("rowupdated", this.onRowUpdated, this);
15464 view.on("rowremoved", this.onRemove, this);
15468 onRefresh : function(){
15469 var ds = this.grid.dataSource, i, v = this.grid.view;
15470 var s = this.selections;
15471 s.each(function(r){
15472 if((i = ds.indexOfId(r.id)) != -1){
15481 onRemove : function(v, index, r){
15482 this.selections.remove(r);
15486 onRowUpdated : function(v, index, r){
15487 if(this.isSelected(r)){
15488 v.onRowSelect(index);
15494 * @param {Array} records The records to select
15495 * @param {Boolean} keepExisting (optional) True to keep existing selections
15497 selectRecords : function(records, keepExisting){
15499 this.clearSelections();
15501 var ds = this.grid.dataSource;
15502 for(var i = 0, len = records.length; i < len; i++){
15503 this.selectRow(ds.indexOf(records[i]), true);
15508 * Gets the number of selected rows.
15511 getCount : function(){
15512 return this.selections.length;
15516 * Selects the first row in the grid.
15518 selectFirstRow : function(){
15523 * Select the last row.
15524 * @param {Boolean} keepExisting (optional) True to keep existing selections
15526 selectLastRow : function(keepExisting){
15527 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15531 * Selects the row immediately following the last selected row.
15532 * @param {Boolean} keepExisting (optional) True to keep existing selections
15534 selectNext : function(keepExisting){
15535 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15536 this.selectRow(this.last+1, keepExisting);
15537 this.grid.getView().focusRow(this.last);
15542 * Selects the row that precedes the last selected row.
15543 * @param {Boolean} keepExisting (optional) True to keep existing selections
15545 selectPrevious : function(keepExisting){
15547 this.selectRow(this.last-1, keepExisting);
15548 this.grid.getView().focusRow(this.last);
15553 * Returns the selected records
15554 * @return {Array} Array of selected records
15556 getSelections : function(){
15557 return [].concat(this.selections.items);
15561 * Returns the first selected record.
15564 getSelected : function(){
15565 return this.selections.itemAt(0);
15570 * Clears all selections.
15572 clearSelections : function(fast){
15573 if(this.locked) return;
15575 var ds = this.grid.dataSource;
15576 var s = this.selections;
15577 s.each(function(r){
15578 this.deselectRow(ds.indexOfId(r.id));
15582 this.selections.clear();
15589 * Selects all rows.
15591 selectAll : function(){
15592 if(this.locked) return;
15593 this.selections.clear();
15594 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15595 this.selectRow(i, true);
15600 * Returns True if there is a selection.
15601 * @return {Boolean}
15603 hasSelection : function(){
15604 return this.selections.length > 0;
15608 * Returns True if the specified row is selected.
15609 * @param {Number/Record} record The record or index of the record to check
15610 * @return {Boolean}
15612 isSelected : function(index){
15613 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15614 return (r && this.selections.key(r.id) ? true : false);
15618 * Returns True if the specified record id is selected.
15619 * @param {String} id The id of record to check
15620 * @return {Boolean}
15622 isIdSelected : function(id){
15623 return (this.selections.key(id) ? true : false);
15627 handleMouseDown : function(e, t){
15628 var view = this.grid.getView(), rowIndex;
15629 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15632 if(e.shiftKey && this.last !== false){
15633 var last = this.last;
15634 this.selectRange(last, rowIndex, e.ctrlKey);
15635 this.last = last; // reset the last
15636 view.focusRow(rowIndex);
15638 var isSelected = this.isSelected(rowIndex);
15639 if(e.button !== 0 && isSelected){
15640 view.focusRow(rowIndex);
15641 }else if(e.ctrlKey && isSelected){
15642 this.deselectRow(rowIndex);
15643 }else if(!isSelected){
15644 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15645 view.focusRow(rowIndex);
15648 this.fireEvent("afterselectionchange", this);
15651 handleDragableRowClick : function(grid, rowIndex, e)
15653 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15654 this.selectRow(rowIndex, false);
15655 grid.view.focusRow(rowIndex);
15656 this.fireEvent("afterselectionchange", this);
15661 * Selects multiple rows.
15662 * @param {Array} rows Array of the indexes of the row to select
15663 * @param {Boolean} keepExisting (optional) True to keep existing selections
15665 selectRows : function(rows, keepExisting){
15667 this.clearSelections();
15669 for(var i = 0, len = rows.length; i < len; i++){
15670 this.selectRow(rows[i], true);
15675 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15676 * @param {Number} startRow The index of the first row in the range
15677 * @param {Number} endRow The index of the last row in the range
15678 * @param {Boolean} keepExisting (optional) True to retain existing selections
15680 selectRange : function(startRow, endRow, keepExisting){
15681 if(this.locked) return;
15683 this.clearSelections();
15685 if(startRow <= endRow){
15686 for(var i = startRow; i <= endRow; i++){
15687 this.selectRow(i, true);
15690 for(var i = startRow; i >= endRow; i--){
15691 this.selectRow(i, true);
15697 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15698 * @param {Number} startRow The index of the first row in the range
15699 * @param {Number} endRow The index of the last row in the range
15701 deselectRange : function(startRow, endRow, preventViewNotify){
15702 if(this.locked) return;
15703 for(var i = startRow; i <= endRow; i++){
15704 this.deselectRow(i, preventViewNotify);
15710 * @param {Number} row The index of the row to select
15711 * @param {Boolean} keepExisting (optional) True to keep existing selections
15713 selectRow : function(index, keepExisting, preventViewNotify){
15714 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15715 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15716 if(!keepExisting || this.singleSelect){
15717 this.clearSelections();
15719 var r = this.grid.dataSource.getAt(index);
15720 this.selections.add(r);
15721 this.last = this.lastActive = index;
15722 if(!preventViewNotify){
15723 this.grid.getView().onRowSelect(index);
15725 this.fireEvent("rowselect", this, index, r);
15726 this.fireEvent("selectionchange", this);
15732 * @param {Number} row The index of the row to deselect
15734 deselectRow : function(index, preventViewNotify){
15735 if(this.locked) return;
15736 if(this.last == index){
15739 if(this.lastActive == index){
15740 this.lastActive = false;
15742 var r = this.grid.dataSource.getAt(index);
15743 this.selections.remove(r);
15744 if(!preventViewNotify){
15745 this.grid.getView().onRowDeselect(index);
15747 this.fireEvent("rowdeselect", this, index);
15748 this.fireEvent("selectionchange", this);
15752 restoreLast : function(){
15754 this.last = this._last;
15759 acceptsNav : function(row, col, cm){
15760 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15764 onEditorKey : function(field, e){
15765 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15770 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15772 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15774 }else if(k == e.ENTER && !e.ctrlKey){
15778 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15780 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15782 }else if(k == e.ESC){
15786 g.startEditing(newCell[0], newCell[1]);
15797 * @class Roo.bootstrap.MessageBar
15798 * @extends Roo.bootstrap.Component
15799 * Bootstrap MessageBar class
15800 * @cfg {String} html contents of the MessageBar
15801 * @cfg {String} weight (info | success | warning | danger) default info
15802 * @cfg {String} beforeClass insert the bar before the given class
15803 * @cfg {Boolean} closable (true | false) default false
15804 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15807 * Create a new Element
15808 * @param {Object} config The config object
15811 Roo.bootstrap.MessageBar = function(config){
15812 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15815 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15821 beforeClass: 'bootstrap-sticky-wrap',
15823 getAutoCreate : function(){
15827 cls: 'alert alert-dismissable alert-' + this.weight,
15832 html: this.html || ''
15838 cfg.cls += ' alert-messages-fixed';
15852 onRender : function(ct, position)
15854 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15857 var cfg = Roo.apply({}, this.getAutoCreate());
15861 cfg.cls += ' ' + this.cls;
15864 cfg.style = this.style;
15866 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15868 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15871 this.el.select('>button.close').on('click', this.hide, this);
15877 if (!this.rendered) {
15883 this.fireEvent('show', this);
15889 if (!this.rendered) {
15895 this.fireEvent('hide', this);
15898 update : function()
15900 // var e = this.el.dom.firstChild;
15902 // if(this.closable){
15903 // e = e.nextSibling;
15906 // e.data = this.html || '';
15908 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';