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);
2039 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2058 getAutoCreate : function(){
2063 if (this.sidebar === true) {
2071 if (this.bar === true) {
2079 cls: 'navbar-header',
2084 cls: 'navbar-toggle',
2085 'data-toggle': 'collapse',
2090 html: 'Toggle navigation'
2110 cls: 'collapse navbar-collapse'
2115 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2117 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2118 cfg.cls += ' navbar-' + this.position;
2119 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2122 if (this.brand !== '') {
2125 href: this.brand_href ? this.brand_href : '#',
2126 cls: 'navbar-brand',
2134 cfg.cls += ' main-nav';
2140 } else if (this.bar === false) {
2143 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2153 if (['tabs','pills'].indexOf(this.type)!==-1) {
2154 cfg.cn[0].cls += ' nav-' + this.type
2156 if (this.type!=='nav') {
2157 Roo.log('nav type must be nav/tabs/pills')
2159 cfg.cn[0].cls += ' navbar-nav'
2162 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2163 cfg.cn[0].cls += ' nav-' + this.arrangement;
2166 if (this.align === 'right') {
2167 cfg.cn[0].cls += ' navbar-right';
2170 cfg.cls += ' navbar-inverse';
2178 initEvents :function ()
2180 //Roo.log(this.el.select('.navbar-toggle',true));
2181 this.el.select('.navbar-toggle',true).on('click', function() {
2182 // Roo.log('click');
2183 this.el.select('.navbar-collapse',true).toggleClass('in');
2191 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2193 var size = this.el.getSize();
2194 this.maskEl.setSize(size.width, size.height);
2195 this.maskEl.enableDisplayMode("block");
2204 getChildContainer : function()
2206 if (this.bar === true) {
2207 return this.el.select('.collapse',true).first();
2239 * @class Roo.bootstrap.NavGroup
2240 * @extends Roo.bootstrap.Component
2241 * Bootstrap NavGroup class
2242 * @cfg {String} align left | right
2243 * @cfg {Boolean} inverse false | true
2244 * @cfg {String} type (nav|pills|tab) default nav
2245 * @cfg {String} navId - reference Id for navbar.
2249 * Create a new nav group
2250 * @param {Object} config The config object
2253 Roo.bootstrap.NavGroup = function(config){
2254 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2256 Roo.bootstrap.NavGroup.register(this);
2260 * Fires when the active item changes
2261 * @param {Roo.bootstrap.NavGroup} this
2262 * @param {Roo.bootstrap.Navbar.Item} item The item selected
2263 * @param {Roo.bootstrap.Navbar.Item} item The previously selected item
2270 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2281 getAutoCreate : function()
2283 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2290 if (['tabs','pills'].indexOf(this.type)!==-1) {
2291 cfg.cls += ' nav-' + this.type
2293 if (this.type!=='nav') {
2294 Roo.log('nav type must be nav/tabs/pills')
2296 cfg.cls += ' navbar-nav'
2299 if (this.parent().sidebar === true) {
2302 cls: 'dashboard-menu'
2308 if (this.form === true) {
2314 if (this.align === 'right') {
2315 cfg.cls += ' navbar-right';
2317 cfg.cls += ' navbar-left';
2321 if (this.align === 'right') {
2322 cfg.cls += ' navbar-right';
2326 cfg.cls += ' navbar-inverse';
2334 setActiveItem : function(item)
2337 Roo.each(this.navItems, function(v){
2339 v.setActive(false, true);
2346 item.setActive(true, true);
2347 this.fireEvent('changed', this, item, prev);
2353 register : function(item)
2355 this.navItems.push( item);
2356 item.navId = this.navId;
2359 getNavItem: function(tabId)
2362 Roo.each(this.navItems, function(e) {
2363 if (e.tabId == tabId) {
2375 Roo.apply(Roo.bootstrap.NavGroup, {
2379 register : function(navgrp)
2381 this.groups[navgrp.navId] = navgrp;
2384 get: function(navId) {
2385 return this.groups[navId];
2400 * @class Roo.bootstrap.Navbar.Item
2401 * @extends Roo.bootstrap.Component
2402 * Bootstrap Navbar.Button class
2403 * @cfg {String} href link to
2404 * @cfg {String} html content of button
2405 * @cfg {String} badge text inside badge
2406 * @cfg {String} glyphicon name of glyphicon
2407 * @cfg {String} icon name of font awesome icon
2408 * @cfg {Boolean} active Is item active
2409 * @cfg {Boolean} preventDefault (true | false) default false
2410 * @cfg {String} tabId the tab that this item activates.
2413 * Create a new Navbar Button
2414 * @param {Object} config The config object
2416 Roo.bootstrap.Navbar.Item = function(config){
2417 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2422 * The raw click event for the entire grid.
2423 * @param {Roo.EventObject} e
2428 * Fires when the active item active state changes
2429 * @param {Roo.bootstrap.Navbar.Item} this
2430 * @param {boolean} state the new state
2438 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
2446 preventDefault : false,
2449 getAutoCreate : function(){
2451 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2453 if (this.parent().parent().sidebar === true) {
2466 cfg.cn[0].html = this.html;
2470 this.cls += ' active';
2474 cfg.cn[0].cls += ' dropdown-toggle';
2475 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2479 cfg.cn[0].tag = 'a',
2480 cfg.cn[0].href = this.href;
2483 if (this.glyphicon) {
2484 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2488 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2500 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2510 if (this.glyphicon) {
2511 if(cfg.html){cfg.html = ' ' + this.html};
2515 cls: 'glyphicon glyphicon-' + this.glyphicon
2520 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2525 cfg.cn[0].html += " <span class='caret'></span>";
2526 //}else if (!this.href) {
2527 // cfg.cn[0].tag='p';
2528 // cfg.cn[0].cls='navbar-text';
2531 cfg.cn[0].href=this.href||'#';
2532 cfg.cn[0].html=this.html;
2535 if (this.badge !== '') {
2538 cfg.cn[0].html + ' ',
2549 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2554 initEvents: function() {
2555 // Roo.log('init events?');
2556 // Roo.log(this.el.dom);
2557 this.el.select('a',true).on('click', this.onClick, this);
2558 // at this point parent should be available..
2559 this.parent().register(this);
2562 onClick : function(e)
2564 if(this.preventDefault){
2568 if(this.fireEvent('click', this, e) === false){
2572 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2573 if (typeof(this.parent().setActiveItem) !== 'undefined') {
2574 this.parent().setActiveItem(this);
2582 isActive: function () {
2585 setActive : function(state, fire)
2587 this.active = state;
2589 this.el.removeClass('active');
2590 } else if (!this.hasClass('active')) {
2591 this.addClass('active');
2594 this.fireEvent('changed', this, state);
2599 // this should not be here...
2612 * @class Roo.bootstrap.Row
2613 * @extends Roo.bootstrap.Component
2614 * Bootstrap Row class (contains columns...)
2618 * @param {Object} config The config object
2621 Roo.bootstrap.Row = function(config){
2622 Roo.bootstrap.Row.superclass.constructor.call(this, config);
2625 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
2627 getAutoCreate : function(){
2646 * @class Roo.bootstrap.Element
2647 * @extends Roo.bootstrap.Component
2648 * Bootstrap Element class
2649 * @cfg {String} html contents of the element
2650 * @cfg {String} tag tag of the element
2651 * @cfg {String} cls class of the element
2654 * Create a new Element
2655 * @param {Object} config The config object
2658 Roo.bootstrap.Element = function(config){
2659 Roo.bootstrap.Element.superclass.constructor.call(this, config);
2662 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
2669 getAutoCreate : function(){
2694 * @class Roo.bootstrap.Pagination
2695 * @extends Roo.bootstrap.Component
2696 * Bootstrap Pagination class
2697 * @cfg {String} size xs | sm | md | lg
2698 * @cfg {Boolean} inverse false | true
2701 * Create a new Pagination
2702 * @param {Object} config The config object
2705 Roo.bootstrap.Pagination = function(config){
2706 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2709 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
2715 getAutoCreate : function(){
2721 cfg.cls += ' inverse';
2727 cfg.cls += " " + this.cls;
2745 * @class Roo.bootstrap.PaginationItem
2746 * @extends Roo.bootstrap.Component
2747 * Bootstrap PaginationItem class
2748 * @cfg {String} html text
2749 * @cfg {String} href the link
2750 * @cfg {Boolean} preventDefault (true | false) default true
2751 * @cfg {Boolean} active (true | false) default false
2755 * Create a new PaginationItem
2756 * @param {Object} config The config object
2760 Roo.bootstrap.PaginationItem = function(config){
2761 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2766 * The raw click event for the entire grid.
2767 * @param {Roo.EventObject} e
2773 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
2777 preventDefault: true,
2781 getAutoCreate : function(){
2787 href : this.href ? this.href : '#',
2788 html : this.html ? this.html : ''
2798 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2804 initEvents: function() {
2806 this.el.on('click', this.onClick, this);
2809 onClick : function(e)
2811 Roo.log('PaginationItem on click ');
2812 if(this.preventDefault){
2816 this.fireEvent('click', this, e);
2832 * @class Roo.bootstrap.Slider
2833 * @extends Roo.bootstrap.Component
2834 * Bootstrap Slider class
2837 * Create a new Slider
2838 * @param {Object} config The config object
2841 Roo.bootstrap.Slider = function(config){
2842 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2845 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
2847 getAutoCreate : function(){
2851 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2855 cls: 'ui-slider-handle ui-state-default ui-corner-all'
2873 * @class Roo.bootstrap.Table
2874 * @extends Roo.bootstrap.Component
2875 * Bootstrap Table class
2876 * @cfg {String} cls table class
2877 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2878 * @cfg {String} bgcolor Specifies the background color for a table
2879 * @cfg {Number} border Specifies whether the table cells should have borders or not
2880 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2881 * @cfg {Number} cellspacing Specifies the space between cells
2882 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2883 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2884 * @cfg {String} sortable Specifies that the table should be sortable
2885 * @cfg {String} summary Specifies a summary of the content of a table
2886 * @cfg {Number} width Specifies the width of a table
2888 * @cfg {boolean} striped Should the rows be alternative striped
2889 * @cfg {boolean} bordered Add borders to the table
2890 * @cfg {boolean} hover Add hover highlighting
2891 * @cfg {boolean} condensed Format condensed
2892 * @cfg {boolean} responsive Format condensed
2898 * Create a new Table
2899 * @param {Object} config The config object
2902 Roo.bootstrap.Table = function(config){
2903 Roo.bootstrap.Table.superclass.constructor.call(this, config);
2906 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2907 this.sm = this.selModel;
2908 this.sm.xmodule = this.xmodule || false;
2910 if (this.cm && typeof(this.cm.config) == 'undefined') {
2911 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2912 this.cm = this.colModel;
2913 this.cm.xmodule = this.xmodule || false;
2916 this.store= Roo.factory(this.store, Roo.data);
2917 this.ds = this.store;
2918 this.ds.xmodule = this.xmodule || false;
2923 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
2945 getAutoCreate : function(){
2946 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2955 cfg.cls += ' table-striped';
2958 cfg.cls += ' table-hover';
2960 if (this.bordered) {
2961 cfg.cls += ' table-bordered';
2963 if (this.condensed) {
2964 cfg.cls += ' table-condensed';
2966 if (this.responsive) {
2967 cfg.cls += ' table-responsive';
2974 cfg.cls+= ' ' +this.cls;
2977 // this lot should be simplifed...
2980 cfg.align=this.align;
2983 cfg.bgcolor=this.bgcolor;
2986 cfg.border=this.border;
2988 if (this.cellpadding) {
2989 cfg.cellpadding=this.cellpadding;
2991 if (this.cellspacing) {
2992 cfg.cellspacing=this.cellspacing;
2995 cfg.frame=this.frame;
2998 cfg.rules=this.rules;
3000 if (this.sortable) {
3001 cfg.sortable=this.sortable;
3004 cfg.summary=this.summary;
3007 cfg.width=this.width;
3010 if(this.store || this.cm){
3011 cfg.cn.push(this.renderHeader());
3012 cfg.cn.push(this.renderBody());
3013 cfg.cn.push(this.renderFooter());
3015 cfg.cls+= ' TableGrid';
3021 // initTableGrid : function()
3030 // var cm = this.cm;
3032 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3035 // html: cm.getColumnHeader(i)
3039 // cfg.push(header);
3046 initEvents : function()
3048 if(!this.store || !this.cm){
3052 Roo.log('initEvents with ds!!!!');
3056 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3057 e.on('click', _this.sort, _this);
3059 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
3060 // this.maskEl.enableDisplayMode("block");
3061 // this.maskEl.show();
3063 this.store.on('load', this.onLoad, this);
3064 this.store.on('beforeload', this.onBeforeLoad, this);
3072 sort : function(e,el)
3074 var col = Roo.get(el)
3076 if(!col.hasClass('sortable')){
3080 var sort = col.attr('sort');
3083 if(col.hasClass('glyphicon-arrow-up')){
3087 this.store.sortInfo = {field : sort, direction : dir};
3092 renderHeader : function()
3101 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3103 var config = cm.config[i];
3107 html: cm.getColumnHeader(i)
3110 if(typeof(config.dataIndex) != 'undefined'){
3111 c.sort = config.dataIndex;
3114 if(typeof(config.sortable) != 'undefined' && config.sortable){
3118 if(typeof(config.width) != 'undefined'){
3119 c.style = 'width:' + config.width + 'px';
3128 renderBody : function()
3138 renderFooter : function()
3150 Roo.log('ds onload');
3155 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
3156 e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
3158 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
3159 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
3162 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
3163 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
3167 var tbody = this.el.select('tbody', true).first();
3171 if(this.store.getCount() > 0){
3172 this.store.data.each(function(d){
3178 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3179 var renderer = cm.getRenderer(i);
3180 var config = cm.config[i];
3184 if(typeof(renderer) !== 'undefined'){
3185 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3188 if(typeof(value) === 'object'){
3198 html: (typeof(value) === 'object') ? '' : value
3201 if(typeof(config.width) != 'undefined'){
3202 td.style = 'width:' + config.width + 'px';
3209 tbody.createChild(row);
3217 Roo.each(renders, function(r){
3218 _this.renderColumn(r);
3222 // if(this.loadMask){
3223 // this.maskEl.hide();
3227 onBeforeLoad : function()
3229 Roo.log('ds onBeforeLoad');
3233 // if(this.loadMask){
3234 // this.maskEl.show();
3240 this.el.select('tbody', true).first().dom.innerHTML = '';
3243 getSelectionModel : function(){
3245 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3247 return this.selModel;
3250 renderColumn : function(r)
3253 r.cfg.render(Roo.get(r.id));
3256 Roo.each(r.cfg.cn, function(c){
3261 _this.renderColumn(child);
3278 * @class Roo.bootstrap.TableCell
3279 * @extends Roo.bootstrap.Component
3280 * Bootstrap TableCell class
3281 * @cfg {String} html cell contain text
3282 * @cfg {String} cls cell class
3283 * @cfg {String} tag cell tag (td|th) default td
3284 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3285 * @cfg {String} align Aligns the content in a cell
3286 * @cfg {String} axis Categorizes cells
3287 * @cfg {String} bgcolor Specifies the background color of a cell
3288 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3289 * @cfg {Number} colspan Specifies the number of columns a cell should span
3290 * @cfg {String} headers Specifies one or more header cells a cell is related to
3291 * @cfg {Number} height Sets the height of a cell
3292 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3293 * @cfg {Number} rowspan Sets the number of rows a cell should span
3294 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3295 * @cfg {String} valign Vertical aligns the content in a cell
3296 * @cfg {Number} width Specifies the width of a cell
3299 * Create a new TableCell
3300 * @param {Object} config The config object
3303 Roo.bootstrap.TableCell = function(config){
3304 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3307 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3327 getAutoCreate : function(){
3328 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3348 cfg.align=this.align
3354 cfg.bgcolor=this.bgcolor
3357 cfg.charoff=this.charoff
3360 cfg.colspan=this.colspan
3363 cfg.headers=this.headers
3366 cfg.height=this.height
3369 cfg.nowrap=this.nowrap
3372 cfg.rowspan=this.rowspan
3375 cfg.scope=this.scope
3378 cfg.valign=this.valign
3381 cfg.width=this.width
3400 * @class Roo.bootstrap.TableRow
3401 * @extends Roo.bootstrap.Component
3402 * Bootstrap TableRow class
3403 * @cfg {String} cls row class
3404 * @cfg {String} align Aligns the content in a table row
3405 * @cfg {String} bgcolor Specifies a background color for a table row
3406 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3407 * @cfg {String} valign Vertical aligns the content in a table row
3410 * Create a new TableRow
3411 * @param {Object} config The config object
3414 Roo.bootstrap.TableRow = function(config){
3415 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3418 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3426 getAutoCreate : function(){
3427 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3437 cfg.align = this.align;
3440 cfg.bgcolor = this.bgcolor;
3443 cfg.charoff = this.charoff;
3446 cfg.valign = this.valign;
3464 * @class Roo.bootstrap.TableBody
3465 * @extends Roo.bootstrap.Component
3466 * Bootstrap TableBody class
3467 * @cfg {String} cls element class
3468 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3469 * @cfg {String} align Aligns the content inside the element
3470 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3471 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3474 * Create a new TableBody
3475 * @param {Object} config The config object
3478 Roo.bootstrap.TableBody = function(config){
3479 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3482 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3490 getAutoCreate : function(){
3491 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3505 cfg.align = this.align;
3508 cfg.charoff = this.charoff;
3511 cfg.valign = this.valign;
3518 // initEvents : function()
3525 // this.store = Roo.factory(this.store, Roo.data);
3526 // this.store.on('load', this.onLoad, this);
3528 // this.store.load();
3532 // onLoad: function ()
3534 // this.fireEvent('load', this);
3544 * Ext JS Library 1.1.1
3545 * Copyright(c) 2006-2007, Ext JS, LLC.
3547 * Originally Released Under LGPL - original licence link has changed is not relivant.
3550 * <script type="text/javascript">
3553 // as we use this in bootstrap.
3554 Roo.namespace('Roo.form');
3556 * @class Roo.form.Action
3557 * Internal Class used to handle form actions
3559 * @param {Roo.form.BasicForm} el The form element or its id
3560 * @param {Object} config Configuration options
3565 // define the action interface
3566 Roo.form.Action = function(form, options){
3568 this.options = options || {};
3571 * Client Validation Failed
3574 Roo.form.Action.CLIENT_INVALID = 'client';
3576 * Server Validation Failed
3579 Roo.form.Action.SERVER_INVALID = 'server';
3581 * Connect to Server Failed
3584 Roo.form.Action.CONNECT_FAILURE = 'connect';
3586 * Reading Data from Server Failed
3589 Roo.form.Action.LOAD_FAILURE = 'load';
3591 Roo.form.Action.prototype = {
3593 failureType : undefined,
3594 response : undefined,
3598 run : function(options){
3603 success : function(response){
3608 handleResponse : function(response){
3612 // default connection failure
3613 failure : function(response){
3615 this.response = response;
3616 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3617 this.form.afterAction(this, false);
3620 processResponse : function(response){
3621 this.response = response;
3622 if(!response.responseText){
3625 this.result = this.handleResponse(response);
3629 // utility functions used internally
3630 getUrl : function(appendParams){
3631 var url = this.options.url || this.form.url || this.form.el.dom.action;
3633 var p = this.getParams();
3635 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3641 getMethod : function(){
3642 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3645 getParams : function(){
3646 var bp = this.form.baseParams;
3647 var p = this.options.params;
3649 if(typeof p == "object"){
3650 p = Roo.urlEncode(Roo.applyIf(p, bp));
3651 }else if(typeof p == 'string' && bp){
3652 p += '&' + Roo.urlEncode(bp);
3655 p = Roo.urlEncode(bp);
3660 createCallback : function(){
3662 success: this.success,
3663 failure: this.failure,
3665 timeout: (this.form.timeout*1000),
3666 upload: this.form.fileUpload ? this.success : undefined
3671 Roo.form.Action.Submit = function(form, options){
3672 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3675 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3678 haveProgress : false,
3679 uploadComplete : false,
3681 // uploadProgress indicator.
3682 uploadProgress : function()
3684 if (!this.form.progressUrl) {
3688 if (!this.haveProgress) {
3689 Roo.MessageBox.progress("Uploading", "Uploading");
3691 if (this.uploadComplete) {
3692 Roo.MessageBox.hide();
3696 this.haveProgress = true;
3698 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3700 var c = new Roo.data.Connection();
3702 url : this.form.progressUrl,
3707 success : function(req){
3708 //console.log(data);
3712 rdata = Roo.decode(req.responseText)
3714 Roo.log("Invalid data from server..");
3718 if (!rdata || !rdata.success) {
3720 Roo.MessageBox.alert(Roo.encode(rdata));
3723 var data = rdata.data;
3725 if (this.uploadComplete) {
3726 Roo.MessageBox.hide();
3731 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3732 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3735 this.uploadProgress.defer(2000,this);
3738 failure: function(data) {
3739 Roo.log('progress url failed ');
3750 // run get Values on the form, so it syncs any secondary forms.
3751 this.form.getValues();
3753 var o = this.options;
3754 var method = this.getMethod();
3755 var isPost = method == 'POST';
3756 if(o.clientValidation === false || this.form.isValid()){
3758 if (this.form.progressUrl) {
3759 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3760 (new Date() * 1) + '' + Math.random());
3765 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3766 form:this.form.el.dom,
3767 url:this.getUrl(!isPost),
3769 params:isPost ? this.getParams() : null,
3770 isUpload: this.form.fileUpload
3773 this.uploadProgress();
3775 }else if (o.clientValidation !== false){ // client validation failed
3776 this.failureType = Roo.form.Action.CLIENT_INVALID;
3777 this.form.afterAction(this, false);
3781 success : function(response)
3783 this.uploadComplete= true;
3784 if (this.haveProgress) {
3785 Roo.MessageBox.hide();
3789 var result = this.processResponse(response);
3790 if(result === true || result.success){
3791 this.form.afterAction(this, true);
3795 this.form.markInvalid(result.errors);
3796 this.failureType = Roo.form.Action.SERVER_INVALID;
3798 this.form.afterAction(this, false);
3800 failure : function(response)
3802 this.uploadComplete= true;
3803 if (this.haveProgress) {
3804 Roo.MessageBox.hide();
3807 this.response = response;
3808 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3809 this.form.afterAction(this, false);
3812 handleResponse : function(response){
3813 if(this.form.errorReader){
3814 var rs = this.form.errorReader.read(response);
3817 for(var i = 0, len = rs.records.length; i < len; i++) {
3818 var r = rs.records[i];
3822 if(errors.length < 1){
3826 success : rs.success,
3832 ret = Roo.decode(response.responseText);
3836 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3846 Roo.form.Action.Load = function(form, options){
3847 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3848 this.reader = this.form.reader;
3851 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3856 Roo.Ajax.request(Roo.apply(
3857 this.createCallback(), {
3858 method:this.getMethod(),
3859 url:this.getUrl(false),
3860 params:this.getParams()
3864 success : function(response){
3866 var result = this.processResponse(response);
3867 if(result === true || !result.success || !result.data){
3868 this.failureType = Roo.form.Action.LOAD_FAILURE;
3869 this.form.afterAction(this, false);
3872 this.form.clearInvalid();
3873 this.form.setValues(result.data);
3874 this.form.afterAction(this, true);
3877 handleResponse : function(response){
3878 if(this.form.reader){
3879 var rs = this.form.reader.read(response);
3880 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3882 success : rs.success,
3886 return Roo.decode(response.responseText);
3890 Roo.form.Action.ACTION_TYPES = {
3891 'load' : Roo.form.Action.Load,
3892 'submit' : Roo.form.Action.Submit
3901 * @class Roo.bootstrap.Form
3902 * @extends Roo.bootstrap.Component
3903 * Bootstrap Form class
3904 * @cfg {String} method GET | POST (default POST)
3905 * @cfg {String} labelAlign top | left (default top)
3906 * @cfg {String} align left | right - for navbars
3911 * @param {Object} config The config object
3915 Roo.bootstrap.Form = function(config){
3916 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3919 * @event clientvalidation
3920 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3921 * @param {Form} this
3922 * @param {Boolean} valid true if the form has passed client-side validation
3924 clientvalidation: true,
3926 * @event beforeaction
3927 * Fires before any action is performed. Return false to cancel the action.
3928 * @param {Form} this
3929 * @param {Action} action The action to be performed
3933 * @event actionfailed
3934 * Fires when an action fails.
3935 * @param {Form} this
3936 * @param {Action} action The action that failed
3938 actionfailed : true,
3940 * @event actioncomplete
3941 * Fires when an action is completed.
3942 * @param {Form} this
3943 * @param {Action} action The action that completed
3945 actioncomplete : true
3950 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3953 * @cfg {String} method
3954 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3959 * The URL to use for form actions if one isn't supplied in the action options.
3962 * @cfg {Boolean} fileUpload
3963 * Set to true if this form is a file upload.
3967 * @cfg {Object} baseParams
3968 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3972 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3976 * @cfg {Sting} align (left|right) for navbar forms
3981 activeAction : null,
3984 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3985 * element by passing it or its id or mask the form itself by passing in true.
3988 waitMsgTarget : false,
3993 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3994 * element by passing it or its id or mask the form itself by passing in true.
3998 getAutoCreate : function(){
4002 method : this.method || 'POST',
4003 id : this.id || Roo.id(),
4006 if (this.parent().xtype.match(/^Nav/)) {
4007 cfg.cls = 'navbar-form navbar-' + this.align;
4011 if (this.labelAlign == 'left' ) {
4012 cfg.cls += ' form-horizontal';
4018 initEvents : function()
4020 this.el.on('submit', this.onSubmit, this);
4025 onSubmit : function(e){
4030 * Returns true if client-side validation on the form is successful.
4033 isValid : function(){
4034 var items = this.getItems();
4036 items.each(function(f){
4045 * Returns true if any fields in this form have changed since their original load.
4048 isDirty : function(){
4050 var items = this.getItems();
4051 items.each(function(f){
4061 * Performs a predefined action (submit or load) or custom actions you define on this form.
4062 * @param {String} actionName The name of the action type
4063 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
4064 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
4065 * accept other config options):
4067 Property Type Description
4068 ---------------- --------------- ----------------------------------------------------------------------------------
4069 url String The url for the action (defaults to the form's url)
4070 method String The form method to use (defaults to the form's method, or POST if not defined)
4071 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
4072 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
4073 validate the form on the client (defaults to false)
4075 * @return {BasicForm} this
4077 doAction : function(action, options){
4078 if(typeof action == 'string'){
4079 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
4081 if(this.fireEvent('beforeaction', this, action) !== false){
4082 this.beforeAction(action);
4083 action.run.defer(100, action);
4089 beforeAction : function(action){
4090 var o = action.options;
4092 // not really supported yet.. ??
4094 //if(this.waitMsgTarget === true){
4095 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
4096 //}else if(this.waitMsgTarget){
4097 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
4098 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
4100 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
4106 afterAction : function(action, success){
4107 this.activeAction = null;
4108 var o = action.options;
4110 //if(this.waitMsgTarget === true){
4112 //}else if(this.waitMsgTarget){
4113 // this.waitMsgTarget.unmask();
4115 // Roo.MessageBox.updateProgress(1);
4116 // Roo.MessageBox.hide();
4123 Roo.callback(o.success, o.scope, [this, action]);
4124 this.fireEvent('actioncomplete', this, action);
4128 // failure condition..
4129 // we have a scenario where updates need confirming.
4130 // eg. if a locking scenario exists..
4131 // we look for { errors : { needs_confirm : true }} in the response.
4133 (typeof(action.result) != 'undefined') &&
4134 (typeof(action.result.errors) != 'undefined') &&
4135 (typeof(action.result.errors.needs_confirm) != 'undefined')
4138 Roo.log("not supported yet");
4141 Roo.MessageBox.confirm(
4142 "Change requires confirmation",
4143 action.result.errorMsg,
4148 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4158 Roo.callback(o.failure, o.scope, [this, action]);
4159 // show an error message if no failed handler is set..
4160 if (!this.hasListener('actionfailed')) {
4161 Roo.log("need to add dialog support");
4163 Roo.MessageBox.alert("Error",
4164 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4165 action.result.errorMsg :
4166 "Saving Failed, please check your entries or try again"
4171 this.fireEvent('actionfailed', this, action);
4176 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4177 * @param {String} id The value to search for
4180 findField : function(id){
4181 var items = this.getItems();
4182 var field = items.get(id);
4184 items.each(function(f){
4185 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4192 return field || null;
4195 * Mark fields in this form invalid in bulk.
4196 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4197 * @return {BasicForm} this
4199 markInvalid : function(errors){
4200 if(errors instanceof Array){
4201 for(var i = 0, len = errors.length; i < len; i++){
4202 var fieldError = errors[i];
4203 var f = this.findField(fieldError.id);
4205 f.markInvalid(fieldError.msg);
4211 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4212 field.markInvalid(errors[id]);
4216 //Roo.each(this.childForms || [], function (f) {
4217 // f.markInvalid(errors);
4224 * Set values for fields in this form in bulk.
4225 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4226 * @return {BasicForm} this
4228 setValues : function(values){
4229 if(values instanceof Array){ // array of objects
4230 for(var i = 0, len = values.length; i < len; i++){
4232 var f = this.findField(v.id);
4234 f.setValue(v.value);
4235 if(this.trackResetOnLoad){
4236 f.originalValue = f.getValue();
4240 }else{ // object hash
4243 if(typeof values[id] != 'function' && (field = this.findField(id))){
4245 if (field.setFromData &&
4247 field.displayField &&
4248 // combos' with local stores can
4249 // be queried via setValue()
4250 // to set their value..
4251 (field.store && !field.store.isLocal)
4255 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4256 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4257 field.setFromData(sd);
4260 field.setValue(values[id]);
4264 if(this.trackResetOnLoad){
4265 field.originalValue = field.getValue();
4271 //Roo.each(this.childForms || [], function (f) {
4272 // f.setValues(values);
4279 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4280 * they are returned as an array.
4281 * @param {Boolean} asString
4284 getValues : function(asString){
4285 //if (this.childForms) {
4286 // copy values from the child forms
4287 // Roo.each(this.childForms, function (f) {
4288 // this.setValues(f.getValues());
4294 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4295 if(asString === true){
4298 return Roo.urlDecode(fs);
4302 * Returns the fields in this form as an object with key/value pairs.
4303 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4306 getFieldValues : function(with_hidden)
4308 var items = this.getItems();
4310 items.each(function(f){
4314 var v = f.getValue();
4315 if (f.inputType =='radio') {
4316 if (typeof(ret[f.getName()]) == 'undefined') {
4317 ret[f.getName()] = ''; // empty..
4320 if (!f.el.dom.checked) {
4328 // not sure if this supported any more..
4329 if ((typeof(v) == 'object') && f.getRawValue) {
4330 v = f.getRawValue() ; // dates..
4332 // combo boxes where name != hiddenName...
4333 if (f.name != f.getName()) {
4334 ret[f.name] = f.getRawValue();
4336 ret[f.getName()] = v;
4343 * Clears all invalid messages in this form.
4344 * @return {BasicForm} this
4346 clearInvalid : function(){
4347 var items = this.getItems();
4349 items.each(function(f){
4360 * @return {BasicForm} this
4363 var items = this.getItems();
4364 items.each(function(f){
4368 Roo.each(this.childForms || [], function (f) {
4375 getItems : function()
4377 var r=new Roo.util.MixedCollection(false, function(o){
4378 return o.id || (o.id = Roo.id());
4380 var iter = function(el) {
4387 Roo.each(el.items,function(e) {
4406 * Ext JS Library 1.1.1
4407 * Copyright(c) 2006-2007, Ext JS, LLC.
4409 * Originally Released Under LGPL - original licence link has changed is not relivant.
4412 * <script type="text/javascript">
4415 * @class Roo.form.VTypes
4416 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4419 Roo.form.VTypes = function(){
4420 // closure these in so they are only created once.
4421 var alpha = /^[a-zA-Z_]+$/;
4422 var alphanum = /^[a-zA-Z0-9_]+$/;
4423 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4424 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4426 // All these messages and functions are configurable
4429 * The function used to validate email addresses
4430 * @param {String} value The email address
4432 'email' : function(v){
4433 return email.test(v);
4436 * The error text to display when the email validation function returns false
4439 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4441 * The keystroke filter mask to be applied on email input
4444 'emailMask' : /[a-z0-9_\.\-@]/i,
4447 * The function used to validate URLs
4448 * @param {String} value The URL
4450 'url' : function(v){
4454 * The error text to display when the url validation function returns false
4457 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4460 * The function used to validate alpha values
4461 * @param {String} value The value
4463 'alpha' : function(v){
4464 return alpha.test(v);
4467 * The error text to display when the alpha validation function returns false
4470 'alphaText' : 'This field should only contain letters and _',
4472 * The keystroke filter mask to be applied on alpha input
4475 'alphaMask' : /[a-z_]/i,
4478 * The function used to validate alphanumeric values
4479 * @param {String} value The value
4481 'alphanum' : function(v){
4482 return alphanum.test(v);
4485 * The error text to display when the alphanumeric validation function returns false
4488 'alphanumText' : 'This field should only contain letters, numbers and _',
4490 * The keystroke filter mask to be applied on alphanumeric input
4493 'alphanumMask' : /[a-z0-9_]/i
4503 * @class Roo.bootstrap.Input
4504 * @extends Roo.bootstrap.Component
4505 * Bootstrap Input class
4506 * @cfg {Boolean} disabled is it disabled
4507 * @cfg {String} fieldLabel - the label associated
4508 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4509 * @cfg {String} name name of the input
4510 * @cfg {string} fieldLabel - the label associated
4511 * @cfg {string} inputType - input / file submit ...
4512 * @cfg {string} placeholder - placeholder to put in text.
4513 * @cfg {string} before - input group add on before
4514 * @cfg {string} after - input group add on after
4515 * @cfg {string} size - (lg|sm) or leave empty..
4516 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4517 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4518 * @cfg {Number} md colspan out of 12 for computer-sized screens
4519 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4520 * @cfg {string} value default value of the input
4521 * @cfg {Number} labelWidth set the width of label (0-12)
4522 * @cfg {String} labelAlign (top|left)
4523 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4527 * Create a new Input
4528 * @param {Object} config The config object
4531 Roo.bootstrap.Input = function(config){
4532 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4537 * Fires when this field receives input focus.
4538 * @param {Roo.form.Field} this
4543 * Fires when this field loses input focus.
4544 * @param {Roo.form.Field} this
4549 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4550 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4551 * @param {Roo.form.Field} this
4552 * @param {Roo.EventObject} e The event object
4557 * Fires just before the field blurs if the field value has changed.
4558 * @param {Roo.form.Field} this
4559 * @param {Mixed} newValue The new value
4560 * @param {Mixed} oldValue The original value
4565 * Fires after the field has been marked as invalid.
4566 * @param {Roo.form.Field} this
4567 * @param {String} msg The validation message
4572 * Fires after the field has been validated with no errors.
4573 * @param {Roo.form.Field} this
4578 * Fires after the key up
4579 * @param {Roo.form.Field} this
4580 * @param {Roo.EventObject} e The event Object
4586 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4588 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4589 automatic validation (defaults to "keyup").
4591 validationEvent : "keyup",
4593 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4595 validateOnBlur : true,
4597 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4599 validationDelay : 250,
4601 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4603 focusClass : "x-form-focus", // not needed???
4607 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4609 invalidClass : "has-error",
4612 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4614 selectOnFocus : false,
4617 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4621 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4626 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4628 disableKeyFilter : false,
4631 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4635 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4639 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4641 blankText : "This field is required",
4644 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4648 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4650 maxLength : Number.MAX_VALUE,
4652 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4654 minLengthText : "The minimum length for this field is {0}",
4656 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4658 maxLengthText : "The maximum length for this field is {0}",
4662 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4663 * If available, this function will be called only after the basic validators all return true, and will be passed the
4664 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4668 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4669 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4670 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4674 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4697 parentLabelAlign : function()
4700 while (parent.parent()) {
4701 parent = parent.parent();
4702 if (typeof(parent.labelAlign) !='undefined') {
4703 return parent.labelAlign;
4710 getAutoCreate : function(){
4712 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4718 if(this.inputType != 'hidden'){
4719 cfg.cls = 'form-group' //input-group
4725 type : this.inputType,
4727 cls : 'form-control',
4728 placeholder : this.placeholder || ''
4732 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4733 input.maxLength = this.maxLength;
4736 if (this.disabled) {
4737 input.disabled=true;
4740 if (this.readOnly) {
4741 input.readonly=true;
4745 input.name = this.name;
4748 input.cls += ' input-' + this.size;
4751 ['xs','sm','md','lg'].map(function(size){
4752 if (settings[size]) {
4753 cfg.cls += ' col-' + size + '-' + settings[size];
4757 var inputblock = input;
4759 if (this.before || this.after) {
4762 cls : 'input-group',
4766 inputblock.cn.push({
4768 cls : 'input-group-addon',
4772 inputblock.cn.push(input);
4774 inputblock.cn.push({
4776 cls : 'input-group-addon',
4783 if (align ==='left' && this.fieldLabel.length) {
4784 Roo.log("left and has label");
4790 cls : 'control-label col-sm-' + this.labelWidth,
4791 html : this.fieldLabel
4795 cls : "col-sm-" + (12 - this.labelWidth),
4802 } else if ( this.fieldLabel.length) {
4808 //cls : 'input-group-addon',
4809 html : this.fieldLabel
4819 Roo.log(" no label && no align");
4828 Roo.log('input-parentType: ' + this.parentType);
4830 if (this.parentType === 'Navbar' && this.parent().bar) {
4831 cfg.cls += ' navbar-form';
4839 * return the real input element.
4841 inputEl: function ()
4843 return this.el.select('input.form-control',true).first();
4845 setDisabled : function(v)
4847 var i = this.inputEl().dom;
4849 i.removeAttribute('disabled');
4853 i.setAttribute('disabled','true');
4855 initEvents : function()
4858 this.inputEl().on("keydown" , this.fireKey, this);
4859 this.inputEl().on("focus", this.onFocus, this);
4860 this.inputEl().on("blur", this.onBlur, this);
4862 this.inputEl().relayEvent('keyup', this);
4864 // reference to original value for reset
4865 this.originalValue = this.getValue();
4866 //Roo.form.TextField.superclass.initEvents.call(this);
4867 if(this.validationEvent == 'keyup'){
4868 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4869 this.inputEl().on('keyup', this.filterValidation, this);
4871 else if(this.validationEvent !== false){
4872 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4875 if(this.selectOnFocus){
4876 this.on("focus", this.preFocus, this);
4879 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4880 this.inputEl().on("keypress", this.filterKeys, this);
4883 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4884 this.el.on("click", this.autoSize, this);
4887 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4888 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4892 filterValidation : function(e){
4893 if(!e.isNavKeyPress()){
4894 this.validationTask.delay(this.validationDelay);
4898 * Validates the field value
4899 * @return {Boolean} True if the value is valid, else false
4901 validate : function(){
4902 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4903 if(this.disabled || this.validateValue(this.getRawValue())){
4904 this.clearInvalid();
4912 * Validates a value according to the field's validation rules and marks the field as invalid
4913 * if the validation fails
4914 * @param {Mixed} value The value to validate
4915 * @return {Boolean} True if the value is valid, else false
4917 validateValue : function(value){
4918 if(value.length < 1) { // if it's blank
4919 if(this.allowBlank){
4920 this.clearInvalid();
4923 this.markInvalid(this.blankText);
4927 if(value.length < this.minLength){
4928 this.markInvalid(String.format(this.minLengthText, this.minLength));
4931 if(value.length > this.maxLength){
4932 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4936 var vt = Roo.form.VTypes;
4937 if(!vt[this.vtype](value, this)){
4938 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4942 if(typeof this.validator == "function"){
4943 var msg = this.validator(value);
4945 this.markInvalid(msg);
4949 if(this.regex && !this.regex.test(value)){
4950 this.markInvalid(this.regexText);
4959 fireKey : function(e){
4960 //Roo.log('field ' + e.getKey());
4961 if(e.isNavKeyPress()){
4962 this.fireEvent("specialkey", this, e);
4965 focus : function (selectText){
4967 this.inputEl().focus();
4968 if(selectText === true){
4969 this.inputEl().dom.select();
4975 onFocus : function(){
4976 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4977 // this.el.addClass(this.focusClass);
4980 this.hasFocus = true;
4981 this.startValue = this.getValue();
4982 this.fireEvent("focus", this);
4986 beforeBlur : Roo.emptyFn,
4990 onBlur : function(){
4992 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4993 //this.el.removeClass(this.focusClass);
4995 this.hasFocus = false;
4996 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4999 var v = this.getValue();
5000 if(String(v) !== String(this.startValue)){
5001 this.fireEvent('change', this, v, this.startValue);
5003 this.fireEvent("blur", this);
5007 * Resets the current field value to the originally loaded value and clears any validation messages
5010 this.setValue(this.originalValue);
5011 this.clearInvalid();
5014 * Returns the name of the field
5015 * @return {Mixed} name The name field
5017 getName: function(){
5021 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
5022 * @return {Mixed} value The field value
5024 getValue : function(){
5025 return this.inputEl().getValue();
5028 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
5029 * @return {Mixed} value The field value
5031 getRawValue : function(){
5032 var v = this.inputEl().getValue();
5038 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
5039 * @param {Mixed} value The value to set
5041 setRawValue : function(v){
5042 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5045 selectText : function(start, end){
5046 var v = this.getRawValue();
5048 start = start === undefined ? 0 : start;
5049 end = end === undefined ? v.length : end;
5050 var d = this.inputEl().dom;
5051 if(d.setSelectionRange){
5052 d.setSelectionRange(start, end);
5053 }else if(d.createTextRange){
5054 var range = d.createTextRange();
5055 range.moveStart("character", start);
5056 range.moveEnd("character", v.length-end);
5063 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
5064 * @param {Mixed} value The value to set
5066 setValue : function(v){
5069 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
5075 processValue : function(value){
5076 if(this.stripCharsRe){
5077 var newValue = value.replace(this.stripCharsRe, '');
5078 if(newValue !== value){
5079 this.setRawValue(newValue);
5086 preFocus : function(){
5088 if(this.selectOnFocus){
5089 this.inputEl().dom.select();
5092 filterKeys : function(e){
5094 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
5097 var c = e.getCharCode(), cc = String.fromCharCode(c);
5098 if(Roo.isIE && (e.isSpecialKey() || !cc)){
5101 if(!this.maskRe.test(cc)){
5106 * Clear any invalid styles/messages for this field
5108 clearInvalid : function(){
5110 if(!this.el || this.preventMark){ // not rendered
5113 this.el.removeClass(this.invalidClass);
5115 switch(this.msgTarget){
5117 this.el.dom.qtip = '';
5120 this.el.dom.title = '';
5124 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5129 this.errorIcon.dom.qtip = '';
5130 this.errorIcon.hide();
5131 this.un('resize', this.alignErrorIcon, this);
5135 var t = Roo.getDom(this.msgTarget);
5137 t.style.display = 'none';
5141 this.fireEvent('valid', this);
5144 * Mark this field as invalid
5145 * @param {String} msg The validation message
5147 markInvalid : function(msg){
5148 if(!this.el || this.preventMark){ // not rendered
5151 this.el.addClass(this.invalidClass);
5153 msg = msg || this.invalidText;
5154 switch(this.msgTarget){
5156 this.el.dom.qtip = msg;
5157 this.el.dom.qclass = 'x-form-invalid-tip';
5158 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5159 Roo.QuickTips.enable();
5163 this.el.dom.title = msg;
5167 var elp = this.el.findParent('.x-form-element', 5, true);
5168 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5169 this.errorEl.setWidth(elp.getWidth(true)-20);
5171 this.errorEl.update(msg);
5172 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5175 if(!this.errorIcon){
5176 var elp = this.el.findParent('.x-form-element', 5, true);
5177 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5179 this.alignErrorIcon();
5180 this.errorIcon.dom.qtip = msg;
5181 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5182 this.errorIcon.show();
5183 this.on('resize', this.alignErrorIcon, this);
5186 var t = Roo.getDom(this.msgTarget);
5188 t.style.display = this.msgDisplay;
5192 this.fireEvent('invalid', this, msg);
5195 SafariOnKeyDown : function(event)
5197 // this is a workaround for a password hang bug on chrome/ webkit.
5199 var isSelectAll = false;
5201 if(this.inputEl().dom.selectionEnd > 0){
5202 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5204 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5205 event.preventDefault();
5210 if(isSelectAll){ // backspace and delete key
5212 event.preventDefault();
5213 // this is very hacky as keydown always get's upper case.
5215 var cc = String.fromCharCode(event.getCharCode());
5216 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5220 adjustWidth : function(tag, w){
5221 tag = tag.toLowerCase();
5222 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5223 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5227 if(tag == 'textarea'){
5230 }else if(Roo.isOpera){
5234 if(tag == 'textarea'){
5253 * @class Roo.bootstrap.TextArea
5254 * @extends Roo.bootstrap.Input
5255 * Bootstrap TextArea class
5256 * @cfg {Number} cols Specifies the visible width of a text area
5257 * @cfg {Number} rows Specifies the visible number of lines in a text area
5258 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5259 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5260 * @cfg {string} html text
5263 * Create a new TextArea
5264 * @param {Object} config The config object
5267 Roo.bootstrap.TextArea = function(config){
5268 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5272 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5282 getAutoCreate : function(){
5284 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5295 value : this.value || '',
5296 html: this.html || '',
5297 cls : 'form-control',
5298 placeholder : this.placeholder || ''
5302 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5303 input.maxLength = this.maxLength;
5307 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5311 input.cols = this.cols;
5314 if (this.readOnly) {
5315 input.readonly = true;
5319 input.name = this.name;
5323 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5327 ['xs','sm','md','lg'].map(function(size){
5328 if (settings[size]) {
5329 cfg.cls += ' col-' + size + '-' + settings[size];
5333 var inputblock = input;
5335 if (this.before || this.after) {
5338 cls : 'input-group',
5342 inputblock.cn.push({
5344 cls : 'input-group-addon',
5348 inputblock.cn.push(input);
5350 inputblock.cn.push({
5352 cls : 'input-group-addon',
5359 if (align ==='left' && this.fieldLabel.length) {
5360 Roo.log("left and has label");
5366 cls : 'control-label col-sm-' + this.labelWidth,
5367 html : this.fieldLabel
5371 cls : "col-sm-" + (12 - this.labelWidth),
5378 } else if ( this.fieldLabel.length) {
5384 //cls : 'input-group-addon',
5385 html : this.fieldLabel
5395 Roo.log(" no label && no align");
5405 if (this.disabled) {
5406 input.disabled=true;
5413 * return the real textarea element.
5415 inputEl: function ()
5417 return this.el.select('textarea.form-control',true).first();
5425 * trigger field - base class for combo..
5430 * @class Roo.bootstrap.TriggerField
5431 * @extends Roo.bootstrap.Input
5432 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5433 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5434 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5435 * for which you can provide a custom implementation. For example:
5437 var trigger = new Roo.bootstrap.TriggerField();
5438 trigger.onTriggerClick = myTriggerFn;
5439 trigger.applyTo('my-field');
5442 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5443 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5444 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5445 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5447 * Create a new TriggerField.
5448 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5449 * to the base TextField)
5451 Roo.bootstrap.TriggerField = function(config){
5452 this.mimicing = false;
5453 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5456 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5458 * @cfg {String} triggerClass A CSS class to apply to the trigger
5461 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5465 /** @cfg {Boolean} grow @hide */
5466 /** @cfg {Number} growMin @hide */
5467 /** @cfg {Number} growMax @hide */
5473 autoSize: Roo.emptyFn,
5480 actionMode : 'wrap',
5484 getAutoCreate : function(){
5486 var parent = this.parent();
5488 var align = this.parentLabelAlign();
5493 cls: 'form-group' //input-group
5500 type : this.inputType,
5501 cls : 'form-control',
5502 autocomplete: 'off',
5503 placeholder : this.placeholder || ''
5507 input.name = this.name;
5510 input.cls += ' input-' + this.size;
5513 if (this.disabled) {
5514 input.disabled=true;
5517 var inputblock = input;
5519 if (this.before || this.after) {
5522 cls : 'input-group',
5526 inputblock.cn.push({
5528 cls : 'input-group-addon',
5532 inputblock.cn.push(input);
5534 inputblock.cn.push({
5536 cls : 'input-group-addon',
5549 cls: 'form-hidden-field'
5557 Roo.log('multiple');
5565 cls: 'form-hidden-field'
5569 cls: 'select2-choices',
5573 cls: 'select2-search-field',
5586 cls: 'select2-container input-group',
5591 cls: 'typeahead typeahead-long dropdown-menu',
5592 style: 'display:none'
5600 cls : 'input-group-addon btn dropdown-toggle',
5608 cls: 'combobox-clear',
5622 combobox.cls += ' select2-container-multi';
5625 if (align ==='left' && this.fieldLabel.length) {
5627 Roo.log("left and has label");
5633 cls : 'control-label col-sm-' + this.labelWidth,
5634 html : this.fieldLabel
5638 cls : "col-sm-" + (12 - this.labelWidth),
5645 } else if ( this.fieldLabel.length) {
5651 //cls : 'input-group-addon',
5652 html : this.fieldLabel
5662 Roo.log(" no label && no align");
5669 ['xs','sm','md','lg'].map(function(size){
5670 if (settings[size]) {
5671 cfg.cls += ' col-' + size + '-' + settings[size];
5682 onResize : function(w, h){
5683 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5684 // if(typeof w == 'number'){
5685 // var x = w - this.trigger.getWidth();
5686 // this.inputEl().setWidth(this.adjustWidth('input', x));
5687 // this.trigger.setStyle('left', x+'px');
5692 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5695 getResizeEl : function(){
5696 return this.inputEl();
5700 getPositionEl : function(){
5701 return this.inputEl();
5705 alignErrorIcon : function(){
5706 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5710 initEvents : function(){
5712 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5713 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5715 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5716 if(this.hideTrigger){
5717 this.trigger.setDisplayed(false);
5719 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5723 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5726 //this.trigger.addClassOnOver('x-form-trigger-over');
5727 //this.trigger.addClassOnClick('x-form-trigger-click');
5730 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5735 initTrigger : function(){
5740 onDestroy : function(){
5742 this.trigger.removeAllListeners();
5743 // this.trigger.remove();
5746 // this.wrap.remove();
5748 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5752 onFocus : function(){
5753 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5756 this.wrap.addClass('x-trigger-wrap-focus');
5757 this.mimicing = true;
5758 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5759 if(this.monitorTab){
5760 this.el.on("keydown", this.checkTab, this);
5767 checkTab : function(e){
5768 if(e.getKey() == e.TAB){
5774 onBlur : function(){
5779 mimicBlur : function(e, t){
5781 if(!this.wrap.contains(t) && this.validateBlur()){
5788 triggerBlur : function(){
5789 this.mimicing = false;
5790 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5791 if(this.monitorTab){
5792 this.el.un("keydown", this.checkTab, this);
5794 //this.wrap.removeClass('x-trigger-wrap-focus');
5795 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5799 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5800 validateBlur : function(e, t){
5805 onDisable : function(){
5806 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5808 // this.wrap.addClass('x-item-disabled');
5813 onEnable : function(){
5814 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5816 // this.el.removeClass('x-item-disabled');
5821 onShow : function(){
5822 var ae = this.getActionEl();
5825 ae.dom.style.display = '';
5826 ae.dom.style.visibility = 'visible';
5832 onHide : function(){
5833 var ae = this.getActionEl();
5834 ae.dom.style.display = 'none';
5838 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5839 * by an implementing function.
5841 * @param {EventObject} e
5843 onTriggerClick : Roo.emptyFn
5847 * Ext JS Library 1.1.1
5848 * Copyright(c) 2006-2007, Ext JS, LLC.
5850 * Originally Released Under LGPL - original licence link has changed is not relivant.
5853 * <script type="text/javascript">
5858 * @class Roo.data.SortTypes
5860 * Defines the default sorting (casting?) comparison functions used when sorting data.
5862 Roo.data.SortTypes = {
5864 * Default sort that does nothing
5865 * @param {Mixed} s The value being converted
5866 * @return {Mixed} The comparison value
5873 * The regular expression used to strip tags
5877 stripTagsRE : /<\/?[^>]+>/gi,
5880 * Strips all HTML tags to sort on text only
5881 * @param {Mixed} s The value being converted
5882 * @return {String} The comparison value
5884 asText : function(s){
5885 return String(s).replace(this.stripTagsRE, "");
5889 * Strips all HTML tags to sort on text only - Case insensitive
5890 * @param {Mixed} s The value being converted
5891 * @return {String} The comparison value
5893 asUCText : function(s){
5894 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5898 * Case insensitive string
5899 * @param {Mixed} s The value being converted
5900 * @return {String} The comparison value
5902 asUCString : function(s) {
5903 return String(s).toUpperCase();
5908 * @param {Mixed} s The value being converted
5909 * @return {Number} The comparison value
5911 asDate : function(s) {
5915 if(s instanceof Date){
5918 return Date.parse(String(s));
5923 * @param {Mixed} s The value being converted
5924 * @return {Float} The comparison value
5926 asFloat : function(s) {
5927 var val = parseFloat(String(s).replace(/,/g, ""));
5928 if(isNaN(val)) val = 0;
5934 * @param {Mixed} s The value being converted
5935 * @return {Number} The comparison value
5937 asInt : function(s) {
5938 var val = parseInt(String(s).replace(/,/g, ""));
5939 if(isNaN(val)) val = 0;
5944 * Ext JS Library 1.1.1
5945 * Copyright(c) 2006-2007, Ext JS, LLC.
5947 * Originally Released Under LGPL - original licence link has changed is not relivant.
5950 * <script type="text/javascript">
5954 * @class Roo.data.Record
5955 * Instances of this class encapsulate both record <em>definition</em> information, and record
5956 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5957 * to access Records cached in an {@link Roo.data.Store} object.<br>
5959 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5960 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5963 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5965 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5966 * {@link #create}. The parameters are the same.
5967 * @param {Array} data An associative Array of data values keyed by the field name.
5968 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5969 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5970 * not specified an integer id is generated.
5972 Roo.data.Record = function(data, id){
5973 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5978 * Generate a constructor for a specific record layout.
5979 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5980 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5981 * Each field definition object may contain the following properties: <ul>
5982 * <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,
5983 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5984 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5985 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5986 * is being used, then this is a string containing the javascript expression to reference the data relative to
5987 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5988 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5989 * this may be omitted.</p></li>
5990 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5991 * <ul><li>auto (Default, implies no conversion)</li>
5996 * <li>date</li></ul></p></li>
5997 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5998 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5999 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
6000 * by the Reader into an object that will be stored in the Record. It is passed the
6001 * following parameters:<ul>
6002 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
6004 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
6006 * <br>usage:<br><pre><code>
6007 var TopicRecord = Roo.data.Record.create(
6008 {name: 'title', mapping: 'topic_title'},
6009 {name: 'author', mapping: 'username'},
6010 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
6011 {name: 'lastPost', mapping: 'post_time', type: 'date'},
6012 {name: 'lastPoster', mapping: 'user2'},
6013 {name: 'excerpt', mapping: 'post_text'}
6016 var myNewRecord = new TopicRecord({
6017 title: 'Do my job please',
6020 lastPost: new Date(),
6021 lastPoster: 'Animal',
6022 excerpt: 'No way dude!'
6024 myStore.add(myNewRecord);
6029 Roo.data.Record.create = function(o){
6031 f.superclass.constructor.apply(this, arguments);
6033 Roo.extend(f, Roo.data.Record);
6034 var p = f.prototype;
6035 p.fields = new Roo.util.MixedCollection(false, function(field){
6038 for(var i = 0, len = o.length; i < len; i++){
6039 p.fields.add(new Roo.data.Field(o[i]));
6041 f.getField = function(name){
6042 return p.fields.get(name);
6047 Roo.data.Record.AUTO_ID = 1000;
6048 Roo.data.Record.EDIT = 'edit';
6049 Roo.data.Record.REJECT = 'reject';
6050 Roo.data.Record.COMMIT = 'commit';
6052 Roo.data.Record.prototype = {
6054 * Readonly flag - true if this record has been modified.
6063 join : function(store){
6068 * Set the named field to the specified value.
6069 * @param {String} name The name of the field to set.
6070 * @param {Object} value The value to set the field to.
6072 set : function(name, value){
6073 if(this.data[name] == value){
6080 if(typeof this.modified[name] == 'undefined'){
6081 this.modified[name] = this.data[name];
6083 this.data[name] = value;
6084 if(!this.editing && this.store){
6085 this.store.afterEdit(this);
6090 * Get the value of the named field.
6091 * @param {String} name The name of the field to get the value of.
6092 * @return {Object} The value of the field.
6094 get : function(name){
6095 return this.data[name];
6099 beginEdit : function(){
6100 this.editing = true;
6105 cancelEdit : function(){
6106 this.editing = false;
6107 delete this.modified;
6111 endEdit : function(){
6112 this.editing = false;
6113 if(this.dirty && this.store){
6114 this.store.afterEdit(this);
6119 * Usually called by the {@link Roo.data.Store} which owns the Record.
6120 * Rejects all changes made to the Record since either creation, or the last commit operation.
6121 * Modified fields are reverted to their original values.
6123 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6124 * of reject operations.
6126 reject : function(){
6127 var m = this.modified;
6129 if(typeof m[n] != "function"){
6130 this.data[n] = m[n];
6134 delete this.modified;
6135 this.editing = false;
6137 this.store.afterReject(this);
6142 * Usually called by the {@link Roo.data.Store} which owns the Record.
6143 * Commits all changes made to the Record since either creation, or the last commit operation.
6145 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6146 * of commit operations.
6148 commit : function(){
6150 delete this.modified;
6151 this.editing = false;
6153 this.store.afterCommit(this);
6158 hasError : function(){
6159 return this.error != null;
6163 clearError : function(){
6168 * Creates a copy of this record.
6169 * @param {String} id (optional) A new record id if you don't want to use this record's id
6172 copy : function(newId) {
6173 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6177 * Ext JS Library 1.1.1
6178 * Copyright(c) 2006-2007, Ext JS, LLC.
6180 * Originally Released Under LGPL - original licence link has changed is not relivant.
6183 * <script type="text/javascript">
6189 * @class Roo.data.Store
6190 * @extends Roo.util.Observable
6191 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6192 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6194 * 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
6195 * has no knowledge of the format of the data returned by the Proxy.<br>
6197 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6198 * instances from the data object. These records are cached and made available through accessor functions.
6200 * Creates a new Store.
6201 * @param {Object} config A config object containing the objects needed for the Store to access data,
6202 * and read the data into Records.
6204 Roo.data.Store = function(config){
6205 this.data = new Roo.util.MixedCollection(false);
6206 this.data.getKey = function(o){
6209 this.baseParams = {};
6216 "multisort" : "_multisort"
6219 if(config && config.data){
6220 this.inlineData = config.data;
6224 Roo.apply(this, config);
6226 if(this.reader){ // reader passed
6227 this.reader = Roo.factory(this.reader, Roo.data);
6228 this.reader.xmodule = this.xmodule || false;
6229 if(!this.recordType){
6230 this.recordType = this.reader.recordType;
6232 if(this.reader.onMetaChange){
6233 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6237 if(this.recordType){
6238 this.fields = this.recordType.prototype.fields;
6244 * @event datachanged
6245 * Fires when the data cache has changed, and a widget which is using this Store
6246 * as a Record cache should refresh its view.
6247 * @param {Store} this
6252 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6253 * @param {Store} this
6254 * @param {Object} meta The JSON metadata
6259 * Fires when Records have been added to the Store
6260 * @param {Store} this
6261 * @param {Roo.data.Record[]} records The array of Records added
6262 * @param {Number} index The index at which the record(s) were added
6267 * Fires when a Record has been removed from the Store
6268 * @param {Store} this
6269 * @param {Roo.data.Record} record The Record that was removed
6270 * @param {Number} index The index at which the record was removed
6275 * Fires when a Record has been updated
6276 * @param {Store} this
6277 * @param {Roo.data.Record} record The Record that was updated
6278 * @param {String} operation The update operation being performed. Value may be one of:
6280 Roo.data.Record.EDIT
6281 Roo.data.Record.REJECT
6282 Roo.data.Record.COMMIT
6288 * Fires when the data cache has been cleared.
6289 * @param {Store} this
6294 * Fires before a request is made for a new data object. If the beforeload handler returns false
6295 * the load action will be canceled.
6296 * @param {Store} this
6297 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6301 * @event beforeloadadd
6302 * Fires after a new set of Records has been loaded.
6303 * @param {Store} this
6304 * @param {Roo.data.Record[]} records The Records that were loaded
6305 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6307 beforeloadadd : true,
6310 * Fires after a new set of Records has been loaded, before they are added to the store.
6311 * @param {Store} this
6312 * @param {Roo.data.Record[]} records The Records that were loaded
6313 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6314 * @params {Object} return from reader
6318 * @event loadexception
6319 * Fires if an exception occurs in the Proxy during loading.
6320 * Called with the signature of the Proxy's "loadexception" event.
6321 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6324 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6325 * @param {Object} load options
6326 * @param {Object} jsonData from your request (normally this contains the Exception)
6328 loadexception : true
6332 this.proxy = Roo.factory(this.proxy, Roo.data);
6333 this.proxy.xmodule = this.xmodule || false;
6334 this.relayEvents(this.proxy, ["loadexception"]);
6336 this.sortToggle = {};
6337 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6339 Roo.data.Store.superclass.constructor.call(this);
6341 if(this.inlineData){
6342 this.loadData(this.inlineData);
6343 delete this.inlineData;
6347 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6349 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6350 * without a remote query - used by combo/forms at present.
6354 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6357 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6360 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6361 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6364 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6365 * on any HTTP request
6368 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6371 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6375 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6376 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6381 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6382 * loaded or when a record is removed. (defaults to false).
6384 pruneModifiedRecords : false,
6390 * Add Records to the Store and fires the add event.
6391 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6393 add : function(records){
6394 records = [].concat(records);
6395 for(var i = 0, len = records.length; i < len; i++){
6396 records[i].join(this);
6398 var index = this.data.length;
6399 this.data.addAll(records);
6400 this.fireEvent("add", this, records, index);
6404 * Remove a Record from the Store and fires the remove event.
6405 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6407 remove : function(record){
6408 var index = this.data.indexOf(record);
6409 this.data.removeAt(index);
6410 if(this.pruneModifiedRecords){
6411 this.modified.remove(record);
6413 this.fireEvent("remove", this, record, index);
6417 * Remove all Records from the Store and fires the clear event.
6419 removeAll : function(){
6421 if(this.pruneModifiedRecords){
6424 this.fireEvent("clear", this);
6428 * Inserts Records to the Store at the given index and fires the add event.
6429 * @param {Number} index The start index at which to insert the passed Records.
6430 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6432 insert : function(index, records){
6433 records = [].concat(records);
6434 for(var i = 0, len = records.length; i < len; i++){
6435 this.data.insert(index, records[i]);
6436 records[i].join(this);
6438 this.fireEvent("add", this, records, index);
6442 * Get the index within the cache of the passed Record.
6443 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6444 * @return {Number} The index of the passed Record. Returns -1 if not found.
6446 indexOf : function(record){
6447 return this.data.indexOf(record);
6451 * Get the index within the cache of the Record with the passed id.
6452 * @param {String} id The id of the Record to find.
6453 * @return {Number} The index of the Record. Returns -1 if not found.
6455 indexOfId : function(id){
6456 return this.data.indexOfKey(id);
6460 * Get the Record with the specified id.
6461 * @param {String} id The id of the Record to find.
6462 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6464 getById : function(id){
6465 return this.data.key(id);
6469 * Get the Record at the specified index.
6470 * @param {Number} index The index of the Record to find.
6471 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6473 getAt : function(index){
6474 return this.data.itemAt(index);
6478 * Returns a range of Records between specified indices.
6479 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6480 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6481 * @return {Roo.data.Record[]} An array of Records
6483 getRange : function(start, end){
6484 return this.data.getRange(start, end);
6488 storeOptions : function(o){
6489 o = Roo.apply({}, o);
6492 this.lastOptions = o;
6496 * Loads the Record cache from the configured Proxy using the configured Reader.
6498 * If using remote paging, then the first load call must specify the <em>start</em>
6499 * and <em>limit</em> properties in the options.params property to establish the initial
6500 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6502 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6503 * and this call will return before the new data has been loaded. Perform any post-processing
6504 * in a callback function, or in a "load" event handler.</strong>
6506 * @param {Object} options An object containing properties which control loading options:<ul>
6507 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6508 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6509 * passed the following arguments:<ul>
6510 * <li>r : Roo.data.Record[]</li>
6511 * <li>options: Options object from the load call</li>
6512 * <li>success: Boolean success indicator</li></ul></li>
6513 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6514 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6517 load : function(options){
6518 options = options || {};
6519 if(this.fireEvent("beforeload", this, options) !== false){
6520 this.storeOptions(options);
6521 var p = Roo.apply(options.params || {}, this.baseParams);
6522 // if meta was not loaded from remote source.. try requesting it.
6523 if (!this.reader.metaFromRemote) {
6526 if(this.sortInfo && this.remoteSort){
6527 var pn = this.paramNames;
6528 p[pn["sort"]] = this.sortInfo.field;
6529 p[pn["dir"]] = this.sortInfo.direction;
6531 if (this.multiSort) {
6532 var pn = this.paramNames;
6533 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6536 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6541 * Reloads the Record cache from the configured Proxy using the configured Reader and
6542 * the options from the last load operation performed.
6543 * @param {Object} options (optional) An object containing properties which may override the options
6544 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6545 * the most recently used options are reused).
6547 reload : function(options){
6548 this.load(Roo.applyIf(options||{}, this.lastOptions));
6552 // Called as a callback by the Reader during a load operation.
6553 loadRecords : function(o, options, success){
6554 if(!o || success === false){
6555 if(success !== false){
6556 this.fireEvent("load", this, [], options, o);
6558 if(options.callback){
6559 options.callback.call(options.scope || this, [], options, false);
6563 // if data returned failure - throw an exception.
6564 if (o.success === false) {
6565 // show a message if no listener is registered.
6566 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6567 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6569 // loadmask wil be hooked into this..
6570 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6573 var r = o.records, t = o.totalRecords || r.length;
6575 this.fireEvent("beforeloadadd", this, r, options, o);
6577 if(!options || options.add !== true){
6578 if(this.pruneModifiedRecords){
6581 for(var i = 0, len = r.length; i < len; i++){
6585 this.data = this.snapshot;
6586 delete this.snapshot;
6589 this.data.addAll(r);
6590 this.totalLength = t;
6592 this.fireEvent("datachanged", this);
6594 this.totalLength = Math.max(t, this.data.length+r.length);
6597 this.fireEvent("load", this, r, options, o);
6598 if(options.callback){
6599 options.callback.call(options.scope || this, r, options, true);
6605 * Loads data from a passed data block. A Reader which understands the format of the data
6606 * must have been configured in the constructor.
6607 * @param {Object} data The data block from which to read the Records. The format of the data expected
6608 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6609 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6611 loadData : function(o, append){
6612 var r = this.reader.readRecords(o);
6613 this.loadRecords(r, {add: append}, true);
6617 * Gets the number of cached records.
6619 * <em>If using paging, this may not be the total size of the dataset. If the data object
6620 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6621 * the data set size</em>
6623 getCount : function(){
6624 return this.data.length || 0;
6628 * Gets the total number of records in the dataset as returned by the server.
6630 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6631 * the dataset size</em>
6633 getTotalCount : function(){
6634 return this.totalLength || 0;
6638 * Returns the sort state of the Store as an object with two properties:
6640 field {String} The name of the field by which the Records are sorted
6641 direction {String} The sort order, "ASC" or "DESC"
6644 getSortState : function(){
6645 return this.sortInfo;
6649 applySort : function(){
6650 if(this.sortInfo && !this.remoteSort){
6651 var s = this.sortInfo, f = s.field;
6652 var st = this.fields.get(f).sortType;
6653 var fn = function(r1, r2){
6654 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6655 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6657 this.data.sort(s.direction, fn);
6658 if(this.snapshot && this.snapshot != this.data){
6659 this.snapshot.sort(s.direction, fn);
6665 * Sets the default sort column and order to be used by the next load operation.
6666 * @param {String} fieldName The name of the field to sort by.
6667 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6669 setDefaultSort : function(field, dir){
6670 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6675 * If remote sorting is used, the sort is performed on the server, and the cache is
6676 * reloaded. If local sorting is used, the cache is sorted internally.
6677 * @param {String} fieldName The name of the field to sort by.
6678 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6680 sort : function(fieldName, dir){
6681 var f = this.fields.get(fieldName);
6683 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6685 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6686 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6691 this.sortToggle[f.name] = dir;
6692 this.sortInfo = {field: f.name, direction: dir};
6693 if(!this.remoteSort){
6695 this.fireEvent("datachanged", this);
6697 this.load(this.lastOptions);
6702 * Calls the specified function for each of the Records in the cache.
6703 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6704 * Returning <em>false</em> aborts and exits the iteration.
6705 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6707 each : function(fn, scope){
6708 this.data.each(fn, scope);
6712 * Gets all records modified since the last commit. Modified records are persisted across load operations
6713 * (e.g., during paging).
6714 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6716 getModifiedRecords : function(){
6717 return this.modified;
6721 createFilterFn : function(property, value, anyMatch){
6722 if(!value.exec){ // not a regex
6723 value = String(value);
6724 if(value.length == 0){
6727 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6730 return value.test(r.data[property]);
6735 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6736 * @param {String} property A field on your records
6737 * @param {Number} start The record index to start at (defaults to 0)
6738 * @param {Number} end The last record index to include (defaults to length - 1)
6739 * @return {Number} The sum
6741 sum : function(property, start, end){
6742 var rs = this.data.items, v = 0;
6744 end = (end || end === 0) ? end : rs.length-1;
6746 for(var i = start; i <= end; i++){
6747 v += (rs[i].data[property] || 0);
6753 * Filter the records by a specified property.
6754 * @param {String} field A field on your records
6755 * @param {String/RegExp} value Either a string that the field
6756 * should start with or a RegExp to test against the field
6757 * @param {Boolean} anyMatch True to match any part not just the beginning
6759 filter : function(property, value, anyMatch){
6760 var fn = this.createFilterFn(property, value, anyMatch);
6761 return fn ? this.filterBy(fn) : this.clearFilter();
6765 * Filter by a function. The specified function will be called with each
6766 * record in this data source. If the function returns true the record is included,
6767 * otherwise it is filtered.
6768 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6769 * @param {Object} scope (optional) The scope of the function (defaults to this)
6771 filterBy : function(fn, scope){
6772 this.snapshot = this.snapshot || this.data;
6773 this.data = this.queryBy(fn, scope||this);
6774 this.fireEvent("datachanged", this);
6778 * Query the records by a specified property.
6779 * @param {String} field A field on your records
6780 * @param {String/RegExp} value Either a string that the field
6781 * should start with or a RegExp to test against the field
6782 * @param {Boolean} anyMatch True to match any part not just the beginning
6783 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6785 query : function(property, value, anyMatch){
6786 var fn = this.createFilterFn(property, value, anyMatch);
6787 return fn ? this.queryBy(fn) : this.data.clone();
6791 * Query by a function. The specified function will be called with each
6792 * record in this data source. If the function returns true the record is included
6794 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6795 * @param {Object} scope (optional) The scope of the function (defaults to this)
6796 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6798 queryBy : function(fn, scope){
6799 var data = this.snapshot || this.data;
6800 return data.filterBy(fn, scope||this);
6804 * Collects unique values for a particular dataIndex from this store.
6805 * @param {String} dataIndex The property to collect
6806 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6807 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6808 * @return {Array} An array of the unique values
6810 collect : function(dataIndex, allowNull, bypassFilter){
6811 var d = (bypassFilter === true && this.snapshot) ?
6812 this.snapshot.items : this.data.items;
6813 var v, sv, r = [], l = {};
6814 for(var i = 0, len = d.length; i < len; i++){
6815 v = d[i].data[dataIndex];
6817 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6826 * Revert to a view of the Record cache with no filtering applied.
6827 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6829 clearFilter : function(suppressEvent){
6830 if(this.snapshot && this.snapshot != this.data){
6831 this.data = this.snapshot;
6832 delete this.snapshot;
6833 if(suppressEvent !== true){
6834 this.fireEvent("datachanged", this);
6840 afterEdit : function(record){
6841 if(this.modified.indexOf(record) == -1){
6842 this.modified.push(record);
6844 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6848 afterReject : function(record){
6849 this.modified.remove(record);
6850 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6854 afterCommit : function(record){
6855 this.modified.remove(record);
6856 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6860 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6861 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6863 commitChanges : function(){
6864 var m = this.modified.slice(0);
6866 for(var i = 0, len = m.length; i < len; i++){
6872 * Cancel outstanding changes on all changed records.
6874 rejectChanges : function(){
6875 var m = this.modified.slice(0);
6877 for(var i = 0, len = m.length; i < len; i++){
6882 onMetaChange : function(meta, rtype, o){
6883 this.recordType = rtype;
6884 this.fields = rtype.prototype.fields;
6885 delete this.snapshot;
6886 this.sortInfo = meta.sortInfo || this.sortInfo;
6888 this.fireEvent('metachange', this, this.reader.meta);
6891 moveIndex : function(data, type)
6893 var index = this.indexOf(data);
6895 var newIndex = index + type;
6899 this.insert(newIndex, data);
6904 * Ext JS Library 1.1.1
6905 * Copyright(c) 2006-2007, Ext JS, LLC.
6907 * Originally Released Under LGPL - original licence link has changed is not relivant.
6910 * <script type="text/javascript">
6914 * @class Roo.data.SimpleStore
6915 * @extends Roo.data.Store
6916 * Small helper class to make creating Stores from Array data easier.
6917 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6918 * @cfg {Array} fields An array of field definition objects, or field name strings.
6919 * @cfg {Array} data The multi-dimensional array of data
6921 * @param {Object} config
6923 Roo.data.SimpleStore = function(config){
6924 Roo.data.SimpleStore.superclass.constructor.call(this, {
6926 reader: new Roo.data.ArrayReader({
6929 Roo.data.Record.create(config.fields)
6931 proxy : new Roo.data.MemoryProxy(config.data)
6935 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6937 * Ext JS Library 1.1.1
6938 * Copyright(c) 2006-2007, Ext JS, LLC.
6940 * Originally Released Under LGPL - original licence link has changed is not relivant.
6943 * <script type="text/javascript">
6948 * @extends Roo.data.Store
6949 * @class Roo.data.JsonStore
6950 * Small helper class to make creating Stores for JSON data easier. <br/>
6952 var store = new Roo.data.JsonStore({
6953 url: 'get-images.php',
6955 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6958 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6959 * JsonReader and HttpProxy (unless inline data is provided).</b>
6960 * @cfg {Array} fields An array of field definition objects, or field name strings.
6962 * @param {Object} config
6964 Roo.data.JsonStore = function(c){
6965 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6966 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6967 reader: new Roo.data.JsonReader(c, c.fields)
6970 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6972 * Ext JS Library 1.1.1
6973 * Copyright(c) 2006-2007, Ext JS, LLC.
6975 * Originally Released Under LGPL - original licence link has changed is not relivant.
6978 * <script type="text/javascript">
6982 Roo.data.Field = function(config){
6983 if(typeof config == "string"){
6984 config = {name: config};
6986 Roo.apply(this, config);
6992 var st = Roo.data.SortTypes;
6993 // named sortTypes are supported, here we look them up
6994 if(typeof this.sortType == "string"){
6995 this.sortType = st[this.sortType];
6998 // set default sortType for strings and dates
7002 this.sortType = st.asUCString;
7005 this.sortType = st.asDate;
7008 this.sortType = st.none;
7013 var stripRe = /[\$,%]/g;
7015 // prebuilt conversion function for this field, instead of
7016 // switching every time we're reading a value
7018 var cv, dateFormat = this.dateFormat;
7023 cv = function(v){ return v; };
7026 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
7030 return v !== undefined && v !== null && v !== '' ?
7031 parseInt(String(v).replace(stripRe, ""), 10) : '';
7036 return v !== undefined && v !== null && v !== '' ?
7037 parseFloat(String(v).replace(stripRe, ""), 10) : '';
7042 cv = function(v){ return v === true || v === "true" || v == 1; };
7049 if(v instanceof Date){
7053 if(dateFormat == "timestamp"){
7054 return new Date(v*1000);
7056 return Date.parseDate(v, dateFormat);
7058 var parsed = Date.parse(v);
7059 return parsed ? new Date(parsed) : null;
7068 Roo.data.Field.prototype = {
7076 * Ext JS Library 1.1.1
7077 * Copyright(c) 2006-2007, Ext JS, LLC.
7079 * Originally Released Under LGPL - original licence link has changed is not relivant.
7082 * <script type="text/javascript">
7085 // Base class for reading structured data from a data source. This class is intended to be
7086 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
7089 * @class Roo.data.DataReader
7090 * Base class for reading structured data from a data source. This class is intended to be
7091 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
7094 Roo.data.DataReader = function(meta, recordType){
7098 this.recordType = recordType instanceof Array ?
7099 Roo.data.Record.create(recordType) : recordType;
7102 Roo.data.DataReader.prototype = {
7104 * Create an empty record
7105 * @param {Object} data (optional) - overlay some values
7106 * @return {Roo.data.Record} record created.
7108 newRow : function(d) {
7110 this.recordType.prototype.fields.each(function(c) {
7112 case 'int' : da[c.name] = 0; break;
7113 case 'date' : da[c.name] = new Date(); break;
7114 case 'float' : da[c.name] = 0.0; break;
7115 case 'boolean' : da[c.name] = false; break;
7116 default : da[c.name] = ""; break;
7120 return new this.recordType(Roo.apply(da, d));
7125 * Ext JS Library 1.1.1
7126 * Copyright(c) 2006-2007, Ext JS, LLC.
7128 * Originally Released Under LGPL - original licence link has changed is not relivant.
7131 * <script type="text/javascript">
7135 * @class Roo.data.DataProxy
7136 * @extends Roo.data.Observable
7137 * This class is an abstract base class for implementations which provide retrieval of
7138 * unformatted data objects.<br>
7140 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7141 * (of the appropriate type which knows how to parse the data object) to provide a block of
7142 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7144 * Custom implementations must implement the load method as described in
7145 * {@link Roo.data.HttpProxy#load}.
7147 Roo.data.DataProxy = function(){
7151 * Fires before a network request is made to retrieve a data object.
7152 * @param {Object} This DataProxy object.
7153 * @param {Object} params The params parameter to the load function.
7158 * Fires before the load method's callback is called.
7159 * @param {Object} This DataProxy object.
7160 * @param {Object} o The data object.
7161 * @param {Object} arg The callback argument object passed to the load function.
7165 * @event loadexception
7166 * Fires if an Exception occurs during data retrieval.
7167 * @param {Object} This DataProxy object.
7168 * @param {Object} o The data object.
7169 * @param {Object} arg The callback argument object passed to the load function.
7170 * @param {Object} e The Exception.
7172 loadexception : true
7174 Roo.data.DataProxy.superclass.constructor.call(this);
7177 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7180 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7184 * Ext JS Library 1.1.1
7185 * Copyright(c) 2006-2007, Ext JS, LLC.
7187 * Originally Released Under LGPL - original licence link has changed is not relivant.
7190 * <script type="text/javascript">
7193 * @class Roo.data.MemoryProxy
7194 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7195 * to the Reader when its load method is called.
7197 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7199 Roo.data.MemoryProxy = function(data){
7203 Roo.data.MemoryProxy.superclass.constructor.call(this);
7207 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7209 * Load data from the requested source (in this case an in-memory
7210 * data object passed to the constructor), read the data object into
7211 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7212 * process that block using the passed callback.
7213 * @param {Object} params This parameter is not used by the MemoryProxy class.
7214 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7215 * object into a block of Roo.data.Records.
7216 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7217 * The function must be passed <ul>
7218 * <li>The Record block object</li>
7219 * <li>The "arg" argument from the load function</li>
7220 * <li>A boolean success indicator</li>
7222 * @param {Object} scope The scope in which to call the callback
7223 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7225 load : function(params, reader, callback, scope, arg){
7226 params = params || {};
7229 result = reader.readRecords(this.data);
7231 this.fireEvent("loadexception", this, arg, null, e);
7232 callback.call(scope, null, arg, false);
7235 callback.call(scope, result, arg, true);
7239 update : function(params, records){
7244 * Ext JS Library 1.1.1
7245 * Copyright(c) 2006-2007, Ext JS, LLC.
7247 * Originally Released Under LGPL - original licence link has changed is not relivant.
7250 * <script type="text/javascript">
7253 * @class Roo.data.HttpProxy
7254 * @extends Roo.data.DataProxy
7255 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7256 * configured to reference a certain URL.<br><br>
7258 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7259 * from which the running page was served.<br><br>
7261 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7263 * Be aware that to enable the browser to parse an XML document, the server must set
7264 * the Content-Type header in the HTTP response to "text/xml".
7266 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7267 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7268 * will be used to make the request.
7270 Roo.data.HttpProxy = function(conn){
7271 Roo.data.HttpProxy.superclass.constructor.call(this);
7272 // is conn a conn config or a real conn?
7274 this.useAjax = !conn || !conn.events;
7278 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7279 // thse are take from connection...
7282 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7285 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7286 * extra parameters to each request made by this object. (defaults to undefined)
7289 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7290 * to each request made by this object. (defaults to undefined)
7293 * @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)
7296 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7299 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7305 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7309 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7310 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7311 * a finer-grained basis than the DataProxy events.
7313 getConnection : function(){
7314 return this.useAjax ? Roo.Ajax : this.conn;
7318 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7319 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7320 * process that block using the passed callback.
7321 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7322 * for the request to the remote server.
7323 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7324 * object into a block of Roo.data.Records.
7325 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7326 * The function must be passed <ul>
7327 * <li>The Record block object</li>
7328 * <li>The "arg" argument from the load function</li>
7329 * <li>A boolean success indicator</li>
7331 * @param {Object} scope The scope in which to call the callback
7332 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7334 load : function(params, reader, callback, scope, arg){
7335 if(this.fireEvent("beforeload", this, params) !== false){
7337 params : params || {},
7339 callback : callback,
7344 callback : this.loadResponse,
7348 Roo.applyIf(o, this.conn);
7349 if(this.activeRequest){
7350 Roo.Ajax.abort(this.activeRequest);
7352 this.activeRequest = Roo.Ajax.request(o);
7354 this.conn.request(o);
7357 callback.call(scope||this, null, arg, false);
7362 loadResponse : function(o, success, response){
7363 delete this.activeRequest;
7365 this.fireEvent("loadexception", this, o, response);
7366 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7371 result = o.reader.read(response);
7373 this.fireEvent("loadexception", this, o, response, e);
7374 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7378 this.fireEvent("load", this, o, o.request.arg);
7379 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7383 update : function(dataSet){
7388 updateResponse : function(dataSet){
7393 * Ext JS Library 1.1.1
7394 * Copyright(c) 2006-2007, Ext JS, LLC.
7396 * Originally Released Under LGPL - original licence link has changed is not relivant.
7399 * <script type="text/javascript">
7403 * @class Roo.data.ScriptTagProxy
7404 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7405 * other than the originating domain of the running page.<br><br>
7407 * <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
7408 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7410 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7411 * source code that is used as the source inside a <script> tag.<br><br>
7413 * In order for the browser to process the returned data, the server must wrap the data object
7414 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7415 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7416 * depending on whether the callback name was passed:
7419 boolean scriptTag = false;
7420 String cb = request.getParameter("callback");
7423 response.setContentType("text/javascript");
7425 response.setContentType("application/x-json");
7427 Writer out = response.getWriter();
7429 out.write(cb + "(");
7431 out.print(dataBlock.toJsonString());
7438 * @param {Object} config A configuration object.
7440 Roo.data.ScriptTagProxy = function(config){
7441 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7442 Roo.apply(this, config);
7443 this.head = document.getElementsByTagName("head")[0];
7446 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7448 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7450 * @cfg {String} url The URL from which to request the data object.
7453 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7457 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7458 * the server the name of the callback function set up by the load call to process the returned data object.
7459 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7460 * javascript output which calls this named function passing the data object as its only parameter.
7462 callbackParam : "callback",
7464 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7465 * name to the request.
7470 * Load data from the configured URL, read the data object into
7471 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7472 * process that block using the passed callback.
7473 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7474 * for the request to the remote server.
7475 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7476 * object into a block of Roo.data.Records.
7477 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7478 * The function must be passed <ul>
7479 * <li>The Record block object</li>
7480 * <li>The "arg" argument from the load function</li>
7481 * <li>A boolean success indicator</li>
7483 * @param {Object} scope The scope in which to call the callback
7484 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7486 load : function(params, reader, callback, scope, arg){
7487 if(this.fireEvent("beforeload", this, params) !== false){
7489 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7492 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7494 url += "&_dc=" + (new Date().getTime());
7496 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7499 cb : "stcCallback"+transId,
7500 scriptId : "stcScript"+transId,
7504 callback : callback,
7510 window[trans.cb] = function(o){
7511 conn.handleResponse(o, trans);
7514 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7516 if(this.autoAbort !== false){
7520 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7522 var script = document.createElement("script");
7523 script.setAttribute("src", url);
7524 script.setAttribute("type", "text/javascript");
7525 script.setAttribute("id", trans.scriptId);
7526 this.head.appendChild(script);
7530 callback.call(scope||this, null, arg, false);
7535 isLoading : function(){
7536 return this.trans ? true : false;
7540 * Abort the current server request.
7543 if(this.isLoading()){
7544 this.destroyTrans(this.trans);
7549 destroyTrans : function(trans, isLoaded){
7550 this.head.removeChild(document.getElementById(trans.scriptId));
7551 clearTimeout(trans.timeoutId);
7553 window[trans.cb] = undefined;
7555 delete window[trans.cb];
7558 // if hasn't been loaded, wait for load to remove it to prevent script error
7559 window[trans.cb] = function(){
7560 window[trans.cb] = undefined;
7562 delete window[trans.cb];
7569 handleResponse : function(o, trans){
7571 this.destroyTrans(trans, true);
7574 result = trans.reader.readRecords(o);
7576 this.fireEvent("loadexception", this, o, trans.arg, e);
7577 trans.callback.call(trans.scope||window, null, trans.arg, false);
7580 this.fireEvent("load", this, o, trans.arg);
7581 trans.callback.call(trans.scope||window, result, trans.arg, true);
7585 handleFailure : function(trans){
7587 this.destroyTrans(trans, false);
7588 this.fireEvent("loadexception", this, null, trans.arg);
7589 trans.callback.call(trans.scope||window, null, trans.arg, false);
7593 * Ext JS Library 1.1.1
7594 * Copyright(c) 2006-2007, Ext JS, LLC.
7596 * Originally Released Under LGPL - original licence link has changed is not relivant.
7599 * <script type="text/javascript">
7603 * @class Roo.data.JsonReader
7604 * @extends Roo.data.DataReader
7605 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7606 * based on mappings in a provided Roo.data.Record constructor.
7608 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7609 * in the reply previously.
7614 var RecordDef = Roo.data.Record.create([
7615 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7616 {name: 'occupation'} // This field will use "occupation" as the mapping.
7618 var myReader = new Roo.data.JsonReader({
7619 totalProperty: "results", // The property which contains the total dataset size (optional)
7620 root: "rows", // The property which contains an Array of row objects
7621 id: "id" // The property within each row object that provides an ID for the record (optional)
7625 * This would consume a JSON file like this:
7627 { 'results': 2, 'rows': [
7628 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7629 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7632 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7633 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7634 * paged from the remote server.
7635 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7636 * @cfg {String} root name of the property which contains the Array of row objects.
7637 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7639 * Create a new JsonReader
7640 * @param {Object} meta Metadata configuration options
7641 * @param {Object} recordType Either an Array of field definition objects,
7642 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7644 Roo.data.JsonReader = function(meta, recordType){
7647 // set some defaults:
7649 totalProperty: 'total',
7650 successProperty : 'success',
7655 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7657 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7660 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7661 * Used by Store query builder to append _requestMeta to params.
7664 metaFromRemote : false,
7666 * This method is only used by a DataProxy which has retrieved data from a remote server.
7667 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7668 * @return {Object} data A data block which is used by an Roo.data.Store object as
7669 * a cache of Roo.data.Records.
7671 read : function(response){
7672 var json = response.responseText;
7674 var o = /* eval:var:o */ eval("("+json+")");
7676 throw {message: "JsonReader.read: Json object not found"};
7682 this.metaFromRemote = true;
7683 this.meta = o.metaData;
7684 this.recordType = Roo.data.Record.create(o.metaData.fields);
7685 this.onMetaChange(this.meta, this.recordType, o);
7687 return this.readRecords(o);
7690 // private function a store will implement
7691 onMetaChange : function(meta, recordType, o){
7698 simpleAccess: function(obj, subsc) {
7705 getJsonAccessor: function(){
7707 return function(expr) {
7709 return(re.test(expr))
7710 ? new Function("obj", "return obj." + expr)
7720 * Create a data block containing Roo.data.Records from an XML document.
7721 * @param {Object} o An object which contains an Array of row objects in the property specified
7722 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7723 * which contains the total size of the dataset.
7724 * @return {Object} data A data block which is used by an Roo.data.Store object as
7725 * a cache of Roo.data.Records.
7727 readRecords : function(o){
7729 * After any data loads, the raw JSON data is available for further custom processing.
7733 var s = this.meta, Record = this.recordType,
7734 f = Record.prototype.fields, fi = f.items, fl = f.length;
7736 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7738 if(s.totalProperty) {
7739 this.getTotal = this.getJsonAccessor(s.totalProperty);
7741 if(s.successProperty) {
7742 this.getSuccess = this.getJsonAccessor(s.successProperty);
7744 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7746 var g = this.getJsonAccessor(s.id);
7747 this.getId = function(rec) {
7749 return (r === undefined || r === "") ? null : r;
7752 this.getId = function(){return null;};
7755 for(var jj = 0; jj < fl; jj++){
7757 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7758 this.ef[jj] = this.getJsonAccessor(map);
7762 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7763 if(s.totalProperty){
7764 var vt = parseInt(this.getTotal(o), 10);
7769 if(s.successProperty){
7770 var vs = this.getSuccess(o);
7771 if(vs === false || vs === 'false'){
7776 for(var i = 0; i < c; i++){
7779 var id = this.getId(n);
7780 for(var j = 0; j < fl; j++){
7782 var v = this.ef[j](n);
7784 Roo.log('missing convert for ' + f.name);
7788 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7790 var record = new Record(values, id);
7792 records[i] = record;
7798 totalRecords : totalRecords
7803 * Ext JS Library 1.1.1
7804 * Copyright(c) 2006-2007, Ext JS, LLC.
7806 * Originally Released Under LGPL - original licence link has changed is not relivant.
7809 * <script type="text/javascript">
7813 * @class Roo.data.ArrayReader
7814 * @extends Roo.data.DataReader
7815 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7816 * Each element of that Array represents a row of data fields. The
7817 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7818 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7822 var RecordDef = Roo.data.Record.create([
7823 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7824 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7826 var myReader = new Roo.data.ArrayReader({
7827 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7831 * This would consume an Array like this:
7833 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7835 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7837 * Create a new JsonReader
7838 * @param {Object} meta Metadata configuration options.
7839 * @param {Object} recordType Either an Array of field definition objects
7840 * as specified to {@link Roo.data.Record#create},
7841 * or an {@link Roo.data.Record} object
7842 * created using {@link Roo.data.Record#create}.
7844 Roo.data.ArrayReader = function(meta, recordType){
7845 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7848 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7850 * Create a data block containing Roo.data.Records from an XML document.
7851 * @param {Object} o An Array of row objects which represents the dataset.
7852 * @return {Object} data A data block which is used by an Roo.data.Store object as
7853 * a cache of Roo.data.Records.
7855 readRecords : function(o){
7856 var sid = this.meta ? this.meta.id : null;
7857 var recordType = this.recordType, fields = recordType.prototype.fields;
7860 for(var i = 0; i < root.length; i++){
7863 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7864 for(var j = 0, jlen = fields.length; j < jlen; j++){
7865 var f = fields.items[j];
7866 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7867 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7871 var record = new recordType(values, id);
7873 records[records.length] = record;
7877 totalRecords : records.length
7886 * @class Roo.bootstrap.ComboBox
7887 * @extends Roo.bootstrap.TriggerField
7888 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7889 * @cfg {Boolean} append (true|false) default false
7891 * Create a new ComboBox.
7892 * @param {Object} config Configuration options
7894 Roo.bootstrap.ComboBox = function(config){
7895 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7899 * Fires when the dropdown list is expanded
7900 * @param {Roo.bootstrap.ComboBox} combo This combo box
7905 * Fires when the dropdown list is collapsed
7906 * @param {Roo.bootstrap.ComboBox} combo This combo box
7910 * @event beforeselect
7911 * Fires before a list item is selected. Return false to cancel the selection.
7912 * @param {Roo.bootstrap.ComboBox} combo This combo box
7913 * @param {Roo.data.Record} record The data record returned from the underlying store
7914 * @param {Number} index The index of the selected item in the dropdown list
7916 'beforeselect' : true,
7919 * Fires when a list item is selected
7920 * @param {Roo.bootstrap.ComboBox} combo This combo box
7921 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7922 * @param {Number} index The index of the selected item in the dropdown list
7926 * @event beforequery
7927 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7928 * The event object passed has these properties:
7929 * @param {Roo.bootstrap.ComboBox} combo This combo box
7930 * @param {String} query The query
7931 * @param {Boolean} forceAll true to force "all" query
7932 * @param {Boolean} cancel true to cancel the query
7933 * @param {Object} e The query event object
7935 'beforequery': true,
7938 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7939 * @param {Roo.bootstrap.ComboBox} combo This combo box
7944 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7945 * @param {Roo.bootstrap.ComboBox} combo This combo box
7946 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7951 * Fires when the remove value from the combobox array
7952 * @param {Roo.bootstrap.ComboBox} combo This combo box
7959 this.selectedIndex = -1;
7960 if(this.mode == 'local'){
7961 if(config.queryDelay === undefined){
7962 this.queryDelay = 10;
7964 if(config.minChars === undefined){
7970 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7973 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7974 * rendering into an Roo.Editor, defaults to false)
7977 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7978 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7981 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7984 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7985 * the dropdown list (defaults to undefined, with no header element)
7989 * @cfg {String/Roo.Template} tpl The template to use to render the output
7993 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7995 listWidth: undefined,
7997 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7998 * mode = 'remote' or 'text' if mode = 'local')
8000 displayField: undefined,
8002 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
8003 * mode = 'remote' or 'value' if mode = 'local').
8004 * Note: use of a valueField requires the user make a selection
8005 * in order for a value to be mapped.
8007 valueField: undefined,
8011 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
8012 * field's data value (defaults to the underlying DOM element's name)
8014 hiddenName: undefined,
8016 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
8020 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
8022 selectedClass: 'active',
8025 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
8029 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
8030 * anchor positions (defaults to 'tl-bl')
8032 listAlign: 'tl-bl?',
8034 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
8038 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
8039 * query specified by the allQuery config option (defaults to 'query')
8041 triggerAction: 'query',
8043 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
8044 * (defaults to 4, does not apply if editable = false)
8048 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
8049 * delay (typeAheadDelay) if it matches a known value (defaults to false)
8053 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
8054 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
8058 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
8059 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
8063 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
8064 * when editable = true (defaults to false)
8066 selectOnFocus:false,
8068 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
8070 queryParam: 'query',
8072 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
8073 * when mode = 'remote' (defaults to 'Loading...')
8075 loadingText: 'Loading...',
8077 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
8081 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
8085 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
8086 * traditional select (defaults to true)
8090 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
8094 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
8098 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
8099 * listWidth has a higher value)
8103 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
8104 * allow the user to set arbitrary text into the field (defaults to false)
8106 forceSelection:false,
8108 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
8109 * if typeAhead = true (defaults to 250)
8111 typeAheadDelay : 250,
8113 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
8114 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
8116 valueNotFoundText : undefined,
8118 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
8123 * @cfg {Boolean} disableClear Disable showing of clear button.
8125 disableClear : false,
8127 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
8129 alwaysQuery : false,
8132 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8146 // element that contains real text value.. (when hidden is used..)
8149 initEvents: function(){
8152 throw "can not find store for combo";
8154 this.store = Roo.factory(this.store, Roo.data);
8158 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8161 if(this.hiddenName){
8163 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8165 this.hiddenField.dom.value =
8166 this.hiddenValue !== undefined ? this.hiddenValue :
8167 this.value !== undefined ? this.value : '';
8169 // prevent input submission
8170 this.el.dom.removeAttribute('name');
8171 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8176 // this.el.dom.setAttribute('autocomplete', 'off');
8179 var cls = 'x-combo-list';
8180 this.list = this.el.select('ul.dropdown-menu',true).first();
8182 //this.list = new Roo.Layer({
8183 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8186 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8187 this.list.setWidth(lw);
8189 this.list.on('mouseover', this.onViewOver, this);
8190 this.list.on('mousemove', this.onViewMove, this);
8192 this.list.on('scroll', this.onViewScroll, this);
8195 this.list.swallowEvent('mousewheel');
8196 this.assetHeight = 0;
8199 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8200 this.assetHeight += this.header.getHeight();
8203 this.innerList = this.list.createChild({cls:cls+'-inner'});
8204 this.innerList.on('mouseover', this.onViewOver, this);
8205 this.innerList.on('mousemove', this.onViewMove, this);
8206 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8208 if(this.allowBlank && !this.pageSize && !this.disableClear){
8209 this.footer = this.list.createChild({cls:cls+'-ft'});
8210 this.pageTb = new Roo.Toolbar(this.footer);
8214 this.footer = this.list.createChild({cls:cls+'-ft'});
8215 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8216 {pageSize: this.pageSize});
8220 if (this.pageTb && this.allowBlank && !this.disableClear) {
8222 this.pageTb.add(new Roo.Toolbar.Fill(), {
8223 cls: 'x-btn-icon x-btn-clear',
8229 _this.onSelect(false, -1);
8234 this.assetHeight += this.footer.getHeight();
8239 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8242 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8243 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8245 //this.view.wrapEl.setDisplayed(false);
8246 this.view.on('click', this.onViewClick, this);
8250 this.store.on('beforeload', this.onBeforeLoad, this);
8251 this.store.on('load', this.onLoad, this);
8252 this.store.on('loadexception', this.onLoadException, this);
8255 this.resizer = new Roo.Resizable(this.list, {
8256 pinned:true, handles:'se'
8258 this.resizer.on('resize', function(r, w, h){
8259 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8261 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8262 this.restrictHeight();
8264 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8268 this.editable = true;
8269 this.setEditable(false);
8274 if (typeof(this.events.add.listeners) != 'undefined') {
8276 this.addicon = this.wrap.createChild(
8277 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8279 this.addicon.on('click', function(e) {
8280 this.fireEvent('add', this);
8283 if (typeof(this.events.edit.listeners) != 'undefined') {
8285 this.editicon = this.wrap.createChild(
8286 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8288 this.editicon.setStyle('margin-left', '40px');
8290 this.editicon.on('click', function(e) {
8292 // we fire even if inothing is selected..
8293 this.fireEvent('edit', this, this.lastData );
8299 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8301 this.inKeyMode = true;
8305 "down" : function(e){
8306 if(!this.isExpanded()){
8307 this.onTriggerClick();
8309 this.inKeyMode = true;
8314 "enter" : function(e){
8319 "esc" : function(e){
8323 "tab" : function(e){
8326 if(this.fireEvent("specialkey", this, e)){
8327 this.onViewClick(false);
8335 doRelay : function(foo, bar, hname){
8336 if(hname == 'down' || this.scope.isExpanded()){
8337 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8346 this.queryDelay = Math.max(this.queryDelay || 10,
8347 this.mode == 'local' ? 10 : 250);
8350 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8353 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8355 if(this.editable !== false){
8356 this.inputEl().on("keyup", this.onKeyUp, this);
8358 if(this.forceSelection){
8359 this.on('blur', this.doForce, this);
8363 this.choices = this.el.select('ul.select2-choices', true).first();
8364 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8368 onDestroy : function(){
8370 this.view.setStore(null);
8371 this.view.el.removeAllListeners();
8372 this.view.el.remove();
8373 this.view.purgeListeners();
8376 this.list.dom.innerHTML = '';
8379 this.store.un('beforeload', this.onBeforeLoad, this);
8380 this.store.un('load', this.onLoad, this);
8381 this.store.un('loadexception', this.onLoadException, this);
8383 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8387 fireKey : function(e){
8388 if(e.isNavKeyPress() && !this.list.isVisible()){
8389 this.fireEvent("specialkey", this, e);
8394 onResize: function(w, h){
8395 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8397 // if(typeof w != 'number'){
8398 // // we do not handle it!?!?
8401 // var tw = this.trigger.getWidth();
8402 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8403 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8405 // this.inputEl().setWidth( this.adjustWidth('input', x));
8407 // //this.trigger.setStyle('left', x+'px');
8409 // if(this.list && this.listWidth === undefined){
8410 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8411 // this.list.setWidth(lw);
8412 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8420 * Allow or prevent the user from directly editing the field text. If false is passed,
8421 * the user will only be able to select from the items defined in the dropdown list. This method
8422 * is the runtime equivalent of setting the 'editable' config option at config time.
8423 * @param {Boolean} value True to allow the user to directly edit the field text
8425 setEditable : function(value){
8426 if(value == this.editable){
8429 this.editable = value;
8431 this.inputEl().dom.setAttribute('readOnly', true);
8432 this.inputEl().on('mousedown', this.onTriggerClick, this);
8433 this.inputEl().addClass('x-combo-noedit');
8435 this.inputEl().dom.setAttribute('readOnly', false);
8436 this.inputEl().un('mousedown', this.onTriggerClick, this);
8437 this.inputEl().removeClass('x-combo-noedit');
8443 onBeforeLoad : function(combo,opts){
8448 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8450 this.restrictHeight();
8451 this.selectedIndex = -1;
8455 onLoad : function(){
8457 this.hasQuery = false;
8463 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8464 this.loading.hide();
8467 if(this.store.getCount() > 0){
8469 this.restrictHeight();
8470 if(this.lastQuery == this.allQuery){
8472 this.inputEl().dom.select();
8474 if(!this.selectByValue(this.value, true)){
8475 this.select(0, true);
8479 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8480 this.taTask.delay(this.typeAheadDelay);
8484 this.onEmptyResults();
8490 onLoadException : function()
8492 this.hasQuery = false;
8494 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8495 this.loading.hide();
8499 Roo.log(this.store.reader.jsonData);
8500 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8502 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8508 onTypeAhead : function(){
8509 if(this.store.getCount() > 0){
8510 var r = this.store.getAt(0);
8511 var newValue = r.data[this.displayField];
8512 var len = newValue.length;
8513 var selStart = this.getRawValue().length;
8515 if(selStart != len){
8516 this.setRawValue(newValue);
8517 this.selectText(selStart, newValue.length);
8523 onSelect : function(record, index){
8525 if(this.fireEvent('beforeselect', this, record, index) !== false){
8527 this.setFromData(index > -1 ? record.data : false);
8530 this.fireEvent('select', this, record, index);
8535 * Returns the currently selected field value or empty string if no value is set.
8536 * @return {String} value The selected value
8538 getValue : function(){
8541 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8544 if(this.valueField){
8545 return typeof this.value != 'undefined' ? this.value : '';
8547 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8552 * Clears any text/value currently set in the field
8554 clearValue : function(){
8555 if(this.hiddenField){
8556 this.hiddenField.dom.value = '';
8559 this.setRawValue('');
8560 this.lastSelectionText = '';
8565 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8566 * will be displayed in the field. If the value does not match the data value of an existing item,
8567 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8568 * Otherwise the field will be blank (although the value will still be set).
8569 * @param {String} value The value to match
8571 setValue : function(v){
8578 if(this.valueField){
8579 var r = this.findRecord(this.valueField, v);
8581 text = r.data[this.displayField];
8582 }else if(this.valueNotFoundText !== undefined){
8583 text = this.valueNotFoundText;
8586 this.lastSelectionText = text;
8587 if(this.hiddenField){
8588 this.hiddenField.dom.value = v;
8590 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8594 * @property {Object} the last set data for the element
8599 * Sets the value of the field based on a object which is related to the record format for the store.
8600 * @param {Object} value the value to set as. or false on reset?
8602 setFromData : function(o){
8609 var dv = ''; // display value
8610 var vv = ''; // value value..
8612 if (this.displayField) {
8613 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8615 // this is an error condition!!!
8616 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8619 if(this.valueField){
8620 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8623 if(this.hiddenField){
8624 this.hiddenField.dom.value = vv;
8626 this.lastSelectionText = dv;
8627 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8631 // no hidden field.. - we store the value in 'value', but still display
8632 // display field!!!!
8633 this.lastSelectionText = dv;
8634 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8641 // overridden so that last data is reset..
8642 this.setValue(this.originalValue);
8643 this.clearInvalid();
8644 this.lastData = false;
8646 this.view.clearSelections();
8650 findRecord : function(prop, value){
8652 if(this.store.getCount() > 0){
8653 this.store.each(function(r){
8654 if(r.data[prop] == value){
8666 // returns hidden if it's set..
8667 if (!this.rendered) {return ''};
8668 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8672 onViewMove : function(e, t){
8673 this.inKeyMode = false;
8677 onViewOver : function(e, t){
8678 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8681 var item = this.view.findItemFromChild(t);
8683 var index = this.view.indexOf(item);
8684 this.select(index, false);
8689 onViewClick : function(doFocus)
8691 var index = this.view.getSelectedIndexes()[0];
8692 var r = this.store.getAt(index);
8694 this.onSelect(r, index);
8696 if(doFocus !== false && !this.blockFocus){
8697 this.inputEl().focus();
8702 restrictHeight : function(){
8703 //this.innerList.dom.style.height = '';
8704 //var inner = this.innerList.dom;
8705 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8706 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8707 //this.list.beginUpdate();
8708 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8709 this.list.alignTo(this.inputEl(), this.listAlign);
8710 //this.list.endUpdate();
8714 onEmptyResults : function(){
8719 * Returns true if the dropdown list is expanded, else false.
8721 isExpanded : function(){
8722 return this.list.isVisible();
8726 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8727 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8728 * @param {String} value The data value of the item to select
8729 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8730 * selected item if it is not currently in view (defaults to true)
8731 * @return {Boolean} True if the value matched an item in the list, else false
8733 selectByValue : function(v, scrollIntoView){
8734 if(v !== undefined && v !== null){
8735 var r = this.findRecord(this.valueField || this.displayField, v);
8737 this.select(this.store.indexOf(r), scrollIntoView);
8745 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8746 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8747 * @param {Number} index The zero-based index of the list item to select
8748 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8749 * selected item if it is not currently in view (defaults to true)
8751 select : function(index, scrollIntoView){
8752 this.selectedIndex = index;
8753 this.view.select(index);
8754 if(scrollIntoView !== false){
8755 var el = this.view.getNode(index);
8757 //this.innerList.scrollChildIntoView(el, false);
8764 selectNext : function(){
8765 var ct = this.store.getCount();
8767 if(this.selectedIndex == -1){
8769 }else if(this.selectedIndex < ct-1){
8770 this.select(this.selectedIndex+1);
8776 selectPrev : function(){
8777 var ct = this.store.getCount();
8779 if(this.selectedIndex == -1){
8781 }else if(this.selectedIndex != 0){
8782 this.select(this.selectedIndex-1);
8788 onKeyUp : function(e){
8789 if(this.editable !== false && !e.isSpecialKey()){
8790 this.lastKey = e.getKey();
8791 this.dqTask.delay(this.queryDelay);
8796 validateBlur : function(){
8797 return !this.list || !this.list.isVisible();
8801 initQuery : function(){
8802 this.doQuery(this.getRawValue());
8806 doForce : function(){
8807 if(this.el.dom.value.length > 0){
8809 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8815 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8816 * query allowing the query action to be canceled if needed.
8817 * @param {String} query The SQL query to execute
8818 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8819 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8820 * saved in the current store (defaults to false)
8822 doQuery : function(q, forceAll){
8824 if(q === undefined || q === null){
8833 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8838 forceAll = qe.forceAll;
8839 if(forceAll === true || (q.length >= this.minChars)){
8841 this.hasQuery = true;
8843 if(this.lastQuery != q || this.alwaysQuery){
8845 if(this.mode == 'local'){
8846 this.selectedIndex = -1;
8848 this.store.clearFilter();
8850 this.store.filter(this.displayField, q);
8854 this.store.baseParams[this.queryParam] = q;
8856 var options = {params : this.getParams(q)};
8860 options.params.start = this.page * this.pageSize;
8863 this.store.load(options);
8867 this.selectedIndex = -1;
8872 this.loadNext = false;
8876 getParams : function(q){
8878 //p[this.queryParam] = q;
8882 p.limit = this.pageSize;
8888 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8890 collapse : function(){
8891 if(!this.isExpanded()){
8896 Roo.get(document).un('mousedown', this.collapseIf, this);
8897 Roo.get(document).un('mousewheel', this.collapseIf, this);
8898 if (!this.editable) {
8899 Roo.get(document).un('keydown', this.listKeyPress, this);
8901 this.fireEvent('collapse', this);
8905 collapseIf : function(e){
8906 var in_combo = e.within(this.el);
8907 var in_list = e.within(this.list);
8909 if (in_combo || in_list) {
8910 //e.stopPropagation();
8919 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8921 expand : function(){
8923 if(this.isExpanded() || !this.hasFocus){
8927 this.list.alignTo(this.inputEl(), this.listAlign);
8929 Roo.get(document).on('mousedown', this.collapseIf, this);
8930 Roo.get(document).on('mousewheel', this.collapseIf, this);
8931 if (!this.editable) {
8932 Roo.get(document).on('keydown', this.listKeyPress, this);
8935 this.fireEvent('expand', this);
8939 // Implements the default empty TriggerField.onTriggerClick function
8940 onTriggerClick : function()
8942 Roo.log('trigger click');
8949 this.loadNext = false;
8951 if(this.isExpanded()){
8953 if (!this.blockFocus) {
8954 this.inputEl().focus();
8958 this.hasFocus = true;
8959 if(this.triggerAction == 'all') {
8960 this.doQuery(this.allQuery, true);
8962 this.doQuery(this.getRawValue());
8964 if (!this.blockFocus) {
8965 this.inputEl().focus();
8969 listKeyPress : function(e)
8971 //Roo.log('listkeypress');
8972 // scroll to first matching element based on key pres..
8973 if (e.isSpecialKey()) {
8976 var k = String.fromCharCode(e.getKey()).toUpperCase();
8979 var csel = this.view.getSelectedNodes();
8980 var cselitem = false;
8982 var ix = this.view.indexOf(csel[0]);
8983 cselitem = this.store.getAt(ix);
8984 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8990 this.store.each(function(v) {
8992 // start at existing selection.
8993 if (cselitem.id == v.id) {
8999 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
9000 match = this.store.indexOf(v);
9006 if (match === false) {
9007 return true; // no more action?
9010 this.view.select(match);
9011 var sn = Roo.get(this.view.getSelectedNodes()[0])
9012 //sn.scrollIntoView(sn.dom.parentNode, false);
9015 onViewScroll : function(e, t){
9017 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
9021 this.hasQuery = true;
9023 this.loading = this.list.select('.loading', true).first();
9025 if(this.loading === null){
9026 this.list.createChild({
9028 cls: 'loading select2-more-results select2-active',
9029 html: 'Loading more results...'
9032 this.loading = this.list.select('.loading', true).first();
9034 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
9036 this.loading.hide();
9039 this.loading.show();
9044 this.loadNext = true;
9046 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
9051 addItem : function(o)
9053 var dv = ''; // display value
9055 if (this.displayField) {
9056 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
9058 // this is an error condition!!!
9059 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
9066 var choice = this.choices.createChild({
9068 cls: 'select2-search-choice',
9077 cls: 'select2-search-choice-close',
9082 }, this.searchField);
9084 var close = choice.select('a.select2-search-choice-close', true).first()
9086 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
9093 this.inputEl().dom.value = '';
9097 onRemoveItem : function(e, _self, o)
9099 Roo.log('remove item');
9100 var index = this.item.indexOf(o.data) * 1;
9103 Roo.log('not this item?!');
9107 this.item.splice(index, 1);
9112 this.fireEvent('remove', this);
9116 syncValue : function()
9118 if(!this.item.length){
9125 Roo.each(this.item, function(i){
9126 if(_this.valueField){
9127 value.push(i[_this.valueField]);
9134 this.value = value.join(',');
9136 if(this.hiddenField){
9137 this.hiddenField.dom.value = this.value;
9141 clearItem : function()
9149 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9159 * @cfg {Boolean} grow
9163 * @cfg {Number} growMin
9167 * @cfg {Number} growMax
9177 * Ext JS Library 1.1.1
9178 * Copyright(c) 2006-2007, Ext JS, LLC.
9180 * Originally Released Under LGPL - original licence link has changed is not relivant.
9183 * <script type="text/javascript">
9188 * @extends Roo.util.Observable
9189 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9190 * This class also supports single and multi selection modes. <br>
9191 * Create a data model bound view:
9193 var store = new Roo.data.Store(...);
9195 var view = new Roo.View({
9197 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9200 selectedClass: "ydataview-selected",
9204 // listen for node click?
9205 view.on("click", function(vw, index, node, e){
9206 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9210 dataModel.load("foobar.xml");
9212 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9214 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9215 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9217 * Note: old style constructor is still suported (container, template, config)
9221 * @param {Object} config The config object
9224 Roo.View = function(config, depreciated_tpl, depreciated_config){
9226 if (typeof(depreciated_tpl) == 'undefined') {
9227 // new way.. - universal constructor.
9228 Roo.apply(this, config);
9229 this.el = Roo.get(this.el);
9232 this.el = Roo.get(config);
9233 this.tpl = depreciated_tpl;
9234 Roo.apply(this, depreciated_config);
9236 this.wrapEl = this.el.wrap().wrap();
9237 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9240 if(typeof(this.tpl) == "string"){
9241 this.tpl = new Roo.Template(this.tpl);
9243 // support xtype ctors..
9244 this.tpl = new Roo.factory(this.tpl, Roo);
9256 * @event beforeclick
9257 * Fires before a click is processed. Returns false to cancel the default action.
9258 * @param {Roo.View} this
9259 * @param {Number} index The index of the target node
9260 * @param {HTMLElement} node The target node
9261 * @param {Roo.EventObject} e The raw event object
9263 "beforeclick" : true,
9266 * Fires when a template node is clicked.
9267 * @param {Roo.View} this
9268 * @param {Number} index The index of the target node
9269 * @param {HTMLElement} node The target node
9270 * @param {Roo.EventObject} e The raw event object
9275 * Fires when a template node is double clicked.
9276 * @param {Roo.View} this
9277 * @param {Number} index The index of the target node
9278 * @param {HTMLElement} node The target node
9279 * @param {Roo.EventObject} e The raw event object
9283 * @event contextmenu
9284 * Fires when a template node is right clicked.
9285 * @param {Roo.View} this
9286 * @param {Number} index The index of the target node
9287 * @param {HTMLElement} node The target node
9288 * @param {Roo.EventObject} e The raw event object
9290 "contextmenu" : true,
9292 * @event selectionchange
9293 * Fires when the selected nodes change.
9294 * @param {Roo.View} this
9295 * @param {Array} selections Array of the selected nodes
9297 "selectionchange" : true,
9300 * @event beforeselect
9301 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9302 * @param {Roo.View} this
9303 * @param {HTMLElement} node The node to be selected
9304 * @param {Array} selections Array of currently selected nodes
9306 "beforeselect" : true,
9308 * @event preparedata
9309 * Fires on every row to render, to allow you to change the data.
9310 * @param {Roo.View} this
9311 * @param {Object} data to be rendered (change this)
9313 "preparedata" : true
9321 "click": this.onClick,
9322 "dblclick": this.onDblClick,
9323 "contextmenu": this.onContextMenu,
9327 this.selections = [];
9329 this.cmp = new Roo.CompositeElementLite([]);
9331 this.store = Roo.factory(this.store, Roo.data);
9332 this.setStore(this.store, true);
9335 if ( this.footer && this.footer.xtype) {
9337 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9339 this.footer.dataSource = this.store
9340 this.footer.container = fctr;
9341 this.footer = Roo.factory(this.footer, Roo);
9342 fctr.insertFirst(this.el);
9344 // this is a bit insane - as the paging toolbar seems to detach the el..
9345 // dom.parentNode.parentNode.parentNode
9346 // they get detached?
9350 Roo.View.superclass.constructor.call(this);
9355 Roo.extend(Roo.View, Roo.util.Observable, {
9358 * @cfg {Roo.data.Store} store Data store to load data from.
9363 * @cfg {String|Roo.Element} el The container element.
9368 * @cfg {String|Roo.Template} tpl The template used by this View
9372 * @cfg {String} dataName the named area of the template to use as the data area
9373 * Works with domtemplates roo-name="name"
9377 * @cfg {String} selectedClass The css class to add to selected nodes
9379 selectedClass : "x-view-selected",
9381 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9386 * @cfg {String} text to display on mask (default Loading)
9390 * @cfg {Boolean} multiSelect Allow multiple selection
9392 multiSelect : false,
9394 * @cfg {Boolean} singleSelect Allow single selection
9396 singleSelect: false,
9399 * @cfg {Boolean} toggleSelect - selecting
9401 toggleSelect : false,
9404 * Returns the element this view is bound to.
9405 * @return {Roo.Element}
9414 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9416 refresh : function(){
9420 // if we are using something like 'domtemplate', then
9421 // the what gets used is:
9422 // t.applySubtemplate(NAME, data, wrapping data..)
9423 // the outer template then get' applied with
9424 // the store 'extra data'
9425 // and the body get's added to the
9426 // roo-name="data" node?
9427 // <span class='roo-tpl-{name}'></span> ?????
9431 this.clearSelections();
9434 var records = this.store.getRange();
9435 if(records.length < 1) {
9437 // is this valid?? = should it render a template??
9439 this.el.update(this.emptyText);
9443 if (this.dataName) {
9444 this.el.update(t.apply(this.store.meta)); //????
9445 el = this.el.child('.roo-tpl-' + this.dataName);
9448 for(var i = 0, len = records.length; i < len; i++){
9449 var data = this.prepareData(records[i].data, i, records[i]);
9450 this.fireEvent("preparedata", this, data, i, records[i]);
9451 html[html.length] = Roo.util.Format.trim(
9453 t.applySubtemplate(this.dataName, data, this.store.meta) :
9460 el.update(html.join(""));
9461 this.nodes = el.dom.childNodes;
9462 this.updateIndexes(0);
9467 * Function to override to reformat the data that is sent to
9468 * the template for each node.
9469 * DEPRICATED - use the preparedata event handler.
9470 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9471 * a JSON object for an UpdateManager bound view).
9473 prepareData : function(data, index, record)
9475 this.fireEvent("preparedata", this, data, index, record);
9479 onUpdate : function(ds, record){
9480 Roo.log('on update');
9481 this.clearSelections();
9482 var index = this.store.indexOf(record);
9483 var n = this.nodes[index];
9484 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9485 n.parentNode.removeChild(n);
9486 this.updateIndexes(index, index);
9492 onAdd : function(ds, records, index)
9494 Roo.log(['on Add', ds, records, index] );
9495 this.clearSelections();
9496 if(this.nodes.length == 0){
9500 var n = this.nodes[index];
9501 for(var i = 0, len = records.length; i < len; i++){
9502 var d = this.prepareData(records[i].data, i, records[i]);
9504 this.tpl.insertBefore(n, d);
9507 this.tpl.append(this.el, d);
9510 this.updateIndexes(index);
9513 onRemove : function(ds, record, index){
9514 Roo.log('onRemove');
9515 this.clearSelections();
9516 var el = this.dataName ?
9517 this.el.child('.roo-tpl-' + this.dataName) :
9520 el.dom.removeChild(this.nodes[index]);
9521 this.updateIndexes(index);
9525 * Refresh an individual node.
9526 * @param {Number} index
9528 refreshNode : function(index){
9529 this.onUpdate(this.store, this.store.getAt(index));
9532 updateIndexes : function(startIndex, endIndex){
9533 var ns = this.nodes;
9534 startIndex = startIndex || 0;
9535 endIndex = endIndex || ns.length - 1;
9536 for(var i = startIndex; i <= endIndex; i++){
9537 ns[i].nodeIndex = i;
9542 * Changes the data store this view uses and refresh the view.
9543 * @param {Store} store
9545 setStore : function(store, initial){
9546 if(!initial && this.store){
9547 this.store.un("datachanged", this.refresh);
9548 this.store.un("add", this.onAdd);
9549 this.store.un("remove", this.onRemove);
9550 this.store.un("update", this.onUpdate);
9551 this.store.un("clear", this.refresh);
9552 this.store.un("beforeload", this.onBeforeLoad);
9553 this.store.un("load", this.onLoad);
9554 this.store.un("loadexception", this.onLoad);
9558 store.on("datachanged", this.refresh, this);
9559 store.on("add", this.onAdd, this);
9560 store.on("remove", this.onRemove, this);
9561 store.on("update", this.onUpdate, this);
9562 store.on("clear", this.refresh, this);
9563 store.on("beforeload", this.onBeforeLoad, this);
9564 store.on("load", this.onLoad, this);
9565 store.on("loadexception", this.onLoad, this);
9573 * onbeforeLoad - masks the loading area.
9576 onBeforeLoad : function(store,opts)
9578 Roo.log('onBeforeLoad');
9582 this.el.mask(this.mask ? this.mask : "Loading" );
9584 onLoad : function ()
9591 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9592 * @param {HTMLElement} node
9593 * @return {HTMLElement} The template node
9595 findItemFromChild : function(node){
9596 var el = this.dataName ?
9597 this.el.child('.roo-tpl-' + this.dataName,true) :
9600 if(!node || node.parentNode == el){
9603 var p = node.parentNode;
9604 while(p && p != el){
9605 if(p.parentNode == el){
9614 onClick : function(e){
9615 var item = this.findItemFromChild(e.getTarget());
9617 var index = this.indexOf(item);
9618 if(this.onItemClick(item, index, e) !== false){
9619 this.fireEvent("click", this, index, item, e);
9622 this.clearSelections();
9627 onContextMenu : function(e){
9628 var item = this.findItemFromChild(e.getTarget());
9630 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9635 onDblClick : function(e){
9636 var item = this.findItemFromChild(e.getTarget());
9638 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9642 onItemClick : function(item, index, e)
9644 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9647 if (this.toggleSelect) {
9648 var m = this.isSelected(item) ? 'unselect' : 'select';
9651 _t[m](item, true, false);
9654 if(this.multiSelect || this.singleSelect){
9655 if(this.multiSelect && e.shiftKey && this.lastSelection){
9656 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9658 this.select(item, this.multiSelect && e.ctrlKey);
9659 this.lastSelection = item;
9667 * Get the number of selected nodes.
9670 getSelectionCount : function(){
9671 return this.selections.length;
9675 * Get the currently selected nodes.
9676 * @return {Array} An array of HTMLElements
9678 getSelectedNodes : function(){
9679 return this.selections;
9683 * Get the indexes of the selected nodes.
9686 getSelectedIndexes : function(){
9687 var indexes = [], s = this.selections;
9688 for(var i = 0, len = s.length; i < len; i++){
9689 indexes.push(s[i].nodeIndex);
9695 * Clear all selections
9696 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9698 clearSelections : function(suppressEvent){
9699 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9700 this.cmp.elements = this.selections;
9701 this.cmp.removeClass(this.selectedClass);
9702 this.selections = [];
9704 this.fireEvent("selectionchange", this, this.selections);
9710 * Returns true if the passed node is selected
9711 * @param {HTMLElement/Number} node The node or node index
9714 isSelected : function(node){
9715 var s = this.selections;
9719 node = this.getNode(node);
9720 return s.indexOf(node) !== -1;
9725 * @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
9726 * @param {Boolean} keepExisting (optional) true to keep existing selections
9727 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9729 select : function(nodeInfo, keepExisting, suppressEvent){
9730 if(nodeInfo instanceof Array){
9732 this.clearSelections(true);
9734 for(var i = 0, len = nodeInfo.length; i < len; i++){
9735 this.select(nodeInfo[i], true, true);
9739 var node = this.getNode(nodeInfo);
9740 if(!node || this.isSelected(node)){
9741 return; // already selected.
9744 this.clearSelections(true);
9746 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9747 Roo.fly(node).addClass(this.selectedClass);
9748 this.selections.push(node);
9750 this.fireEvent("selectionchange", this, this.selections);
9758 * @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
9759 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9760 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9762 unselect : function(nodeInfo, keepExisting, suppressEvent)
9764 if(nodeInfo instanceof Array){
9765 Roo.each(this.selections, function(s) {
9766 this.unselect(s, nodeInfo);
9770 var node = this.getNode(nodeInfo);
9771 if(!node || !this.isSelected(node)){
9772 Roo.log("not selected");
9773 return; // not selected.
9777 Roo.each(this.selections, function(s) {
9779 Roo.fly(node).removeClass(this.selectedClass);
9786 this.selections= ns;
9787 this.fireEvent("selectionchange", this, this.selections);
9791 * Gets a template node.
9792 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9793 * @return {HTMLElement} The node or null if it wasn't found
9795 getNode : function(nodeInfo){
9796 if(typeof nodeInfo == "string"){
9797 return document.getElementById(nodeInfo);
9798 }else if(typeof nodeInfo == "number"){
9799 return this.nodes[nodeInfo];
9805 * Gets a range template nodes.
9806 * @param {Number} startIndex
9807 * @param {Number} endIndex
9808 * @return {Array} An array of nodes
9810 getNodes : function(start, end){
9811 var ns = this.nodes;
9813 end = typeof end == "undefined" ? ns.length - 1 : end;
9816 for(var i = start; i <= end; i++){
9820 for(var i = start; i >= end; i--){
9828 * Finds the index of the passed node
9829 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9830 * @return {Number} The index of the node or -1
9832 indexOf : function(node){
9833 node = this.getNode(node);
9834 if(typeof node.nodeIndex == "number"){
9835 return node.nodeIndex;
9837 var ns = this.nodes;
9838 for(var i = 0, len = ns.length; i < len; i++){
9849 * based on jquery fullcalendar
9853 Roo.bootstrap = Roo.bootstrap || {};
9855 * @class Roo.bootstrap.Calendar
9856 * @extends Roo.bootstrap.Component
9857 * Bootstrap Calendar class
9858 * @cfg {Boolean} loadMask (true|false) default false
9861 * Create a new Container
9862 * @param {Object} config The config object
9867 Roo.bootstrap.Calendar = function(config){
9868 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9872 * Fires when a date is selected
9873 * @param {DatePicker} this
9874 * @param {Date} date The selected date
9878 * @event monthchange
9879 * Fires when the displayed month changes
9880 * @param {DatePicker} this
9881 * @param {Date} date The selected month
9883 'monthchange': true,
9886 * Fires when mouse over an event
9887 * @param {Calendar} this
9888 * @param {event} Event
9893 * Fires when the mouse leaves an
9894 * @param {Calendar} this
9900 * Fires when the mouse click an
9901 * @param {Calendar} this
9910 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9913 * @cfg {Number} startDay
9914 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9920 getAutoCreate : function(){
9923 var fc_button = function(name, corner, style, content ) {
9924 return Roo.apply({},{
9926 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9928 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9931 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9939 style : 'width:100%',
9946 cls : 'fc-header-left',
9948 fc_button('prev', 'left', 'arrow', '‹' ),
9949 fc_button('next', 'right', 'arrow', '›' ),
9950 { tag: 'span', cls: 'fc-header-space' },
9951 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9959 cls : 'fc-header-center',
9963 cls: 'fc-header-title',
9966 html : 'month / year'
9974 cls : 'fc-header-right',
9976 /* fc_button('month', 'left', '', 'month' ),
9977 fc_button('week', '', '', 'week' ),
9978 fc_button('day', 'right', '', 'day' )
9990 var cal_heads = function() {
9992 // fixme - handle this.
9994 for (var i =0; i < Date.dayNames.length; i++) {
9995 var d = Date.dayNames[i];
9998 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9999 html : d.substring(0,3)
10003 ret[0].cls += ' fc-first';
10004 ret[6].cls += ' fc-last';
10007 var cal_cell = function(n) {
10010 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
10015 cls: 'fc-day-number',
10019 cls: 'fc-day-content',
10023 style: 'position: relative;' // height: 17px;
10035 var cal_rows = function() {
10038 for (var r = 0; r < 6; r++) {
10045 for (var i =0; i < Date.dayNames.length; i++) {
10046 var d = Date.dayNames[i];
10047 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
10050 row.cn[0].cls+=' fc-first';
10051 row.cn[0].cn[0].style = 'min-height:90px';
10052 row.cn[6].cls+=' fc-last';
10056 ret[0].cls += ' fc-first';
10057 ret[4].cls += ' fc-prev-last';
10058 ret[5].cls += ' fc-last';
10065 cls: 'fc-border-separate',
10066 style : 'width:100%',
10074 cls : 'fc-first fc-last',
10092 cls : 'fc-content',
10093 style : "position: relative;",
10096 cls : 'fc-view fc-view-month fc-grid',
10097 style : 'position: relative',
10098 unselectable : 'on',
10101 cls : 'fc-event-container',
10102 style : 'position:absolute;z-index:8;top:0;left:0;'
10120 initEvents : function()
10123 throw "can not find store for calendar";
10129 style: "text-align:center",
10133 style: "background-color:white;width:50%;margin:250 auto",
10137 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10148 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10150 var size = this.el.select('.fc-content', true).first().getSize();
10151 this.maskEl.setSize(size.width, size.height);
10152 this.maskEl.enableDisplayMode("block");
10153 if(!this.loadMask){
10154 this.maskEl.hide();
10157 this.store = Roo.factory(this.store, Roo.data);
10158 this.store.on('load', this.onLoad, this);
10159 this.store.on('beforeload', this.onBeforeLoad, this);
10163 this.cells = this.el.select('.fc-day',true);
10164 //Roo.log(this.cells);
10165 this.textNodes = this.el.query('.fc-day-number');
10166 this.cells.addClassOnOver('fc-state-hover');
10168 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10169 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10170 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10171 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10173 this.on('monthchange', this.onMonthChange, this);
10175 this.update(new Date().clearTime());
10178 resize : function() {
10179 var sz = this.el.getSize();
10181 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10182 this.el.select('.fc-day-content div',true).setHeight(34);
10187 showPrevMonth : function(e){
10188 this.update(this.activeDate.add("mo", -1));
10190 showToday : function(e){
10191 this.update(new Date().clearTime());
10194 showNextMonth : function(e){
10195 this.update(this.activeDate.add("mo", 1));
10199 showPrevYear : function(){
10200 this.update(this.activeDate.add("y", -1));
10204 showNextYear : function(){
10205 this.update(this.activeDate.add("y", 1));
10210 update : function(date)
10212 var vd = this.activeDate;
10213 this.activeDate = date;
10214 // if(vd && this.el){
10215 // var t = date.getTime();
10216 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10217 // Roo.log('using add remove');
10219 // this.fireEvent('monthchange', this, date);
10221 // this.cells.removeClass("fc-state-highlight");
10222 // this.cells.each(function(c){
10223 // if(c.dateValue == t){
10224 // c.addClass("fc-state-highlight");
10225 // setTimeout(function(){
10226 // try{c.dom.firstChild.focus();}catch(e){}
10236 var days = date.getDaysInMonth();
10238 var firstOfMonth = date.getFirstDateOfMonth();
10239 var startingPos = firstOfMonth.getDay()-this.startDay;
10241 if(startingPos < this.startDay){
10245 var pm = date.add(Date.MONTH, -1);
10246 var prevStart = pm.getDaysInMonth()-startingPos;
10248 this.cells = this.el.select('.fc-day',true);
10249 this.textNodes = this.el.query('.fc-day-number');
10250 this.cells.addClassOnOver('fc-state-hover');
10252 var cells = this.cells.elements;
10253 var textEls = this.textNodes;
10255 Roo.each(cells, function(cell){
10256 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10259 days += startingPos;
10261 // convert everything to numbers so it's fast
10262 var day = 86400000;
10263 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10266 //Roo.log(prevStart);
10268 var today = new Date().clearTime().getTime();
10269 var sel = date.clearTime().getTime();
10270 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10271 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10272 var ddMatch = this.disabledDatesRE;
10273 var ddText = this.disabledDatesText;
10274 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10275 var ddaysText = this.disabledDaysText;
10276 var format = this.format;
10278 var setCellClass = function(cal, cell){
10280 //Roo.log('set Cell Class');
10282 var t = d.getTime();
10286 cell.dateValue = t;
10288 cell.className += " fc-today";
10289 cell.className += " fc-state-highlight";
10290 cell.title = cal.todayText;
10293 // disable highlight in other month..
10294 //cell.className += " fc-state-highlight";
10299 cell.className = " fc-state-disabled";
10300 cell.title = cal.minText;
10304 cell.className = " fc-state-disabled";
10305 cell.title = cal.maxText;
10309 if(ddays.indexOf(d.getDay()) != -1){
10310 cell.title = ddaysText;
10311 cell.className = " fc-state-disabled";
10314 if(ddMatch && format){
10315 var fvalue = d.dateFormat(format);
10316 if(ddMatch.test(fvalue)){
10317 cell.title = ddText.replace("%0", fvalue);
10318 cell.className = " fc-state-disabled";
10322 if (!cell.initialClassName) {
10323 cell.initialClassName = cell.dom.className;
10326 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10331 for(; i < startingPos; i++) {
10332 textEls[i].innerHTML = (++prevStart);
10333 d.setDate(d.getDate()+1);
10335 cells[i].className = "fc-past fc-other-month";
10336 setCellClass(this, cells[i]);
10341 for(; i < days; i++){
10342 intDay = i - startingPos + 1;
10343 textEls[i].innerHTML = (intDay);
10344 d.setDate(d.getDate()+1);
10346 cells[i].className = ''; // "x-date-active";
10347 setCellClass(this, cells[i]);
10351 for(; i < 42; i++) {
10352 textEls[i].innerHTML = (++extraDays);
10353 d.setDate(d.getDate()+1);
10355 cells[i].className = "fc-future fc-other-month";
10356 setCellClass(this, cells[i]);
10359 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10361 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10363 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10364 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10366 if(totalRows != 6){
10367 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10368 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10371 this.fireEvent('monthchange', this, date);
10375 if(!this.internalRender){
10376 var main = this.el.dom.firstChild;
10377 var w = main.offsetWidth;
10378 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10379 Roo.fly(main).setWidth(w);
10380 this.internalRender = true;
10381 // opera does not respect the auto grow header center column
10382 // then, after it gets a width opera refuses to recalculate
10383 // without a second pass
10384 if(Roo.isOpera && !this.secondPass){
10385 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10386 this.secondPass = true;
10387 this.update.defer(10, this, [date]);
10394 findCell : function(dt) {
10395 dt = dt.clearTime().getTime();
10397 this.cells.each(function(c){
10398 //Roo.log("check " +c.dateValue + '?=' + dt);
10399 if(c.dateValue == dt){
10409 findCells : function(ev) {
10410 var s = ev.start.clone().clearTime().getTime();
10412 var e= ev.end.clone().clearTime().getTime();
10415 this.cells.each(function(c){
10416 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10418 if(c.dateValue > e){
10421 if(c.dateValue < s){
10430 findBestRow: function(cells)
10434 for (var i =0 ; i < cells.length;i++) {
10435 ret = Math.max(cells[i].rows || 0,ret);
10442 addItem : function(ev)
10444 // look for vertical location slot in
10445 var cells = this.findCells(ev);
10447 ev.row = this.findBestRow(cells);
10449 // work out the location.
10453 for(var i =0; i < cells.length; i++) {
10461 if (crow.start.getY() == cells[i].getY()) {
10463 crow.end = cells[i];
10479 for (var i = 0; i < cells.length;i++) {
10480 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10484 this.calevents.push(ev);
10487 clearEvents: function() {
10489 if(!this.calevents){
10493 Roo.each(this.cells.elements, function(c){
10497 Roo.each(this.calevents, function(e) {
10498 Roo.each(e.els, function(el) {
10499 el.un('mouseenter' ,this.onEventEnter, this);
10500 el.un('mouseleave' ,this.onEventLeave, this);
10507 renderEvents: function()
10509 // first make sure there is enough space..
10511 this.cells.each(function(c) {
10513 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10516 for (var e = 0; e < this.calevents.length; e++) {
10517 var ev = this.calevents[e];
10518 var cells = ev.cells;
10519 var rows = ev.rows;
10521 for(var i =0; i < rows.length; i++) {
10524 // how many rows should it span..
10527 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10528 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10530 unselectable : "on",
10533 cls: 'fc-event-inner',
10537 // cls: 'fc-event-time',
10538 // html : cells.length > 1 ? '' : ev.time
10542 cls: 'fc-event-title',
10543 html : String.format('{0}', ev.title)
10550 cls: 'ui-resizable-handle ui-resizable-e',
10551 html : '  '
10557 cfg.cls += ' fc-event-start';
10559 if ((i+1) == rows.length) {
10560 cfg.cls += ' fc-event-end';
10563 var ctr = this.el.select('.fc-event-container',true).first();
10564 var cg = ctr.createChild(cfg);
10566 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10567 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10568 cg.on('click', this.onEventClick, this, ev);
10572 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10573 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10575 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10576 cg.setWidth(ebox.right - sbox.x -2);
10584 onEventEnter: function (e, el,event,d) {
10585 this.fireEvent('evententer', this, el, event);
10588 onEventLeave: function (e, el,event,d) {
10589 this.fireEvent('eventleave', this, el, event);
10592 onEventClick: function (e, el,event,d) {
10593 this.fireEvent('eventclick', this, el, event);
10596 onMonthChange: function () {
10600 onLoad: function ()
10602 this.calevents = [];
10605 if(this.store.getCount() > 0){
10606 this.store.data.each(function(d){
10609 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10610 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10611 time : d.data.start_time,
10612 title : d.data.title,
10613 description : d.data.description,
10614 venue : d.data.venue
10619 this.renderEvents();
10622 this.maskEl.hide();
10626 onBeforeLoad: function()
10628 this.clearEvents();
10631 this.maskEl.show();
10645 * @class Roo.bootstrap.Popover
10646 * @extends Roo.bootstrap.Component
10647 * Bootstrap Popover class
10648 * @cfg {String} html contents of the popover (or false to use children..)
10649 * @cfg {String} title of popover (or false to hide)
10650 * @cfg {String} placement how it is placed
10651 * @cfg {String} trigger click || hover (or false to trigger manually)
10652 * @cfg {String} over what (parent or false to trigger manually.)
10655 * Create a new Popover
10656 * @param {Object} config The config object
10659 Roo.bootstrap.Popover = function(config){
10660 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10663 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10665 title: 'Fill in a title',
10668 placement : 'right',
10669 trigger : 'hover', // hover
10673 can_build_overlaid : false,
10675 getChildContainer : function()
10677 return this.el.select('.popover-content',true).first();
10680 getAutoCreate : function(){
10681 Roo.log('make popover?');
10683 cls : 'popover roo-dynamic',
10684 style: 'display:block',
10690 cls : 'popover-inner',
10694 cls: 'popover-title',
10698 cls : 'popover-content',
10709 setTitle: function(str)
10711 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10713 setContent: function(str)
10715 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10717 // as it get's added to the bottom of the page.
10718 onRender : function(ct, position)
10720 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10722 var cfg = Roo.apply({}, this.getAutoCreate());
10726 cfg.cls += ' ' + this.cls;
10729 cfg.style = this.style;
10731 Roo.log("adding to ")
10732 this.el = Roo.get(document.body).createChild(cfg, position);
10738 initEvents : function()
10740 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10741 this.el.enableDisplayMode('block');
10743 if (this.over === false) {
10746 if (this.triggers === false) {
10749 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10750 var triggers = this.trigger ? this.trigger.split(' ') : [];
10751 Roo.each(triggers, function(trigger) {
10753 if (trigger == 'click') {
10754 on_el.on('click', this.toggle, this);
10755 } else if (trigger != 'manual') {
10756 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10757 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10759 on_el.on(eventIn ,this.enter, this);
10760 on_el.on(eventOut, this.leave, this);
10771 toggle : function () {
10772 this.hoverState == 'in' ? this.leave() : this.enter();
10775 enter : function () {
10778 clearTimeout(this.timeout);
10780 this.hoverState = 'in'
10782 if (!this.delay || !this.delay.show) {
10787 this.timeout = setTimeout(function () {
10788 if (_t.hoverState == 'in') {
10791 }, this.delay.show)
10793 leave : function() {
10794 clearTimeout(this.timeout);
10796 this.hoverState = 'out'
10798 if (!this.delay || !this.delay.hide) {
10803 this.timeout = setTimeout(function () {
10804 if (_t.hoverState == 'out') {
10807 }, this.delay.hide)
10810 show : function (on_el)
10813 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10816 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10817 if (this.html !== false) {
10818 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10820 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10821 if (!this.title.length) {
10822 this.el.select('.popover-title',true).hide();
10825 var placement = typeof this.placement == 'function' ?
10826 this.placement.call(this, this.el, on_el) :
10829 var autoToken = /\s?auto?\s?/i;
10830 var autoPlace = autoToken.test(placement);
10832 placement = placement.replace(autoToken, '') || 'top';
10836 //this.el.setXY([0,0]);
10838 this.el.dom.style.display='block';
10839 this.el.addClass(placement);
10841 //this.el.appendTo(on_el);
10843 var p = this.getPosition();
10844 var box = this.el.getBox();
10849 var align = Roo.bootstrap.Popover.alignment[placement]
10850 this.el.alignTo(on_el, align[0],align[1]);
10851 //var arrow = this.el.select('.arrow',true).first();
10852 //arrow.set(align[2],
10854 this.el.addClass('in');
10855 this.hoverState = null;
10857 if (this.el.hasClass('fade')) {
10864 this.el.setXY([0,0]);
10865 this.el.removeClass('in');
10872 Roo.bootstrap.Popover.alignment = {
10873 'left' : ['r-l', [-10,0], 'right'],
10874 'right' : ['l-r', [10,0], 'left'],
10875 'bottom' : ['t-b', [0,10], 'top'],
10876 'top' : [ 'b-t', [0,-10], 'bottom']
10887 * @class Roo.bootstrap.Progress
10888 * @extends Roo.bootstrap.Component
10889 * Bootstrap Progress class
10890 * @cfg {Boolean} striped striped of the progress bar
10891 * @cfg {Boolean} active animated of the progress bar
10895 * Create a new Progress
10896 * @param {Object} config The config object
10899 Roo.bootstrap.Progress = function(config){
10900 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10903 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10908 getAutoCreate : function(){
10916 cfg.cls += ' progress-striped';
10920 cfg.cls += ' active';
10939 * @class Roo.bootstrap.ProgressBar
10940 * @extends Roo.bootstrap.Component
10941 * Bootstrap ProgressBar class
10942 * @cfg {Number} aria_valuenow aria-value now
10943 * @cfg {Number} aria_valuemin aria-value min
10944 * @cfg {Number} aria_valuemax aria-value max
10945 * @cfg {String} label label for the progress bar
10946 * @cfg {String} panel (success | info | warning | danger )
10947 * @cfg {String} role role of the progress bar
10948 * @cfg {String} sr_only text
10952 * Create a new ProgressBar
10953 * @param {Object} config The config object
10956 Roo.bootstrap.ProgressBar = function(config){
10957 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10960 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10964 aria_valuemax : 100,
10970 getAutoCreate : function()
10975 cls: 'progress-bar',
10976 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10988 cfg.role = this.role;
10991 if(this.aria_valuenow){
10992 cfg['aria-valuenow'] = this.aria_valuenow;
10995 if(this.aria_valuemin){
10996 cfg['aria-valuemin'] = this.aria_valuemin;
10999 if(this.aria_valuemax){
11000 cfg['aria-valuemax'] = this.aria_valuemax;
11003 if(this.label && !this.sr_only){
11004 cfg.html = this.label;
11008 cfg.cls += ' progress-bar-' + this.panel;
11014 update : function(aria_valuenow)
11016 this.aria_valuenow = aria_valuenow;
11018 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
11033 * @class Roo.bootstrap.TabPanel
11034 * @extends Roo.bootstrap.Component
11035 * Bootstrap TabPanel class
11036 * @cfg {Boolean} active panel active
11037 * @cfg {String} html panel content
11038 * @cfg {String} tabId tab relate id
11039 * @cfg {String} navId The navbar which triggers show hide
11043 * Create a new TabPanel
11044 * @param {Object} config The config object
11047 Roo.bootstrap.TabPanel = function(config){
11048 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
11051 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
11058 getAutoCreate : function(){
11062 html: this.html || ''
11066 cfg.cls += ' active';
11070 cfg.tabId = this.tabId;
11075 onRender : function(ct, position)
11077 // Roo.log("Call onRender: " + this.xtype);
11079 Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
11081 if (this.navId && this.tabId) {
11082 var item = Roo.bootstrap.NavGroup.get(this.navId).getNavItem(this.tabId);
11084 Roo.log("could not find navID:" + this.navId + ", tabId: " + this.tabId);
11086 item.on('changed', function(item, state) {
11087 this.setActive(state);
11093 setActive: function(state)
11095 this.active = state;
11097 this.el.removeClass('active');
11099 } else if (!this.el.hasClass('active')) {
11100 this.el.addClass('active');
11102 this.fireEvent('changed', this, state);
11119 * @class Roo.bootstrap.DateField
11120 * @extends Roo.bootstrap.Input
11121 * Bootstrap DateField class
11122 * @cfg {Number} weekStart default 0
11123 * @cfg {Number} weekStart default 0
11124 * @cfg {Number} viewMode default empty, (months|years)
11125 * @cfg {Number} minViewMode default empty, (months|years)
11126 * @cfg {Number} startDate default -Infinity
11127 * @cfg {Number} endDate default Infinity
11128 * @cfg {Boolean} todayHighlight default false
11129 * @cfg {Boolean} todayBtn default false
11130 * @cfg {Boolean} calendarWeeks default false
11131 * @cfg {Object} daysOfWeekDisabled default empty
11133 * @cfg {Boolean} keyboardNavigation default true
11134 * @cfg {String} language default en
11137 * Create a new DateField
11138 * @param {Object} config The config object
11141 Roo.bootstrap.DateField = function(config){
11142 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
11146 * Fires when this field show.
11147 * @param {Roo.bootstrap.DateField} this
11148 * @param {Mixed} date The date value
11153 * Fires when this field hide.
11154 * @param {Roo.bootstrap.DateField} this
11155 * @param {Mixed} date The date value
11160 * Fires when select a date.
11161 * @param {Roo.bootstrap.DateField} this
11162 * @param {Mixed} date The date value
11168 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
11171 * @cfg {String} format
11172 * The default date format string which can be overriden for localization support. The format must be
11173 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11177 * @cfg {String} altFormats
11178 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11179 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11181 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11189 todayHighlight : false,
11195 keyboardNavigation: true,
11197 calendarWeeks: false,
11199 startDate: -Infinity,
11203 daysOfWeekDisabled: [],
11207 UTCDate: function()
11209 return new Date(Date.UTC.apply(Date, arguments));
11212 UTCToday: function()
11214 var today = new Date();
11215 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11218 getDate: function() {
11219 var d = this.getUTCDate();
11220 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11223 getUTCDate: function() {
11227 setDate: function(d) {
11228 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11231 setUTCDate: function(d) {
11233 this.setValue(this.formatDate(this.date));
11236 onRender: function(ct, position)
11239 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11241 this.language = this.language || 'en';
11242 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11243 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11245 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11246 this.format = this.format || 'm/d/y';
11247 this.isInline = false;
11248 this.isInput = true;
11249 this.component = this.el.select('.add-on', true).first() || false;
11250 this.component = (this.component && this.component.length === 0) ? false : this.component;
11251 this.hasInput = this.component && this.inputEL().length;
11253 if (typeof(this.minViewMode === 'string')) {
11254 switch (this.minViewMode) {
11256 this.minViewMode = 1;
11259 this.minViewMode = 2;
11262 this.minViewMode = 0;
11267 if (typeof(this.viewMode === 'string')) {
11268 switch (this.viewMode) {
11281 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11283 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11285 this.picker().on('mousedown', this.onMousedown, this);
11286 this.picker().on('click', this.onClick, this);
11288 this.picker().addClass('datepicker-dropdown');
11290 this.startViewMode = this.viewMode;
11293 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11294 if(!this.calendarWeeks){
11299 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11300 v.attr('colspan', function(i, val){
11301 return parseInt(val) + 1;
11306 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11308 this.setStartDate(this.startDate);
11309 this.setEndDate(this.endDate);
11311 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11318 if(this.isInline) {
11323 picker : function()
11325 return this.el.select('.datepicker', true).first();
11328 fillDow: function()
11330 var dowCnt = this.weekStart;
11339 if(this.calendarWeeks){
11347 while (dowCnt < this.weekStart + 7) {
11351 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11355 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11358 fillMonths: function()
11361 var months = this.picker().select('>.datepicker-months td', true).first();
11363 months.dom.innerHTML = '';
11369 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11372 months.createChild(month);
11377 update: function(){
11379 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11381 if (this.date < this.startDate) {
11382 this.viewDate = new Date(this.startDate);
11383 } else if (this.date > this.endDate) {
11384 this.viewDate = new Date(this.endDate);
11386 this.viewDate = new Date(this.date);
11393 var d = new Date(this.viewDate),
11394 year = d.getUTCFullYear(),
11395 month = d.getUTCMonth(),
11396 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11397 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11398 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11399 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11400 currentDate = this.date && this.date.valueOf(),
11401 today = this.UTCToday();
11403 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11405 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11407 // this.picker.select('>tfoot th.today').
11408 // .text(dates[this.language].today)
11409 // .toggle(this.todayBtn !== false);
11411 this.updateNavArrows();
11414 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11416 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11418 prevMonth.setUTCDate(day);
11420 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11422 var nextMonth = new Date(prevMonth);
11424 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11426 nextMonth = nextMonth.valueOf();
11428 var fillMonths = false;
11430 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11432 while(prevMonth.valueOf() < nextMonth) {
11435 if (prevMonth.getUTCDay() === this.weekStart) {
11437 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11445 if(this.calendarWeeks){
11446 // ISO 8601: First week contains first thursday.
11447 // ISO also states week starts on Monday, but we can be more abstract here.
11449 // Start of current week: based on weekstart/current date
11450 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11451 // Thursday of this week
11452 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11453 // First Thursday of year, year from thursday
11454 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11455 // Calendar week: ms between thursdays, div ms per day, div 7 days
11456 calWeek = (th - yth) / 864e5 / 7 + 1;
11458 fillMonths.cn.push({
11466 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11468 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11471 if (this.todayHighlight &&
11472 prevMonth.getUTCFullYear() == today.getFullYear() &&
11473 prevMonth.getUTCMonth() == today.getMonth() &&
11474 prevMonth.getUTCDate() == today.getDate()) {
11475 clsName += ' today';
11478 if (currentDate && prevMonth.valueOf() === currentDate) {
11479 clsName += ' active';
11482 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11483 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11484 clsName += ' disabled';
11487 fillMonths.cn.push({
11489 cls: 'day ' + clsName,
11490 html: prevMonth.getDate()
11493 prevMonth.setDate(prevMonth.getDate()+1);
11496 var currentYear = this.date && this.date.getUTCFullYear();
11497 var currentMonth = this.date && this.date.getUTCMonth();
11499 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11501 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11502 v.removeClass('active');
11504 if(currentYear === year && k === currentMonth){
11505 v.addClass('active');
11508 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11509 v.addClass('disabled');
11515 year = parseInt(year/10, 10) * 10;
11517 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11519 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11522 for (var i = -1; i < 11; i++) {
11523 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11525 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11533 showMode: function(dir) {
11535 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11537 Roo.each(this.picker().select('>div',true).elements, function(v){
11538 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11541 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11546 if(this.isInline) return;
11548 this.picker().removeClass(['bottom', 'top']);
11550 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11552 * place to the top of element!
11556 this.picker().addClass('top');
11557 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11562 this.picker().addClass('bottom');
11564 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11567 parseDate : function(value){
11568 if(!value || value instanceof Date){
11571 var v = Date.parseDate(value, this.format);
11572 if (!v && this.useIso) {
11573 v = Date.parseDate(value, 'Y-m-d');
11575 if(!v && this.altFormats){
11576 if(!this.altFormatsArray){
11577 this.altFormatsArray = this.altFormats.split("|");
11579 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11580 v = Date.parseDate(value, this.altFormatsArray[i]);
11586 formatDate : function(date, fmt){
11587 return (!date || !(date instanceof Date)) ?
11588 date : date.dateFormat(fmt || this.format);
11591 onFocus : function()
11593 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11597 onBlur : function()
11599 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11605 this.picker().show();
11609 this.fireEvent('show', this, this.date);
11614 if(this.isInline) return;
11615 this.picker().hide();
11616 this.viewMode = this.startViewMode;
11619 this.fireEvent('hide', this, this.date);
11623 onMousedown: function(e){
11624 e.stopPropagation();
11625 e.preventDefault();
11628 keyup: function(e){
11629 Roo.bootstrap.DateField.superclass.keyup.call(this);
11634 setValue: function(v){
11635 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11637 this.fireEvent('select', this, this.date);
11641 fireKey: function(e){
11642 if (!this.picker().isVisible()){
11643 if (e.keyCode == 27) // allow escape to hide and re-show picker
11647 var dateChanged = false,
11649 newDate, newViewDate;
11653 e.preventDefault();
11657 if (!this.keyboardNavigation) break;
11658 dir = e.keyCode == 37 ? -1 : 1;
11661 newDate = this.moveYear(this.date, dir);
11662 newViewDate = this.moveYear(this.viewDate, dir);
11663 } else if (e.shiftKey){
11664 newDate = this.moveMonth(this.date, dir);
11665 newViewDate = this.moveMonth(this.viewDate, dir);
11667 newDate = new Date(this.date);
11668 newDate.setUTCDate(this.date.getUTCDate() + dir);
11669 newViewDate = new Date(this.viewDate);
11670 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11672 if (this.dateWithinRange(newDate)){
11673 this.date = newDate;
11674 this.viewDate = newViewDate;
11675 this.setValue(this.formatDate(this.date));
11677 e.preventDefault();
11678 dateChanged = true;
11683 if (!this.keyboardNavigation) break;
11684 dir = e.keyCode == 38 ? -1 : 1;
11686 newDate = this.moveYear(this.date, dir);
11687 newViewDate = this.moveYear(this.viewDate, dir);
11688 } else if (e.shiftKey){
11689 newDate = this.moveMonth(this.date, dir);
11690 newViewDate = this.moveMonth(this.viewDate, dir);
11692 newDate = new Date(this.date);
11693 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11694 newViewDate = new Date(this.viewDate);
11695 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11697 if (this.dateWithinRange(newDate)){
11698 this.date = newDate;
11699 this.viewDate = newViewDate;
11700 this.setValue(this.formatDate(this.date));
11702 e.preventDefault();
11703 dateChanged = true;
11707 this.setValue(this.formatDate(this.date));
11709 e.preventDefault();
11712 this.setValue(this.formatDate(this.date));
11719 onClick: function(e) {
11720 e.stopPropagation();
11721 e.preventDefault();
11723 var target = e.getTarget();
11725 if(target.nodeName.toLowerCase() === 'i'){
11726 target = Roo.get(target).dom.parentNode;
11729 var nodeName = target.nodeName;
11730 var className = target.className;
11731 var html = target.innerHTML;
11733 switch(nodeName.toLowerCase()) {
11735 switch(className) {
11741 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11742 switch(this.viewMode){
11744 this.viewDate = this.moveMonth(this.viewDate, dir);
11748 this.viewDate = this.moveYear(this.viewDate, dir);
11754 var date = new Date();
11755 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11757 this.setValue(this.formatDate(this.date));
11763 if (className.indexOf('disabled') === -1) {
11764 this.viewDate.setUTCDate(1);
11765 if (className.indexOf('month') !== -1) {
11766 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11768 var year = parseInt(html, 10) || 0;
11769 this.viewDate.setUTCFullYear(year);
11778 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11779 var day = parseInt(html, 10) || 1;
11780 var year = this.viewDate.getUTCFullYear(),
11781 month = this.viewDate.getUTCMonth();
11783 if (className.indexOf('old') !== -1) {
11790 } else if (className.indexOf('new') !== -1) {
11798 this.date = this.UTCDate(year, month, day,0,0,0,0);
11799 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11801 this.setValue(this.formatDate(this.date));
11808 setStartDate: function(startDate){
11809 this.startDate = startDate || -Infinity;
11810 if (this.startDate !== -Infinity) {
11811 this.startDate = this.parseDate(this.startDate);
11814 this.updateNavArrows();
11817 setEndDate: function(endDate){
11818 this.endDate = endDate || Infinity;
11819 if (this.endDate !== Infinity) {
11820 this.endDate = this.parseDate(this.endDate);
11823 this.updateNavArrows();
11826 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11827 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11828 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11829 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11831 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11832 return parseInt(d, 10);
11835 this.updateNavArrows();
11838 updateNavArrows: function() {
11839 var d = new Date(this.viewDate),
11840 year = d.getUTCFullYear(),
11841 month = d.getUTCMonth();
11843 Roo.each(this.picker().select('.prev', true).elements, function(v){
11845 switch (this.viewMode) {
11848 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11854 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11861 Roo.each(this.picker().select('.next', true).elements, function(v){
11863 switch (this.viewMode) {
11866 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11872 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11880 moveMonth: function(date, dir){
11881 if (!dir) return date;
11882 var new_date = new Date(date.valueOf()),
11883 day = new_date.getUTCDate(),
11884 month = new_date.getUTCMonth(),
11885 mag = Math.abs(dir),
11887 dir = dir > 0 ? 1 : -1;
11890 // If going back one month, make sure month is not current month
11891 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11893 return new_date.getUTCMonth() == month;
11895 // If going forward one month, make sure month is as expected
11896 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11898 return new_date.getUTCMonth() != new_month;
11900 new_month = month + dir;
11901 new_date.setUTCMonth(new_month);
11902 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11903 if (new_month < 0 || new_month > 11)
11904 new_month = (new_month + 12) % 12;
11906 // For magnitudes >1, move one month at a time...
11907 for (var i=0; i<mag; i++)
11908 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11909 new_date = this.moveMonth(new_date, dir);
11910 // ...then reset the day, keeping it in the new month
11911 new_month = new_date.getUTCMonth();
11912 new_date.setUTCDate(day);
11914 return new_month != new_date.getUTCMonth();
11917 // Common date-resetting loop -- if date is beyond end of month, make it
11920 new_date.setUTCDate(--day);
11921 new_date.setUTCMonth(new_month);
11926 moveYear: function(date, dir){
11927 return this.moveMonth(date, dir*12);
11930 dateWithinRange: function(date){
11931 return date >= this.startDate && date <= this.endDate;
11935 remove: function() {
11936 this.picker().remove();
11941 Roo.apply(Roo.bootstrap.DateField, {
11952 html: '<i class="icon-arrow-left"/>'
11962 html: '<i class="icon-arrow-right"/>'
12004 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
12005 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
12006 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
12007 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
12008 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
12021 navFnc: 'FullYear',
12026 navFnc: 'FullYear',
12031 Roo.apply(Roo.bootstrap.DateField, {
12035 cls: 'datepicker dropdown-menu',
12039 cls: 'datepicker-days',
12043 cls: 'table-condensed',
12045 Roo.bootstrap.DateField.head,
12049 Roo.bootstrap.DateField.footer
12056 cls: 'datepicker-months',
12060 cls: 'table-condensed',
12062 Roo.bootstrap.DateField.head,
12063 Roo.bootstrap.DateField.content,
12064 Roo.bootstrap.DateField.footer
12071 cls: 'datepicker-years',
12075 cls: 'table-condensed',
12077 Roo.bootstrap.DateField.head,
12078 Roo.bootstrap.DateField.content,
12079 Roo.bootstrap.DateField.footer
12098 * @class Roo.bootstrap.TimeField
12099 * @extends Roo.bootstrap.Input
12100 * Bootstrap DateField class
12104 * Create a new TimeField
12105 * @param {Object} config The config object
12108 Roo.bootstrap.TimeField = function(config){
12109 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
12113 * Fires when this field show.
12114 * @param {Roo.bootstrap.DateField} this
12115 * @param {Mixed} date The date value
12120 * Fires when this field hide.
12121 * @param {Roo.bootstrap.DateField} this
12122 * @param {Mixed} date The date value
12127 * Fires when select a date.
12128 * @param {Roo.bootstrap.DateField} this
12129 * @param {Mixed} date The date value
12135 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
12138 * @cfg {String} format
12139 * The default time format string which can be overriden for localization support. The format must be
12140 * valid according to {@link Date#parseDate} (defaults to 'H:i').
12144 onRender: function(ct, position)
12147 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
12149 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
12151 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
12153 this.pop = this.picker().select('>.datepicker-time',true).first();
12154 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
12156 this.picker().on('mousedown', this.onMousedown, this);
12157 this.picker().on('click', this.onClick, this);
12159 this.picker().addClass('datepicker-dropdown');
12164 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12165 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12166 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12167 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12168 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12169 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12173 fireKey: function(e){
12174 if (!this.picker().isVisible()){
12175 if (e.keyCode == 27) // allow escape to hide and re-show picker
12180 e.preventDefault();
12188 this.onTogglePeriod();
12191 this.onIncrementMinutes();
12194 this.onDecrementMinutes();
12203 onClick: function(e) {
12204 e.stopPropagation();
12205 e.preventDefault();
12208 picker : function()
12210 return this.el.select('.datepicker', true).first();
12213 fillTime: function()
12215 var time = this.pop.select('tbody', true).first();
12217 time.dom.innerHTML = '';
12232 cls: 'hours-up glyphicon glyphicon-chevron-up'
12252 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12273 cls: 'timepicker-hour',
12288 cls: 'timepicker-minute',
12303 cls: 'btn btn-primary period',
12325 cls: 'hours-down glyphicon glyphicon-chevron-down'
12345 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12363 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12370 var hours = this.time.getHours();
12371 var minutes = this.time.getMinutes();
12384 hours = hours - 12;
12388 hours = '0' + hours;
12392 minutes = '0' + minutes;
12395 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12396 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12397 this.pop.select('button', true).first().dom.innerHTML = period;
12403 this.picker().removeClass(['bottom', 'top']);
12405 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12407 * place to the top of element!
12411 this.picker().addClass('top');
12412 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12417 this.picker().addClass('bottom');
12419 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12422 onFocus : function()
12424 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12428 onBlur : function()
12430 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12436 this.picker().show();
12441 this.fireEvent('show', this, this.date);
12446 this.picker().hide();
12449 this.fireEvent('hide', this, this.date);
12452 setTime : function()
12455 this.setValue(this.time.format(this.format));
12457 this.fireEvent('select', this, this.date);
12462 onMousedown: function(e){
12463 e.stopPropagation();
12464 e.preventDefault();
12467 onIncrementHours: function()
12469 Roo.log('onIncrementHours');
12470 this.time = this.time.add(Date.HOUR, 1);
12475 onDecrementHours: function()
12477 Roo.log('onDecrementHours');
12478 this.time = this.time.add(Date.HOUR, -1);
12482 onIncrementMinutes: function()
12484 Roo.log('onIncrementMinutes');
12485 this.time = this.time.add(Date.MINUTE, 1);
12489 onDecrementMinutes: function()
12491 Roo.log('onDecrementMinutes');
12492 this.time = this.time.add(Date.MINUTE, -1);
12496 onTogglePeriod: function()
12498 Roo.log('onTogglePeriod');
12499 this.time = this.time.add(Date.HOUR, 12);
12506 Roo.apply(Roo.bootstrap.TimeField, {
12536 cls: 'btn btn-info ok',
12548 Roo.apply(Roo.bootstrap.TimeField, {
12552 cls: 'datepicker dropdown-menu',
12556 cls: 'datepicker-time',
12560 cls: 'table-condensed',
12562 Roo.bootstrap.TimeField.content,
12563 Roo.bootstrap.TimeField.footer
12582 * @class Roo.bootstrap.CheckBox
12583 * @extends Roo.bootstrap.Input
12584 * Bootstrap CheckBox class
12586 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12587 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12588 * @cfg {String} boxLabel The text that appears beside the checkbox
12589 * @cfg {Boolean} checked initnal the element
12592 * Create a new CheckBox
12593 * @param {Object} config The config object
12596 Roo.bootstrap.CheckBox = function(config){
12597 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12602 * Fires when the element is checked or unchecked.
12603 * @param {Roo.bootstrap.CheckBox} this This input
12604 * @param {Boolean} checked The new checked value
12610 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12612 inputType: 'checkbox',
12618 getAutoCreate : function()
12620 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12626 cfg.cls = 'form-group' //input-group
12631 type : this.inputType,
12632 value : (!this.checked) ? this.valueOff : this.inputValue,
12634 placeholder : this.placeholder || ''
12638 if (this.disabled) {
12639 input.disabled=true;
12643 input.checked = this.checked;
12647 input.name = this.name;
12651 input.cls += ' input-' + this.size;
12655 ['xs','sm','md','lg'].map(function(size){
12656 if (settings[size]) {
12657 cfg.cls += ' col-' + size + '-' + settings[size];
12661 var inputblock = input;
12663 if (this.before || this.after) {
12666 cls : 'input-group',
12670 inputblock.cn.push({
12672 cls : 'input-group-addon',
12676 inputblock.cn.push(input);
12678 inputblock.cn.push({
12680 cls : 'input-group-addon',
12687 if (align ==='left' && this.fieldLabel.length) {
12688 Roo.log("left and has label");
12694 cls : 'control-label col-md-' + this.labelWidth,
12695 html : this.fieldLabel
12699 cls : "col-md-" + (12 - this.labelWidth),
12706 } else if ( this.fieldLabel.length) {
12711 tag: this.boxLabel ? 'span' : 'label',
12713 cls: 'control-label box-input-label',
12714 //cls : 'input-group-addon',
12715 html : this.fieldLabel
12725 Roo.log(" no label && no align");
12740 html: this.boxLabel
12749 * return the real input element.
12751 inputEl: function ()
12753 return this.el.select('input.form-box',true).first();
12756 initEvents : function()
12758 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12760 this.inputEl().on('click', this.onClick, this);
12764 onClick : function()
12766 this.setChecked(!this.checked);
12769 setChecked : function(state,suppressEvent)
12771 this.checked = state;
12773 this.inputEl().dom.checked = state;
12775 if(suppressEvent !== true){
12776 this.fireEvent('check', this, state);
12779 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12783 setValue : function(v,suppressEvent)
12785 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12799 * @class Roo.bootstrap.Radio
12800 * @extends Roo.bootstrap.CheckBox
12801 * Bootstrap Radio class
12804 * Create a new Radio
12805 * @param {Object} config The config object
12808 Roo.bootstrap.Radio = function(config){
12809 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12813 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12815 inputType: 'radio',
12819 getAutoCreate : function()
12821 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12827 cfg.cls = 'form-group' //input-group
12832 type : this.inputType,
12833 value : (!this.checked) ? this.valueOff : this.inputValue,
12835 placeholder : this.placeholder || ''
12839 if (this.disabled) {
12840 input.disabled=true;
12844 input.checked = this.checked;
12848 input.name = this.name;
12852 input.cls += ' input-' + this.size;
12856 ['xs','sm','md','lg'].map(function(size){
12857 if (settings[size]) {
12858 cfg.cls += ' col-' + size + '-' + settings[size];
12862 var inputblock = input;
12864 if (this.before || this.after) {
12867 cls : 'input-group',
12871 inputblock.cn.push({
12873 cls : 'input-group-addon',
12877 inputblock.cn.push(input);
12879 inputblock.cn.push({
12881 cls : 'input-group-addon',
12888 if (align ==='left' && this.fieldLabel.length) {
12889 Roo.log("left and has label");
12895 cls : 'control-label col-md-' + this.labelWidth,
12896 html : this.fieldLabel
12900 cls : "col-md-" + (12 - this.labelWidth),
12907 } else if ( this.fieldLabel.length) {
12914 cls: 'control-label box-input-label',
12915 //cls : 'input-group-addon',
12916 html : this.fieldLabel
12926 Roo.log(" no label && no align");
12941 html: this.boxLabel
12949 onClick : function()
12951 this.setChecked(true);
12954 setChecked : function(state,suppressEvent)
12957 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12958 v.dom.checked = false;
12962 this.checked = state;
12963 this.inputEl().dom.checked = state;
12965 if(suppressEvent !== true){
12966 this.fireEvent('check', this, state);
12969 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12973 getGroupValue : function()
12976 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12977 if(v.dom.checked == true){
12978 value = v.dom.value;
12986 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12987 * @return {Mixed} value The field value
12989 getValue : function(){
12990 return this.getGroupValue();
12996 //<script type="text/javascript">
12999 * Based Ext JS Library 1.1.1
13000 * Copyright(c) 2006-2007, Ext JS, LLC.
13006 * @class Roo.HtmlEditorCore
13007 * @extends Roo.Component
13008 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13010 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13013 Roo.HtmlEditorCore = function(config){
13016 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13019 * @event initialize
13020 * Fires when the editor is fully initialized (including the iframe)
13021 * @param {Roo.HtmlEditorCore} this
13026 * Fires when the editor is first receives the focus. Any insertion must wait
13027 * until after this event.
13028 * @param {Roo.HtmlEditorCore} this
13032 * @event beforesync
13033 * Fires before the textarea is updated with content from the editor iframe. Return false
13034 * to cancel the sync.
13035 * @param {Roo.HtmlEditorCore} this
13036 * @param {String} html
13040 * @event beforepush
13041 * Fires before the iframe editor is updated with content from the textarea. Return false
13042 * to cancel the push.
13043 * @param {Roo.HtmlEditorCore} this
13044 * @param {String} html
13049 * Fires when the textarea is updated with content from the editor iframe.
13050 * @param {Roo.HtmlEditorCore} this
13051 * @param {String} html
13056 * Fires when the iframe editor is updated with content from the textarea.
13057 * @param {Roo.HtmlEditorCore} this
13058 * @param {String} html
13063 * @event editorevent
13064 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13065 * @param {Roo.HtmlEditorCore} this
13073 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
13077 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
13083 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
13088 * @cfg {Number} height (in pixels)
13092 * @cfg {Number} width (in pixels)
13097 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13100 stylesheets: false,
13105 // private properties
13106 validationEvent : false,
13108 initialized : false,
13110 sourceEditMode : false,
13111 onFocus : Roo.emptyFn,
13113 hideMode:'offsets',
13121 * Protected method that will not generally be called directly. It
13122 * is called when the editor initializes the iframe with HTML contents. Override this method if you
13123 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13125 getDocMarkup : function(){
13128 Roo.log(this.stylesheets);
13130 // inherit styels from page...??
13131 if (this.stylesheets === false) {
13133 Roo.get(document.head).select('style').each(function(node) {
13134 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13137 Roo.get(document.head).select('link').each(function(node) {
13138 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13141 } else if (!this.stylesheets.length) {
13143 st = '<style type="text/css">' +
13144 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13147 Roo.each(this.stylesheets, function(s) {
13148 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13153 st += '<style type="text/css">' +
13154 'IMG { cursor: pointer } ' +
13158 return '<html><head>' + st +
13159 //<style type="text/css">' +
13160 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13162 ' </head><body class="roo-htmleditor-body"></body></html>';
13166 onRender : function(ct, position)
13169 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13170 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13173 this.el.dom.style.border = '0 none';
13174 this.el.dom.setAttribute('tabIndex', -1);
13175 this.el.addClass('x-hidden hide');
13179 if(Roo.isIE){ // fix IE 1px bogus margin
13180 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13184 this.frameId = Roo.id();
13188 var iframe = this.owner.wrap.createChild({
13190 cls: 'form-control', // bootstrap..
13192 name: this.frameId,
13193 frameBorder : 'no',
13194 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13199 this.iframe = iframe.dom;
13201 this.assignDocWin();
13203 this.doc.designMode = 'on';
13206 this.doc.write(this.getDocMarkup());
13210 var task = { // must defer to wait for browser to be ready
13212 //console.log("run task?" + this.doc.readyState);
13213 this.assignDocWin();
13214 if(this.doc.body || this.doc.readyState == 'complete'){
13216 this.doc.designMode="on";
13220 Roo.TaskMgr.stop(task);
13221 this.initEditor.defer(10, this);
13228 Roo.TaskMgr.start(task);
13235 onResize : function(w, h)
13237 Roo.log('resize: ' +w + ',' + h );
13238 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13242 if(typeof w == 'number'){
13244 this.iframe.style.width = w + 'px';
13246 if(typeof h == 'number'){
13248 this.iframe.style.height = h + 'px';
13250 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13257 * Toggles the editor between standard and source edit mode.
13258 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13260 toggleSourceEdit : function(sourceEditMode){
13262 this.sourceEditMode = sourceEditMode === true;
13264 if(this.sourceEditMode){
13266 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13269 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13270 //this.iframe.className = '';
13273 //this.setSize(this.owner.wrap.getSize());
13274 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13281 * Protected method that will not generally be called directly. If you need/want
13282 * custom HTML cleanup, this is the method you should override.
13283 * @param {String} html The HTML to be cleaned
13284 * return {String} The cleaned HTML
13286 cleanHtml : function(html){
13287 html = String(html);
13288 if(html.length > 5){
13289 if(Roo.isSafari){ // strip safari nonsense
13290 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13293 if(html == ' '){
13300 * HTML Editor -> Textarea
13301 * Protected method that will not generally be called directly. Syncs the contents
13302 * of the editor iframe with the textarea.
13304 syncValue : function(){
13305 if(this.initialized){
13306 var bd = (this.doc.body || this.doc.documentElement);
13307 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13308 var html = bd.innerHTML;
13310 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13311 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13313 html = '<div style="'+m[0]+'">' + html + '</div>';
13316 html = this.cleanHtml(html);
13317 // fix up the special chars.. normaly like back quotes in word...
13318 // however we do not want to do this with chinese..
13319 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13320 var cc = b.charCodeAt();
13322 (cc >= 0x4E00 && cc < 0xA000 ) ||
13323 (cc >= 0x3400 && cc < 0x4E00 ) ||
13324 (cc >= 0xf900 && cc < 0xfb00 )
13330 if(this.owner.fireEvent('beforesync', this, html) !== false){
13331 this.el.dom.value = html;
13332 this.owner.fireEvent('sync', this, html);
13338 * Protected method that will not generally be called directly. Pushes the value of the textarea
13339 * into the iframe editor.
13341 pushValue : function(){
13342 if(this.initialized){
13343 var v = this.el.dom.value.trim();
13345 // if(v.length < 1){
13349 if(this.owner.fireEvent('beforepush', this, v) !== false){
13350 var d = (this.doc.body || this.doc.documentElement);
13352 this.cleanUpPaste();
13353 this.el.dom.value = d.innerHTML;
13354 this.owner.fireEvent('push', this, v);
13360 deferFocus : function(){
13361 this.focus.defer(10, this);
13365 focus : function(){
13366 if(this.win && !this.sourceEditMode){
13373 assignDocWin: function()
13375 var iframe = this.iframe;
13378 this.doc = iframe.contentWindow.document;
13379 this.win = iframe.contentWindow;
13381 if (!Roo.get(this.frameId)) {
13384 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13385 this.win = Roo.get(this.frameId).dom.contentWindow;
13390 initEditor : function(){
13391 //console.log("INIT EDITOR");
13392 this.assignDocWin();
13396 this.doc.designMode="on";
13398 this.doc.write(this.getDocMarkup());
13401 var dbody = (this.doc.body || this.doc.documentElement);
13402 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13403 // this copies styles from the containing element into thsi one..
13404 // not sure why we need all of this..
13405 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13406 ss['background-attachment'] = 'fixed'; // w3c
13407 dbody.bgProperties = 'fixed'; // ie
13408 Roo.DomHelper.applyStyles(dbody, ss);
13409 Roo.EventManager.on(this.doc, {
13410 //'mousedown': this.onEditorEvent,
13411 'mouseup': this.onEditorEvent,
13412 'dblclick': this.onEditorEvent,
13413 'click': this.onEditorEvent,
13414 'keyup': this.onEditorEvent,
13419 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13421 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13422 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13424 this.initialized = true;
13426 this.owner.fireEvent('initialize', this);
13431 onDestroy : function(){
13437 //for (var i =0; i < this.toolbars.length;i++) {
13438 // // fixme - ask toolbars for heights?
13439 // this.toolbars[i].onDestroy();
13442 //this.wrap.dom.innerHTML = '';
13443 //this.wrap.remove();
13448 onFirstFocus : function(){
13450 this.assignDocWin();
13453 this.activated = true;
13456 if(Roo.isGecko){ // prevent silly gecko errors
13458 var s = this.win.getSelection();
13459 if(!s.focusNode || s.focusNode.nodeType != 3){
13460 var r = s.getRangeAt(0);
13461 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13466 this.execCmd('useCSS', true);
13467 this.execCmd('styleWithCSS', false);
13470 this.owner.fireEvent('activate', this);
13474 adjustFont: function(btn){
13475 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13476 //if(Roo.isSafari){ // safari
13479 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13480 if(Roo.isSafari){ // safari
13481 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13482 v = (v < 10) ? 10 : v;
13483 v = (v > 48) ? 48 : v;
13484 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13489 v = Math.max(1, v+adjust);
13491 this.execCmd('FontSize', v );
13494 onEditorEvent : function(e){
13495 this.owner.fireEvent('editorevent', this, e);
13496 // this.updateToolbar();
13497 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13500 insertTag : function(tg)
13502 // could be a bit smarter... -> wrap the current selected tRoo..
13503 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13505 range = this.createRange(this.getSelection());
13506 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13507 wrappingNode.appendChild(range.extractContents());
13508 range.insertNode(wrappingNode);
13515 this.execCmd("formatblock", tg);
13519 insertText : function(txt)
13523 var range = this.createRange();
13524 range.deleteContents();
13525 //alert(Sender.getAttribute('label'));
13527 range.insertNode(this.doc.createTextNode(txt));
13533 * Executes a Midas editor command on the editor document and performs necessary focus and
13534 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13535 * @param {String} cmd The Midas command
13536 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13538 relayCmd : function(cmd, value){
13540 this.execCmd(cmd, value);
13541 this.owner.fireEvent('editorevent', this);
13542 //this.updateToolbar();
13543 this.owner.deferFocus();
13547 * Executes a Midas editor command directly on the editor document.
13548 * For visual commands, you should use {@link #relayCmd} instead.
13549 * <b>This should only be called after the editor is initialized.</b>
13550 * @param {String} cmd The Midas command
13551 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13553 execCmd : function(cmd, value){
13554 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13561 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13563 * @param {String} text | dom node..
13565 insertAtCursor : function(text)
13570 if(!this.activated){
13576 var r = this.doc.selection.createRange();
13587 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13591 // from jquery ui (MIT licenced)
13593 var win = this.win;
13595 if (win.getSelection && win.getSelection().getRangeAt) {
13596 range = win.getSelection().getRangeAt(0);
13597 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13598 range.insertNode(node);
13599 } else if (win.document.selection && win.document.selection.createRange) {
13600 // no firefox support
13601 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13602 win.document.selection.createRange().pasteHTML(txt);
13604 // no firefox support
13605 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13606 this.execCmd('InsertHTML', txt);
13615 mozKeyPress : function(e){
13617 var c = e.getCharCode(), cmd;
13620 c = String.fromCharCode(c).toLowerCase();
13634 this.cleanUpPaste.defer(100, this);
13642 e.preventDefault();
13650 fixKeys : function(){ // load time branching for fastest keydown performance
13652 return function(e){
13653 var k = e.getKey(), r;
13656 r = this.doc.selection.createRange();
13659 r.pasteHTML('    ');
13666 r = this.doc.selection.createRange();
13668 var target = r.parentElement();
13669 if(!target || target.tagName.toLowerCase() != 'li'){
13671 r.pasteHTML('<br />');
13677 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13678 this.cleanUpPaste.defer(100, this);
13684 }else if(Roo.isOpera){
13685 return function(e){
13686 var k = e.getKey();
13690 this.execCmd('InsertHTML','    ');
13693 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13694 this.cleanUpPaste.defer(100, this);
13699 }else if(Roo.isSafari){
13700 return function(e){
13701 var k = e.getKey();
13705 this.execCmd('InsertText','\t');
13709 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13710 this.cleanUpPaste.defer(100, this);
13718 getAllAncestors: function()
13720 var p = this.getSelectedNode();
13723 a.push(p); // push blank onto stack..
13724 p = this.getParentElement();
13728 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13732 a.push(this.doc.body);
13736 lastSelNode : false,
13739 getSelection : function()
13741 this.assignDocWin();
13742 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13745 getSelectedNode: function()
13747 // this may only work on Gecko!!!
13749 // should we cache this!!!!
13754 var range = this.createRange(this.getSelection()).cloneRange();
13757 var parent = range.parentElement();
13759 var testRange = range.duplicate();
13760 testRange.moveToElementText(parent);
13761 if (testRange.inRange(range)) {
13764 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13767 parent = parent.parentElement;
13772 // is ancestor a text element.
13773 var ac = range.commonAncestorContainer;
13774 if (ac.nodeType == 3) {
13775 ac = ac.parentNode;
13778 var ar = ac.childNodes;
13781 var other_nodes = [];
13782 var has_other_nodes = false;
13783 for (var i=0;i<ar.length;i++) {
13784 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13787 // fullly contained node.
13789 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13794 // probably selected..
13795 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13796 other_nodes.push(ar[i]);
13800 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13805 has_other_nodes = true;
13807 if (!nodes.length && other_nodes.length) {
13808 nodes= other_nodes;
13810 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13816 createRange: function(sel)
13818 // this has strange effects when using with
13819 // top toolbar - not sure if it's a great idea.
13820 //this.editor.contentWindow.focus();
13821 if (typeof sel != "undefined") {
13823 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13825 return this.doc.createRange();
13828 return this.doc.createRange();
13831 getParentElement: function()
13834 this.assignDocWin();
13835 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13837 var range = this.createRange(sel);
13840 var p = range.commonAncestorContainer;
13841 while (p.nodeType == 3) { // text node
13852 * Range intersection.. the hard stuff...
13856 * [ -- selected range --- ]
13860 * if end is before start or hits it. fail.
13861 * if start is after end or hits it fail.
13863 * if either hits (but other is outside. - then it's not
13869 // @see http://www.thismuchiknow.co.uk/?p=64.
13870 rangeIntersectsNode : function(range, node)
13872 var nodeRange = node.ownerDocument.createRange();
13874 nodeRange.selectNode(node);
13876 nodeRange.selectNodeContents(node);
13879 var rangeStartRange = range.cloneRange();
13880 rangeStartRange.collapse(true);
13882 var rangeEndRange = range.cloneRange();
13883 rangeEndRange.collapse(false);
13885 var nodeStartRange = nodeRange.cloneRange();
13886 nodeStartRange.collapse(true);
13888 var nodeEndRange = nodeRange.cloneRange();
13889 nodeEndRange.collapse(false);
13891 return rangeStartRange.compareBoundaryPoints(
13892 Range.START_TO_START, nodeEndRange) == -1 &&
13893 rangeEndRange.compareBoundaryPoints(
13894 Range.START_TO_START, nodeStartRange) == 1;
13898 rangeCompareNode : function(range, node)
13900 var nodeRange = node.ownerDocument.createRange();
13902 nodeRange.selectNode(node);
13904 nodeRange.selectNodeContents(node);
13908 range.collapse(true);
13910 nodeRange.collapse(true);
13912 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13913 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13915 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13917 var nodeIsBefore = ss == 1;
13918 var nodeIsAfter = ee == -1;
13920 if (nodeIsBefore && nodeIsAfter)
13922 if (!nodeIsBefore && nodeIsAfter)
13923 return 1; //right trailed.
13925 if (nodeIsBefore && !nodeIsAfter)
13926 return 2; // left trailed.
13931 // private? - in a new class?
13932 cleanUpPaste : function()
13934 // cleans up the whole document..
13935 Roo.log('cleanuppaste');
13937 this.cleanUpChildren(this.doc.body);
13938 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13939 if (clean != this.doc.body.innerHTML) {
13940 this.doc.body.innerHTML = clean;
13945 cleanWordChars : function(input) {// change the chars to hex code
13946 var he = Roo.HtmlEditorCore;
13948 var output = input;
13949 Roo.each(he.swapCodes, function(sw) {
13950 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13952 output = output.replace(swapper, sw[1]);
13959 cleanUpChildren : function (n)
13961 if (!n.childNodes.length) {
13964 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13965 this.cleanUpChild(n.childNodes[i]);
13972 cleanUpChild : function (node)
13975 //console.log(node);
13976 if (node.nodeName == "#text") {
13977 // clean up silly Windows -- stuff?
13980 if (node.nodeName == "#comment") {
13981 node.parentNode.removeChild(node);
13982 // clean up silly Windows -- stuff?
13986 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13988 node.parentNode.removeChild(node);
13993 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13995 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13996 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13998 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13999 // remove_keep_children = true;
14002 if (remove_keep_children) {
14003 this.cleanUpChildren(node);
14004 // inserts everything just before this node...
14005 while (node.childNodes.length) {
14006 var cn = node.childNodes[0];
14007 node.removeChild(cn);
14008 node.parentNode.insertBefore(cn, node);
14010 node.parentNode.removeChild(node);
14014 if (!node.attributes || !node.attributes.length) {
14015 this.cleanUpChildren(node);
14019 function cleanAttr(n,v)
14022 if (v.match(/^\./) || v.match(/^\//)) {
14025 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14028 if (v.match(/^#/)) {
14031 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14032 node.removeAttribute(n);
14036 function cleanStyle(n,v)
14038 if (v.match(/expression/)) { //XSS?? should we even bother..
14039 node.removeAttribute(n);
14042 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14043 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14046 var parts = v.split(/;/);
14049 Roo.each(parts, function(p) {
14050 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14054 var l = p.split(':').shift().replace(/\s+/g,'');
14055 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14057 if ( cblack.indexOf(l) > -1) {
14058 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14059 //node.removeAttribute(n);
14063 // only allow 'c whitelisted system attributes'
14064 if ( cwhite.length && cwhite.indexOf(l) < 0) {
14065 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14066 //node.removeAttribute(n);
14076 if (clean.length) {
14077 node.setAttribute(n, clean.join(';'));
14079 node.removeAttribute(n);
14085 for (var i = node.attributes.length-1; i > -1 ; i--) {
14086 var a = node.attributes[i];
14089 if (a.name.toLowerCase().substr(0,2)=='on') {
14090 node.removeAttribute(a.name);
14093 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14094 node.removeAttribute(a.name);
14097 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14098 cleanAttr(a.name,a.value); // fixme..
14101 if (a.name == 'style') {
14102 cleanStyle(a.name,a.value);
14105 /// clean up MS crap..
14106 // tecnically this should be a list of valid class'es..
14109 if (a.name == 'class') {
14110 if (a.value.match(/^Mso/)) {
14111 node.className = '';
14114 if (a.value.match(/body/)) {
14115 node.className = '';
14126 this.cleanUpChildren(node);
14132 // hide stuff that is not compatible
14146 * @event specialkey
14150 * @cfg {String} fieldClass @hide
14153 * @cfg {String} focusClass @hide
14156 * @cfg {String} autoCreate @hide
14159 * @cfg {String} inputType @hide
14162 * @cfg {String} invalidClass @hide
14165 * @cfg {String} invalidText @hide
14168 * @cfg {String} msgFx @hide
14171 * @cfg {String} validateOnBlur @hide
14175 Roo.HtmlEditorCore.white = [
14176 'area', 'br', 'img', 'input', 'hr', 'wbr',
14178 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14179 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14180 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14181 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14182 'table', 'ul', 'xmp',
14184 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14187 'dir', 'menu', 'ol', 'ul', 'dl',
14193 Roo.HtmlEditorCore.black = [
14194 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14196 'base', 'basefont', 'bgsound', 'blink', 'body',
14197 'frame', 'frameset', 'head', 'html', 'ilayer',
14198 'iframe', 'layer', 'link', 'meta', 'object',
14199 'script', 'style' ,'title', 'xml' // clean later..
14201 Roo.HtmlEditorCore.clean = [
14202 'script', 'style', 'title', 'xml'
14204 Roo.HtmlEditorCore.remove = [
14209 Roo.HtmlEditorCore.ablack = [
14213 Roo.HtmlEditorCore.aclean = [
14214 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14218 Roo.HtmlEditorCore.pwhite= [
14219 'http', 'https', 'mailto'
14222 // white listed style attributes.
14223 Roo.HtmlEditorCore.cwhite= [
14224 // 'text-align', /// default is to allow most things..
14230 // black listed style attributes.
14231 Roo.HtmlEditorCore.cblack= [
14232 // 'font-size' -- this can be set by the project
14236 Roo.HtmlEditorCore.swapCodes =[
14255 * @class Roo.bootstrap.HtmlEditor
14256 * @extends Roo.bootstrap.TextArea
14257 * Bootstrap HtmlEditor class
14260 * Create a new HtmlEditor
14261 * @param {Object} config The config object
14264 Roo.bootstrap.HtmlEditor = function(config){
14265 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14266 if (!this.toolbars) {
14267 this.toolbars = [];
14269 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14272 * @event initialize
14273 * Fires when the editor is fully initialized (including the iframe)
14274 * @param {HtmlEditor} this
14279 * Fires when the editor is first receives the focus. Any insertion must wait
14280 * until after this event.
14281 * @param {HtmlEditor} this
14285 * @event beforesync
14286 * Fires before the textarea is updated with content from the editor iframe. Return false
14287 * to cancel the sync.
14288 * @param {HtmlEditor} this
14289 * @param {String} html
14293 * @event beforepush
14294 * Fires before the iframe editor is updated with content from the textarea. Return false
14295 * to cancel the push.
14296 * @param {HtmlEditor} this
14297 * @param {String} html
14302 * Fires when the textarea is updated with content from the editor iframe.
14303 * @param {HtmlEditor} this
14304 * @param {String} html
14309 * Fires when the iframe editor is updated with content from the textarea.
14310 * @param {HtmlEditor} this
14311 * @param {String} html
14315 * @event editmodechange
14316 * Fires when the editor switches edit modes
14317 * @param {HtmlEditor} this
14318 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14320 editmodechange: true,
14322 * @event editorevent
14323 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14324 * @param {HtmlEditor} this
14328 * @event firstfocus
14329 * Fires when on first focus - needed by toolbars..
14330 * @param {HtmlEditor} this
14335 * Auto save the htmlEditor value as a file into Events
14336 * @param {HtmlEditor} this
14340 * @event savedpreview
14341 * preview the saved version of htmlEditor
14342 * @param {HtmlEditor} this
14349 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14353 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14358 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14363 * @cfg {Number} height (in pixels)
14367 * @cfg {Number} width (in pixels)
14372 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14375 stylesheets: false,
14380 // private properties
14381 validationEvent : false,
14383 initialized : false,
14386 onFocus : Roo.emptyFn,
14388 hideMode:'offsets',
14391 tbContainer : false,
14393 toolbarContainer :function() {
14394 return this.wrap.select('.x-html-editor-tb',true).first();
14398 * Protected method that will not generally be called directly. It
14399 * is called when the editor creates its toolbar. Override this method if you need to
14400 * add custom toolbar buttons.
14401 * @param {HtmlEditor} editor
14403 createToolbar : function(){
14405 Roo.log("create toolbars");
14407 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14408 this.toolbars[0].render(this.toolbarContainer());
14412 // if (!editor.toolbars || !editor.toolbars.length) {
14413 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14416 // for (var i =0 ; i < editor.toolbars.length;i++) {
14417 // editor.toolbars[i] = Roo.factory(
14418 // typeof(editor.toolbars[i]) == 'string' ?
14419 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14420 // Roo.bootstrap.HtmlEditor);
14421 // editor.toolbars[i].init(editor);
14427 onRender : function(ct, position)
14429 // Roo.log("Call onRender: " + this.xtype);
14431 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14433 this.wrap = this.inputEl().wrap({
14434 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14437 this.editorcore.onRender(ct, position);
14439 if (this.resizable) {
14440 this.resizeEl = new Roo.Resizable(this.wrap, {
14444 minHeight : this.height,
14445 height: this.height,
14446 handles : this.resizable,
14449 resize : function(r, w, h) {
14450 _t.onResize(w,h); // -something
14456 this.createToolbar(this);
14459 if(!this.width && this.resizable){
14460 this.setSize(this.wrap.getSize());
14462 if (this.resizeEl) {
14463 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14464 // should trigger onReize..
14470 onResize : function(w, h)
14472 Roo.log('resize: ' +w + ',' + h );
14473 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14477 if(this.inputEl() ){
14478 if(typeof w == 'number'){
14479 var aw = w - this.wrap.getFrameWidth('lr');
14480 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14483 if(typeof h == 'number'){
14484 var tbh = -11; // fixme it needs to tool bar size!
14485 for (var i =0; i < this.toolbars.length;i++) {
14486 // fixme - ask toolbars for heights?
14487 tbh += this.toolbars[i].el.getHeight();
14488 //if (this.toolbars[i].footer) {
14489 // tbh += this.toolbars[i].footer.el.getHeight();
14497 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14498 ah -= 5; // knock a few pixes off for look..
14499 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14503 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14504 this.editorcore.onResize(ew,eh);
14509 * Toggles the editor between standard and source edit mode.
14510 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14512 toggleSourceEdit : function(sourceEditMode)
14514 this.editorcore.toggleSourceEdit(sourceEditMode);
14516 if(this.editorcore.sourceEditMode){
14517 Roo.log('editor - showing textarea');
14520 // Roo.log(this.syncValue());
14522 this.inputEl().removeClass('hide');
14523 this.inputEl().dom.removeAttribute('tabIndex');
14524 this.inputEl().focus();
14526 Roo.log('editor - hiding textarea');
14528 // Roo.log(this.pushValue());
14531 this.inputEl().addClass('hide');
14532 this.inputEl().dom.setAttribute('tabIndex', -1);
14533 //this.deferFocus();
14536 if(this.resizable){
14537 this.setSize(this.wrap.getSize());
14540 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14543 // private (for BoxComponent)
14544 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14546 // private (for BoxComponent)
14547 getResizeEl : function(){
14551 // private (for BoxComponent)
14552 getPositionEl : function(){
14557 initEvents : function(){
14558 this.originalValue = this.getValue();
14562 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14565 // markInvalid : Roo.emptyFn,
14567 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14570 // clearInvalid : Roo.emptyFn,
14572 setValue : function(v){
14573 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14574 this.editorcore.pushValue();
14579 deferFocus : function(){
14580 this.focus.defer(10, this);
14584 focus : function(){
14585 this.editorcore.focus();
14591 onDestroy : function(){
14597 for (var i =0; i < this.toolbars.length;i++) {
14598 // fixme - ask toolbars for heights?
14599 this.toolbars[i].onDestroy();
14602 this.wrap.dom.innerHTML = '';
14603 this.wrap.remove();
14608 onFirstFocus : function(){
14609 //Roo.log("onFirstFocus");
14610 this.editorcore.onFirstFocus();
14611 for (var i =0; i < this.toolbars.length;i++) {
14612 this.toolbars[i].onFirstFocus();
14618 syncValue : function()
14620 this.editorcore.syncValue();
14623 pushValue : function()
14625 this.editorcore.pushValue();
14629 // hide stuff that is not compatible
14643 * @event specialkey
14647 * @cfg {String} fieldClass @hide
14650 * @cfg {String} focusClass @hide
14653 * @cfg {String} autoCreate @hide
14656 * @cfg {String} inputType @hide
14659 * @cfg {String} invalidClass @hide
14662 * @cfg {String} invalidText @hide
14665 * @cfg {String} msgFx @hide
14668 * @cfg {String} validateOnBlur @hide
14679 * @class Roo.bootstrap.HtmlEditorToolbar1
14684 new Roo.bootstrap.HtmlEditor({
14687 new Roo.bootstrap.HtmlEditorToolbar1({
14688 disable : { fonts: 1 , format: 1, ..., ... , ...],
14694 * @cfg {Object} disable List of elements to disable..
14695 * @cfg {Array} btns List of additional buttons.
14699 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14702 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14705 Roo.apply(this, config);
14707 // default disabled, based on 'good practice'..
14708 this.disable = this.disable || {};
14709 Roo.applyIf(this.disable, {
14712 specialElements : true
14714 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14716 this.editor = config.editor;
14717 this.editorcore = config.editor.editorcore;
14719 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14721 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14722 // dont call parent... till later.
14724 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14730 editorcore : false,
14735 "h1","h2","h3","h4","h5","h6",
14737 "abbr", "acronym", "address", "cite", "samp", "var",
14741 onRender : function(ct, position)
14743 // Roo.log("Call onRender: " + this.xtype);
14745 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14747 this.el.dom.style.marginBottom = '0';
14749 var editorcore = this.editorcore;
14750 var editor= this.editor;
14753 var btn = function(id,cmd , toggle, handler){
14755 var event = toggle ? 'toggle' : 'click';
14760 xns: Roo.bootstrap,
14763 enableToggle:toggle !== false,
14765 pressed : toggle ? false : null,
14768 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14769 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14778 xns: Roo.bootstrap,
14779 glyphicon : 'font',
14783 xns: Roo.bootstrap,
14787 Roo.each(this.formats, function(f) {
14788 style.menu.items.push({
14790 xns: Roo.bootstrap,
14791 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14796 editorcore.insertTag(this.tagname);
14803 children.push(style);
14806 btn('bold',false,true);
14807 btn('italic',false,true);
14808 btn('align-left', 'justifyleft',true);
14809 btn('align-center', 'justifycenter',true);
14810 btn('align-right' , 'justifyright',true);
14811 btn('link', false, false, function(btn) {
14812 //Roo.log("create link?");
14813 var url = prompt(this.createLinkText, this.defaultLinkValue);
14814 if(url && url != 'http:/'+'/'){
14815 this.editorcore.relayCmd('createlink', url);
14818 btn('list','insertunorderedlist',true);
14819 btn('pencil', false,true, function(btn){
14822 this.toggleSourceEdit(btn.pressed);
14828 xns: Roo.bootstrap,
14833 xns: Roo.bootstrap,
14838 cog.menu.items.push({
14840 xns: Roo.bootstrap,
14841 html : Clean styles,
14846 editorcore.insertTag(this.tagname);
14855 this.xtype = 'Navbar';
14857 for(var i=0;i< children.length;i++) {
14859 this.buttons.add(this.addxtypeChild(children[i]));
14863 editor.on('editorevent', this.updateToolbar, this);
14865 onBtnClick : function(id)
14867 this.editorcore.relayCmd(id);
14868 this.editorcore.focus();
14872 * Protected method that will not generally be called directly. It triggers
14873 * a toolbar update by reading the markup state of the current selection in the editor.
14875 updateToolbar: function(){
14877 if(!this.editorcore.activated){
14878 this.editor.onFirstFocus(); // is this neeed?
14882 var btns = this.buttons;
14883 var doc = this.editorcore.doc;
14884 btns.get('bold').setActive(doc.queryCommandState('bold'));
14885 btns.get('italic').setActive(doc.queryCommandState('italic'));
14886 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14888 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14889 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14890 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14892 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14893 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14896 var ans = this.editorcore.getAllAncestors();
14897 if (this.formatCombo) {
14900 var store = this.formatCombo.store;
14901 this.formatCombo.setValue("");
14902 for (var i =0; i < ans.length;i++) {
14903 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14905 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14913 // hides menus... - so this cant be on a menu...
14914 Roo.bootstrap.MenuMgr.hideAll();
14916 Roo.bootstrap.MenuMgr.hideAll();
14917 //this.editorsyncValue();
14919 onFirstFocus: function() {
14920 this.buttons.each(function(item){
14924 toggleSourceEdit : function(sourceEditMode){
14927 if(sourceEditMode){
14928 Roo.log("disabling buttons");
14929 this.buttons.each( function(item){
14930 if(item.cmd != 'pencil'){
14936 Roo.log("enabling buttons");
14937 if(this.editorcore.initialized){
14938 this.buttons.each( function(item){
14944 Roo.log("calling toggole on editor");
14945 // tell the editor that it's been pressed..
14946 this.editor.toggleSourceEdit(sourceEditMode);
14956 * @class Roo.bootstrap.Table.AbstractSelectionModel
14957 * @extends Roo.util.Observable
14958 * Abstract base class for grid SelectionModels. It provides the interface that should be
14959 * implemented by descendant classes. This class should not be directly instantiated.
14962 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14963 this.locked = false;
14964 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14968 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14969 /** @ignore Called by the grid automatically. Do not call directly. */
14970 init : function(grid){
14976 * Locks the selections.
14979 this.locked = true;
14983 * Unlocks the selections.
14985 unlock : function(){
14986 this.locked = false;
14990 * Returns true if the selections are locked.
14991 * @return {Boolean}
14993 isLocked : function(){
14994 return this.locked;
14998 * @class Roo.bootstrap.Table.ColumnModel
14999 * @extends Roo.util.Observable
15000 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
15001 * the columns in the table.
15004 * @param {Object} config An Array of column config objects. See this class's
15005 * config objects for details.
15007 Roo.bootstrap.Table.ColumnModel = function(config){
15009 * The config passed into the constructor
15011 this.config = config;
15014 // if no id, create one
15015 // if the column does not have a dataIndex mapping,
15016 // map it to the order it is in the config
15017 for(var i = 0, len = config.length; i < len; i++){
15019 if(typeof c.dataIndex == "undefined"){
15022 if(typeof c.renderer == "string"){
15023 c.renderer = Roo.util.Format[c.renderer];
15025 if(typeof c.id == "undefined"){
15028 // if(c.editor && c.editor.xtype){
15029 // c.editor = Roo.factory(c.editor, Roo.grid);
15031 // if(c.editor && c.editor.isFormField){
15032 // c.editor = new Roo.grid.GridEditor(c.editor);
15035 this.lookup[c.id] = c;
15039 * The width of columns which have no width specified (defaults to 100)
15042 this.defaultWidth = 100;
15045 * Default sortable of columns which have no sortable specified (defaults to false)
15048 this.defaultSortable = false;
15052 * @event widthchange
15053 * Fires when the width of a column changes.
15054 * @param {ColumnModel} this
15055 * @param {Number} columnIndex The column index
15056 * @param {Number} newWidth The new width
15058 "widthchange": true,
15060 * @event headerchange
15061 * Fires when the text of a header changes.
15062 * @param {ColumnModel} this
15063 * @param {Number} columnIndex The column index
15064 * @param {Number} newText The new header text
15066 "headerchange": true,
15068 * @event hiddenchange
15069 * Fires when a column is hidden or "unhidden".
15070 * @param {ColumnModel} this
15071 * @param {Number} columnIndex The column index
15072 * @param {Boolean} hidden true if hidden, false otherwise
15074 "hiddenchange": true,
15076 * @event columnmoved
15077 * Fires when a column is moved.
15078 * @param {ColumnModel} this
15079 * @param {Number} oldIndex
15080 * @param {Number} newIndex
15082 "columnmoved" : true,
15084 * @event columlockchange
15085 * Fires when a column's locked state is changed
15086 * @param {ColumnModel} this
15087 * @param {Number} colIndex
15088 * @param {Boolean} locked true if locked
15090 "columnlockchange" : true
15092 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
15094 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
15096 * @cfg {String} header The header text to display in the Grid view.
15099 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
15100 * {@link Roo.data.Record} definition from which to draw the column's value. If not
15101 * specified, the column's index is used as an index into the Record's data Array.
15104 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
15105 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
15108 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
15109 * Defaults to the value of the {@link #defaultSortable} property.
15110 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
15113 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
15116 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
15119 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
15122 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
15125 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
15126 * given the cell's data value. See {@link #setRenderer}. If not specified, the
15127 * default renderer uses the raw data value.
15130 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
15134 * Returns the id of the column at the specified index.
15135 * @param {Number} index The column index
15136 * @return {String} the id
15138 getColumnId : function(index){
15139 return this.config[index].id;
15143 * Returns the column for a specified id.
15144 * @param {String} id The column id
15145 * @return {Object} the column
15147 getColumnById : function(id){
15148 return this.lookup[id];
15153 * Returns the column for a specified dataIndex.
15154 * @param {String} dataIndex The column dataIndex
15155 * @return {Object|Boolean} the column or false if not found
15157 getColumnByDataIndex: function(dataIndex){
15158 var index = this.findColumnIndex(dataIndex);
15159 return index > -1 ? this.config[index] : false;
15163 * Returns the index for a specified column id.
15164 * @param {String} id The column id
15165 * @return {Number} the index, or -1 if not found
15167 getIndexById : function(id){
15168 for(var i = 0, len = this.config.length; i < len; i++){
15169 if(this.config[i].id == id){
15177 * Returns the index for a specified column dataIndex.
15178 * @param {String} dataIndex The column dataIndex
15179 * @return {Number} the index, or -1 if not found
15182 findColumnIndex : function(dataIndex){
15183 for(var i = 0, len = this.config.length; i < len; i++){
15184 if(this.config[i].dataIndex == dataIndex){
15192 moveColumn : function(oldIndex, newIndex){
15193 var c = this.config[oldIndex];
15194 this.config.splice(oldIndex, 1);
15195 this.config.splice(newIndex, 0, c);
15196 this.dataMap = null;
15197 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15200 isLocked : function(colIndex){
15201 return this.config[colIndex].locked === true;
15204 setLocked : function(colIndex, value, suppressEvent){
15205 if(this.isLocked(colIndex) == value){
15208 this.config[colIndex].locked = value;
15209 if(!suppressEvent){
15210 this.fireEvent("columnlockchange", this, colIndex, value);
15214 getTotalLockedWidth : function(){
15215 var totalWidth = 0;
15216 for(var i = 0; i < this.config.length; i++){
15217 if(this.isLocked(i) && !this.isHidden(i)){
15218 this.totalWidth += this.getColumnWidth(i);
15224 getLockedCount : function(){
15225 for(var i = 0, len = this.config.length; i < len; i++){
15226 if(!this.isLocked(i)){
15233 * Returns the number of columns.
15236 getColumnCount : function(visibleOnly){
15237 if(visibleOnly === true){
15239 for(var i = 0, len = this.config.length; i < len; i++){
15240 if(!this.isHidden(i)){
15246 return this.config.length;
15250 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15251 * @param {Function} fn
15252 * @param {Object} scope (optional)
15253 * @return {Array} result
15255 getColumnsBy : function(fn, scope){
15257 for(var i = 0, len = this.config.length; i < len; i++){
15258 var c = this.config[i];
15259 if(fn.call(scope||this, c, i) === true){
15267 * Returns true if the specified column is sortable.
15268 * @param {Number} col The column index
15269 * @return {Boolean}
15271 isSortable : function(col){
15272 if(typeof this.config[col].sortable == "undefined"){
15273 return this.defaultSortable;
15275 return this.config[col].sortable;
15279 * Returns the rendering (formatting) function defined for the column.
15280 * @param {Number} col The column index.
15281 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15283 getRenderer : function(col){
15284 if(!this.config[col].renderer){
15285 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15287 return this.config[col].renderer;
15291 * Sets the rendering (formatting) function for a column.
15292 * @param {Number} col The column index
15293 * @param {Function} fn The function to use to process the cell's raw data
15294 * to return HTML markup for the grid view. The render function is called with
15295 * the following parameters:<ul>
15296 * <li>Data value.</li>
15297 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15298 * <li>css A CSS style string to apply to the table cell.</li>
15299 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15300 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15301 * <li>Row index</li>
15302 * <li>Column index</li>
15303 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15305 setRenderer : function(col, fn){
15306 this.config[col].renderer = fn;
15310 * Returns the width for the specified column.
15311 * @param {Number} col The column index
15314 getColumnWidth : function(col){
15315 return this.config[col].width * 1 || this.defaultWidth;
15319 * Sets the width for a column.
15320 * @param {Number} col The column index
15321 * @param {Number} width The new width
15323 setColumnWidth : function(col, width, suppressEvent){
15324 this.config[col].width = width;
15325 this.totalWidth = null;
15326 if(!suppressEvent){
15327 this.fireEvent("widthchange", this, col, width);
15332 * Returns the total width of all columns.
15333 * @param {Boolean} includeHidden True to include hidden column widths
15336 getTotalWidth : function(includeHidden){
15337 if(!this.totalWidth){
15338 this.totalWidth = 0;
15339 for(var i = 0, len = this.config.length; i < len; i++){
15340 if(includeHidden || !this.isHidden(i)){
15341 this.totalWidth += this.getColumnWidth(i);
15345 return this.totalWidth;
15349 * Returns the header for the specified column.
15350 * @param {Number} col The column index
15353 getColumnHeader : function(col){
15354 return this.config[col].header;
15358 * Sets the header for a column.
15359 * @param {Number} col The column index
15360 * @param {String} header The new header
15362 setColumnHeader : function(col, header){
15363 this.config[col].header = header;
15364 this.fireEvent("headerchange", this, col, header);
15368 * Returns the tooltip for the specified column.
15369 * @param {Number} col The column index
15372 getColumnTooltip : function(col){
15373 return this.config[col].tooltip;
15376 * Sets the tooltip for a column.
15377 * @param {Number} col The column index
15378 * @param {String} tooltip The new tooltip
15380 setColumnTooltip : function(col, tooltip){
15381 this.config[col].tooltip = tooltip;
15385 * Returns the dataIndex for the specified column.
15386 * @param {Number} col The column index
15389 getDataIndex : function(col){
15390 return this.config[col].dataIndex;
15394 * Sets the dataIndex for a column.
15395 * @param {Number} col The column index
15396 * @param {Number} dataIndex The new dataIndex
15398 setDataIndex : function(col, dataIndex){
15399 this.config[col].dataIndex = dataIndex;
15405 * Returns true if the cell is editable.
15406 * @param {Number} colIndex The column index
15407 * @param {Number} rowIndex The row index
15408 * @return {Boolean}
15410 isCellEditable : function(colIndex, rowIndex){
15411 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15415 * Returns the editor defined for the cell/column.
15416 * return false or null to disable editing.
15417 * @param {Number} colIndex The column index
15418 * @param {Number} rowIndex The row index
15421 getCellEditor : function(colIndex, rowIndex){
15422 return this.config[colIndex].editor;
15426 * Sets if a column is editable.
15427 * @param {Number} col The column index
15428 * @param {Boolean} editable True if the column is editable
15430 setEditable : function(col, editable){
15431 this.config[col].editable = editable;
15436 * Returns true if the column is hidden.
15437 * @param {Number} colIndex The column index
15438 * @return {Boolean}
15440 isHidden : function(colIndex){
15441 return this.config[colIndex].hidden;
15446 * Returns true if the column width cannot be changed
15448 isFixed : function(colIndex){
15449 return this.config[colIndex].fixed;
15453 * Returns true if the column can be resized
15454 * @return {Boolean}
15456 isResizable : function(colIndex){
15457 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15460 * Sets if a column is hidden.
15461 * @param {Number} colIndex The column index
15462 * @param {Boolean} hidden True if the column is hidden
15464 setHidden : function(colIndex, hidden){
15465 this.config[colIndex].hidden = hidden;
15466 this.totalWidth = null;
15467 this.fireEvent("hiddenchange", this, colIndex, hidden);
15471 * Sets the editor for a column.
15472 * @param {Number} col The column index
15473 * @param {Object} editor The editor object
15475 setEditor : function(col, editor){
15476 this.config[col].editor = editor;
15480 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15481 if(typeof value == "string" && value.length < 1){
15487 // Alias for backwards compatibility
15488 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15491 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15492 * @class Roo.bootstrap.Table.RowSelectionModel
15493 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15494 * It supports multiple selections and keyboard selection/navigation.
15496 * @param {Object} config
15499 Roo.bootstrap.Table.RowSelectionModel = function(config){
15500 Roo.apply(this, config);
15501 this.selections = new Roo.util.MixedCollection(false, function(o){
15506 this.lastActive = false;
15510 * @event selectionchange
15511 * Fires when the selection changes
15512 * @param {SelectionModel} this
15514 "selectionchange" : true,
15516 * @event afterselectionchange
15517 * Fires after the selection changes (eg. by key press or clicking)
15518 * @param {SelectionModel} this
15520 "afterselectionchange" : true,
15522 * @event beforerowselect
15523 * Fires when a row is selected being selected, return false to cancel.
15524 * @param {SelectionModel} this
15525 * @param {Number} rowIndex The selected index
15526 * @param {Boolean} keepExisting False if other selections will be cleared
15528 "beforerowselect" : true,
15531 * Fires when a row is selected.
15532 * @param {SelectionModel} this
15533 * @param {Number} rowIndex The selected index
15534 * @param {Roo.data.Record} r The record
15536 "rowselect" : true,
15538 * @event rowdeselect
15539 * Fires when a row is deselected.
15540 * @param {SelectionModel} this
15541 * @param {Number} rowIndex The selected index
15543 "rowdeselect" : true
15545 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15546 this.locked = false;
15549 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15551 * @cfg {Boolean} singleSelect
15552 * True to allow selection of only one row at a time (defaults to false)
15554 singleSelect : false,
15557 initEvents : function(){
15559 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15560 this.grid.on("mousedown", this.handleMouseDown, this);
15561 }else{ // allow click to work like normal
15562 this.grid.on("rowclick", this.handleDragableRowClick, this);
15565 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15566 "up" : function(e){
15568 this.selectPrevious(e.shiftKey);
15569 }else if(this.last !== false && this.lastActive !== false){
15570 var last = this.last;
15571 this.selectRange(this.last, this.lastActive-1);
15572 this.grid.getView().focusRow(this.lastActive);
15573 if(last !== false){
15577 this.selectFirstRow();
15579 this.fireEvent("afterselectionchange", this);
15581 "down" : function(e){
15583 this.selectNext(e.shiftKey);
15584 }else if(this.last !== false && this.lastActive !== false){
15585 var last = this.last;
15586 this.selectRange(this.last, this.lastActive+1);
15587 this.grid.getView().focusRow(this.lastActive);
15588 if(last !== false){
15592 this.selectFirstRow();
15594 this.fireEvent("afterselectionchange", this);
15599 var view = this.grid.view;
15600 view.on("refresh", this.onRefresh, this);
15601 view.on("rowupdated", this.onRowUpdated, this);
15602 view.on("rowremoved", this.onRemove, this);
15606 onRefresh : function(){
15607 var ds = this.grid.dataSource, i, v = this.grid.view;
15608 var s = this.selections;
15609 s.each(function(r){
15610 if((i = ds.indexOfId(r.id)) != -1){
15619 onRemove : function(v, index, r){
15620 this.selections.remove(r);
15624 onRowUpdated : function(v, index, r){
15625 if(this.isSelected(r)){
15626 v.onRowSelect(index);
15632 * @param {Array} records The records to select
15633 * @param {Boolean} keepExisting (optional) True to keep existing selections
15635 selectRecords : function(records, keepExisting){
15637 this.clearSelections();
15639 var ds = this.grid.dataSource;
15640 for(var i = 0, len = records.length; i < len; i++){
15641 this.selectRow(ds.indexOf(records[i]), true);
15646 * Gets the number of selected rows.
15649 getCount : function(){
15650 return this.selections.length;
15654 * Selects the first row in the grid.
15656 selectFirstRow : function(){
15661 * Select the last row.
15662 * @param {Boolean} keepExisting (optional) True to keep existing selections
15664 selectLastRow : function(keepExisting){
15665 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15669 * Selects the row immediately following the last selected row.
15670 * @param {Boolean} keepExisting (optional) True to keep existing selections
15672 selectNext : function(keepExisting){
15673 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15674 this.selectRow(this.last+1, keepExisting);
15675 this.grid.getView().focusRow(this.last);
15680 * Selects the row that precedes the last selected row.
15681 * @param {Boolean} keepExisting (optional) True to keep existing selections
15683 selectPrevious : function(keepExisting){
15685 this.selectRow(this.last-1, keepExisting);
15686 this.grid.getView().focusRow(this.last);
15691 * Returns the selected records
15692 * @return {Array} Array of selected records
15694 getSelections : function(){
15695 return [].concat(this.selections.items);
15699 * Returns the first selected record.
15702 getSelected : function(){
15703 return this.selections.itemAt(0);
15708 * Clears all selections.
15710 clearSelections : function(fast){
15711 if(this.locked) return;
15713 var ds = this.grid.dataSource;
15714 var s = this.selections;
15715 s.each(function(r){
15716 this.deselectRow(ds.indexOfId(r.id));
15720 this.selections.clear();
15727 * Selects all rows.
15729 selectAll : function(){
15730 if(this.locked) return;
15731 this.selections.clear();
15732 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15733 this.selectRow(i, true);
15738 * Returns True if there is a selection.
15739 * @return {Boolean}
15741 hasSelection : function(){
15742 return this.selections.length > 0;
15746 * Returns True if the specified row is selected.
15747 * @param {Number/Record} record The record or index of the record to check
15748 * @return {Boolean}
15750 isSelected : function(index){
15751 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15752 return (r && this.selections.key(r.id) ? true : false);
15756 * Returns True if the specified record id is selected.
15757 * @param {String} id The id of record to check
15758 * @return {Boolean}
15760 isIdSelected : function(id){
15761 return (this.selections.key(id) ? true : false);
15765 handleMouseDown : function(e, t){
15766 var view = this.grid.getView(), rowIndex;
15767 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15770 if(e.shiftKey && this.last !== false){
15771 var last = this.last;
15772 this.selectRange(last, rowIndex, e.ctrlKey);
15773 this.last = last; // reset the last
15774 view.focusRow(rowIndex);
15776 var isSelected = this.isSelected(rowIndex);
15777 if(e.button !== 0 && isSelected){
15778 view.focusRow(rowIndex);
15779 }else if(e.ctrlKey && isSelected){
15780 this.deselectRow(rowIndex);
15781 }else if(!isSelected){
15782 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15783 view.focusRow(rowIndex);
15786 this.fireEvent("afterselectionchange", this);
15789 handleDragableRowClick : function(grid, rowIndex, e)
15791 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15792 this.selectRow(rowIndex, false);
15793 grid.view.focusRow(rowIndex);
15794 this.fireEvent("afterselectionchange", this);
15799 * Selects multiple rows.
15800 * @param {Array} rows Array of the indexes of the row to select
15801 * @param {Boolean} keepExisting (optional) True to keep existing selections
15803 selectRows : function(rows, keepExisting){
15805 this.clearSelections();
15807 for(var i = 0, len = rows.length; i < len; i++){
15808 this.selectRow(rows[i], true);
15813 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15814 * @param {Number} startRow The index of the first row in the range
15815 * @param {Number} endRow The index of the last row in the range
15816 * @param {Boolean} keepExisting (optional) True to retain existing selections
15818 selectRange : function(startRow, endRow, keepExisting){
15819 if(this.locked) return;
15821 this.clearSelections();
15823 if(startRow <= endRow){
15824 for(var i = startRow; i <= endRow; i++){
15825 this.selectRow(i, true);
15828 for(var i = startRow; i >= endRow; i--){
15829 this.selectRow(i, true);
15835 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15836 * @param {Number} startRow The index of the first row in the range
15837 * @param {Number} endRow The index of the last row in the range
15839 deselectRange : function(startRow, endRow, preventViewNotify){
15840 if(this.locked) return;
15841 for(var i = startRow; i <= endRow; i++){
15842 this.deselectRow(i, preventViewNotify);
15848 * @param {Number} row The index of the row to select
15849 * @param {Boolean} keepExisting (optional) True to keep existing selections
15851 selectRow : function(index, keepExisting, preventViewNotify){
15852 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15853 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15854 if(!keepExisting || this.singleSelect){
15855 this.clearSelections();
15857 var r = this.grid.dataSource.getAt(index);
15858 this.selections.add(r);
15859 this.last = this.lastActive = index;
15860 if(!preventViewNotify){
15861 this.grid.getView().onRowSelect(index);
15863 this.fireEvent("rowselect", this, index, r);
15864 this.fireEvent("selectionchange", this);
15870 * @param {Number} row The index of the row to deselect
15872 deselectRow : function(index, preventViewNotify){
15873 if(this.locked) return;
15874 if(this.last == index){
15877 if(this.lastActive == index){
15878 this.lastActive = false;
15880 var r = this.grid.dataSource.getAt(index);
15881 this.selections.remove(r);
15882 if(!preventViewNotify){
15883 this.grid.getView().onRowDeselect(index);
15885 this.fireEvent("rowdeselect", this, index);
15886 this.fireEvent("selectionchange", this);
15890 restoreLast : function(){
15892 this.last = this._last;
15897 acceptsNav : function(row, col, cm){
15898 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15902 onEditorKey : function(field, e){
15903 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15908 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15910 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15912 }else if(k == e.ENTER && !e.ctrlKey){
15916 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15918 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15920 }else if(k == e.ESC){
15924 g.startEditing(newCell[0], newCell[1]);
15935 * @class Roo.bootstrap.MessageBar
15936 * @extends Roo.bootstrap.Component
15937 * Bootstrap MessageBar class
15938 * @cfg {String} html contents of the MessageBar
15939 * @cfg {String} weight (info | success | warning | danger) default info
15940 * @cfg {String} beforeClass insert the bar before the given class
15941 * @cfg {Boolean} closable (true | false) default false
15942 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15945 * Create a new Element
15946 * @param {Object} config The config object
15949 Roo.bootstrap.MessageBar = function(config){
15950 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15953 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15959 beforeClass: 'bootstrap-sticky-wrap',
15961 getAutoCreate : function(){
15965 cls: 'alert alert-dismissable alert-' + this.weight,
15970 html: this.html || ''
15976 cfg.cls += ' alert-messages-fixed';
15990 onRender : function(ct, position)
15992 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15995 var cfg = Roo.apply({}, this.getAutoCreate());
15999 cfg.cls += ' ' + this.cls;
16002 cfg.style = this.style;
16004 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
16006 this.el.setVisibilityMode(Roo.Element.DISPLAY);
16009 this.el.select('>button.close').on('click', this.hide, this);
16015 if (!this.rendered) {
16021 this.fireEvent('show', this);
16027 if (!this.rendered) {
16033 this.fireEvent('hide', this);
16036 update : function()
16038 // var e = this.el.dom.firstChild;
16040 // if(this.closable){
16041 // e = e.nextSibling;
16044 // e.data = this.html || '';
16046 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';