4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
21 * Do not use directly - it does not do anything..
22 * @param {Object} config The config object
27 Roo.bootstrap.Component = function(config){
28 Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
34 allowDomMove : false, // to stop relocations in parent onRender...
42 initEvents : function() { },
48 can_build_overlaid : true,
55 // returns the parent component..
56 return Roo.ComponentMgr.get(this.parentId)
62 onRender : function(ct, position)
64 // Roo.log("Call onRender: " + this.xtype);
66 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
69 if (this.el.attr('xtype')) {
70 this.el.attr('xtypex', this.el.attr('xtype'));
71 this.el.dom.removeAttribute('xtype');
81 var cfg = Roo.apply({}, this.getAutoCreate());
84 // fill in the extra attributes
85 if (this.xattr && typeof(this.xattr) =='object') {
86 for (var i in this.xattr) {
87 cfg[i] = this.xattr[i];
92 cfg.dataId = this.dataId;
96 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
99 if (this.style) { // fixme needs to support more complex style data.
100 cfg.style = this.style;
104 cfg.name = this.name;
107 this.el = ct.createChild(cfg, position);
109 if(this.tabIndex !== undefined){
110 this.el.dom.setAttribute('tabIndex', this.tabIndex);
117 getChildContainer : function()
123 addxtype : function(tree,cntr)
127 cn = Roo.factory(tree);
129 cn.parentType = this.xtype; //??
130 cn.parentId = this.id;
132 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
134 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
136 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
138 var build_from_html = Roo.XComponent.build_from_html;
140 var is_body = (tree.xtype == 'Body') ;
142 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
144 var self_cntr_el = Roo.get(this[cntr]());
146 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148 return this.addxtypeChild(tree,cntr);
151 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
154 return this.addxtypeChild(Roo.apply({}, tree),cntr);
157 Roo.log('skipping render');
165 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
171 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
175 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
180 addxtypeChild : function (tree, cntr)
182 Roo.log('addxtypeChild:' + cntr);
184 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
187 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188 (typeof(tree['flexy:foreach']) != 'undefined');
193 // render the element if it's not BODY.
194 if (tree.xtype != 'Body') {
196 cn = Roo.factory(tree);
198 cn.parentType = this.xtype; //??
199 cn.parentId = this.id;
201 var build_from_html = Roo.XComponent.build_from_html;
204 // does the container contain child eleemnts with 'xtype' attributes.
205 // that match this xtype..
206 // note - when we render we create these as well..
207 // so we should check to see if body has xtype set.
208 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
210 var self_cntr_el = Roo.get(this[cntr]());
211 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
213 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
220 //echild.dom.removeAttribute('xtype');
222 Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229 // if object has flexy:if - then it may or may not be rendered.
230 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
231 // skip a flexy if element.
232 Roo.log('skipping render');
235 // actually if flexy:foreach is found, we really want to create
236 // multiple copies here...
238 //Roo.log(this[cntr]());
239 cn.render(this[cntr]());
241 // then add the element..
248 if (typeof (tree.menu) != 'undefined') {
249 tree.menu.parentType = cn.xtype;
250 tree.menu.triggerEl = cn.el;
251 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
255 if (!tree.items || !tree.items.length) {
259 var items = tree.items;
262 //Roo.log(items.length);
264 for(var i =0;i < items.length;i++) {
265 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
286 * @class Roo.bootstrap.Body
287 * @extends Roo.bootstrap.Component
288 * Bootstrap Body class
292 * @param {Object} config The config object
295 Roo.bootstrap.Body = function(config){
296 Roo.bootstrap.Body.superclass.constructor.call(this, config);
297 this.el = Roo.get(document.body);
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
305 onRender : function(ct, position){
308 //this.el.addClass([this.fieldClass, this.cls]);
326 * @class Roo.bootstrap.ButtonGroup
327 * @extends Roo.bootstrap.Component
328 * Bootstrap ButtonGroup class
329 * @cfg {String} size lg | sm | xs (default empty normal)
330 * @cfg {String} align vertical | justified (default none)
331 * @cfg {String} direction up | down (default down)
332 * @cfg {Boolean} toolbar false | true
333 * @cfg {Boolean} btn true | false
338 * @param {Object} config The config object
341 Roo.bootstrap.ButtonGroup = function(config){
342 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
353 getAutoCreate : function(){
359 cfg.html = this.html || cfg.html;
370 if (['vertical','justified'].indexOf(this.align)!==-1) {
371 cfg.cls = 'btn-group-' + this.align;
373 if (this.align == 'justified') {
374 console.log(this.items);
378 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379 cfg.cls += ' btn-group-' + this.size;
382 if (this.direction == 'up') {
383 cfg.cls += ' dropup' ;
399 * @class Roo.bootstrap.Button
400 * @extends Roo.bootstrap.Component
401 * Bootstrap Button class
402 * @cfg {String} html The button content
403 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404 * @cfg {String} size empty | lg | sm | xs
405 * @cfg {String} tag empty | a | input | submit
406 * @cfg {String} href empty or href
407 * @cfg {Boolean} disabled false | true
408 * @cfg {Boolean} isClose false | true
409 * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410 * @cfg {String} badge text for badge
411 * @cfg {String} theme default (or empty) | glow
412 * @cfg {Boolean} inverse false | true
413 * @cfg {Boolean} toggle false | true
414 * @cfg {String} ontext text for on toggle state
415 * @cfg {String} offtext text for off toggle state
416 * @cfg {Boolean} defaulton true | false
417 * @cfg {Boolean} preventDefault (true | false) default true
418 * @cfg {Boolean} removeClass true | false remove the standard class..
419 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
422 * Create a new button
423 * @param {Object} config The config object
427 Roo.bootstrap.Button = function(config){
428 Roo.bootstrap.Button.superclass.constructor.call(this, config);
433 * When a butotn is pressed
434 * @param {Roo.EventObject} e
439 * After the button has been toggles
440 * @param {Roo.EventObject} e
441 * @param {boolean} pressed (also available as button.pressed)
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
465 preventDefault: true,
474 getAutoCreate : function(){
482 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
488 cfg.html = this.html || cfg.html;
490 if (this.toggle == true) {
493 cls: 'slider-frame roo-button',
498 'data-off-text':'OFF',
499 cls: 'slider-button',
505 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506 cfg.cls += ' '+this.weight;
515 cfg["aria-hidden"] = true;
517 cfg.html = "×";
523 if (this.theme==='default') {
524 cfg.cls = 'btn roo-button';
526 //if (this.parentType != 'Navbar') {
527 this.weight = this.weight.length ? this.weight : 'default';
529 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
531 cfg.cls += ' btn-' + this.weight;
533 } else if (this.theme==='glow') {
536 cfg.cls = 'btn-glow roo-button';
538 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
540 cfg.cls += ' ' + this.weight;
546 this.cls += ' inverse';
551 cfg.cls += ' active';
555 cfg.disabled = 'disabled';
559 Roo.log('changing to ul' );
561 this.glyphicon = 'caret';
564 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
566 //gsRoo.log(this.parentType);
567 if (this.parentType === 'Navbar' && !this.parent().bar) {
568 Roo.log('changing to li?');
577 href : this.href || '#'
580 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
581 cfg.cls += ' dropdown';
588 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
590 if (this.glyphicon) {
591 cfg.html = ' ' + cfg.html;
596 cls: 'glyphicon glyphicon-' + this.glyphicon
606 // cfg.cls='btn roo-button';
610 var value = cfg.html;
615 cls: 'glyphicon glyphicon-' + this.glyphicon,
634 cfg.cls += ' dropdown';
635 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
638 if (cfg.tag !== 'a' && this.href !== '') {
639 throw "Tag must be a to set href.";
640 } else if (this.href.length > 0) {
641 cfg.href = this.href;
644 if(this.removeClass){
649 cfg.target = this.target;
654 initEvents: function() {
655 // Roo.log('init events?');
656 // Roo.log(this.el.dom);
657 if (this.el.hasClass('roo-button')) {
658 this.el.on('click', this.onClick, this);
660 this.el.select('.roo-button').on('click', this.onClick, this);
666 onClick : function(e)
672 Roo.log('button on click ');
673 if(this.preventDefault){
676 if (this.pressed === true || this.pressed === false) {
677 this.pressed = !this.pressed;
678 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679 this.fireEvent('toggle', this, e, this.pressed);
683 this.fireEvent('click', this, e);
687 * Enables this button
691 this.disabled = false;
692 this.el.removeClass('disabled');
696 * Disable this button
700 this.disabled = true;
701 this.el.addClass('disabled');
704 * sets the active state on/off,
705 * @param {Boolean} state (optional) Force a particular state
707 setActive : function(v) {
709 this.el[v ? 'addClass' : 'removeClass']('active');
712 * toggles the current active state
714 toggleActive : function()
716 var active = this.el.hasClass('active');
717 this.setActive(!active);
734 * @class Roo.bootstrap.Column
735 * @extends Roo.bootstrap.Component
736 * Bootstrap Column class
737 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739 * @cfg {Number} md colspan out of 12 for computer-sized screens
740 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741 * @cfg {String} html content of column.
744 * Create a new Column
745 * @param {Object} config The config object
748 Roo.bootstrap.Column = function(config){
749 Roo.bootstrap.Column.superclass.constructor.call(this, config);
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
761 getAutoCreate : function(){
762 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
770 ['xs','sm','md','lg'].map(function(size){
771 if (settings[size]) {
772 cfg.cls += ' col-' + size + '-' + settings[size];
775 if (this.html.length) {
776 cfg.html = this.html;
795 * @class Roo.bootstrap.Container
796 * @extends Roo.bootstrap.Component
797 * Bootstrap Container class
798 * @cfg {Boolean} jumbotron is it a jumbotron element
799 * @cfg {String} html content of element
800 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802 * @cfg {String} header content of header (for panel)
803 * @cfg {String} footer content of footer (for panel)
804 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
807 * Create a new Container
808 * @param {Object} config The config object
811 Roo.bootstrap.Container = function(config){
812 Roo.bootstrap.Container.superclass.constructor.call(this, config);
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
825 getChildContainer : function() {
831 if (this.panel.length) {
832 return this.el.select('.panel-body',true).first();
839 getAutoCreate : function(){
845 if (this.jumbotron) {
846 cfg.cls = 'jumbotron';
849 cfg.cls = this.cls + '';
852 if (this.sticky.length) {
854 var bd = Roo.get(document.body);
855 if (!bd.hasClass('bootstrap-sticky')) {
856 bd.addClass('bootstrap-sticky');
857 Roo.select('html',true).setStyle('height', '100%');
860 cfg.cls += 'bootstrap-sticky-' + this.sticky;
864 if (this.well.length) {
868 cfg.cls +=' well well-' +this.well;
878 if (this.panel.length) {
879 cfg.cls += ' panel panel-' + this.panel;
881 if (this.header.length) {
884 cls : 'panel-heading',
900 if (this.footer.length) {
902 cls : 'panel-footer',
910 body.html = this.html || cfg.html;
912 if (!cfg.cls.length) {
913 cfg.cls = 'container';
930 * @class Roo.bootstrap.Img
931 * @extends Roo.bootstrap.Component
932 * Bootstrap Img class
933 * @cfg {Boolean} imgResponsive false | true
934 * @cfg {String} border rounded | circle | thumbnail
935 * @cfg {String} src image source
936 * @cfg {String} alt image alternative text
937 * @cfg {String} href a tag href
938 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
942 * @param {Object} config The config object
945 Roo.bootstrap.Img = function(config){
946 Roo.bootstrap.Img.superclass.constructor.call(this, config);
952 * The img click event for the img.
953 * @param {Roo.EventObject} e
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
967 getAutoCreate : function(){
971 cls: 'img-responsive',
975 cfg.html = this.html || cfg.html;
977 cfg.src = this.src || cfg.src;
979 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980 cfg.cls += ' img-' + this.border;
997 a.target = this.target;
1003 return (this.href) ? a : cfg;
1006 initEvents: function() {
1009 this.el.on('click', this.onClick, this);
1013 onClick : function(e)
1015 Roo.log('img onclick');
1016 this.fireEvent('click', this, e);
1029 * @class Roo.bootstrap.Header
1030 * @extends Roo.bootstrap.Component
1031 * Bootstrap Header class
1032 * @cfg {String} html content of header
1033 * @cfg {Number} level (1|2|3|4|5|6) default 1
1036 * Create a new Header
1037 * @param {Object} config The config object
1041 Roo.bootstrap.Header = function(config){
1042 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1053 getAutoCreate : function(){
1056 tag: 'h' + (1 *this.level),
1057 html: this.html || 'fill in html'
1069 * Ext JS Library 1.1.1
1070 * Copyright(c) 2006-2007, Ext JS, LLC.
1072 * Originally Released Under LGPL - original licence link has changed is not relivant.
1075 * <script type="text/javascript">
1079 * @class Roo.bootstrap.MenuMgr
1080 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1083 Roo.bootstrap.MenuMgr = function(){
1084 var menus, active, groups = {}, attached = false, lastShow = new Date();
1086 // private - called when first menu is created
1089 active = new Roo.util.MixedCollection();
1090 Roo.get(document).addKeyListener(27, function(){
1091 if(active.length > 0){
1099 if(active && active.length > 0){
1100 var c = active.clone();
1110 if(active.length < 1){
1111 Roo.get(document).un("mouseup", onMouseDown);
1119 var last = active.last();
1120 lastShow = new Date();
1123 Roo.get(document).on("mouseup", onMouseDown);
1128 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129 m.parentMenu.activeChild = m;
1130 }else if(last && last.isVisible()){
1131 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1136 function onBeforeHide(m){
1138 m.activeChild.hide();
1140 if(m.autoHideTimer){
1141 clearTimeout(m.autoHideTimer);
1142 delete m.autoHideTimer;
1147 function onBeforeShow(m){
1148 var pm = m.parentMenu;
1149 if(!pm && !m.allowOtherMenus){
1151 }else if(pm && pm.activeChild && active != m){
1152 pm.activeChild.hide();
1157 function onMouseDown(e){
1158 Roo.log("on MouseDown");
1159 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1167 function onBeforeCheck(mi, state){
1169 var g = groups[mi.group];
1170 for(var i = 0, l = g.length; i < l; i++){
1172 g[i].setChecked(false);
1181 * Hides all menus that are currently visible
1183 hideAll : function(){
1188 register : function(menu){
1192 menus[menu.id] = menu;
1193 menu.on("beforehide", onBeforeHide);
1194 menu.on("hide", onHide);
1195 menu.on("beforeshow", onBeforeShow);
1196 menu.on("show", onShow);
1198 if(g && menu.events["checkchange"]){
1202 groups[g].push(menu);
1203 menu.on("checkchange", onCheck);
1208 * Returns a {@link Roo.menu.Menu} object
1209 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210 * be used to generate and return a new Menu instance.
1212 get : function(menu){
1213 if(typeof menu == "string"){ // menu id
1215 }else if(menu.events){ // menu instance
1218 /*else if(typeof menu.length == 'number'){ // array of menu items?
1219 return new Roo.bootstrap.Menu({items:menu});
1220 }else{ // otherwise, must be a config
1221 return new Roo.bootstrap.Menu(menu);
1228 unregister : function(menu){
1229 delete menus[menu.id];
1230 menu.un("beforehide", onBeforeHide);
1231 menu.un("hide", onHide);
1232 menu.un("beforeshow", onBeforeShow);
1233 menu.un("show", onShow);
1235 if(g && menu.events["checkchange"]){
1236 groups[g].remove(menu);
1237 menu.un("checkchange", onCheck);
1242 registerCheckable : function(menuItem){
1243 var g = menuItem.group;
1248 groups[g].push(menuItem);
1249 menuItem.on("beforecheckchange", onBeforeCheck);
1254 unregisterCheckable : function(menuItem){
1255 var g = menuItem.group;
1257 groups[g].remove(menuItem);
1258 menuItem.un("beforecheckchange", onBeforeCheck);
1270 * @class Roo.bootstrap.Menu
1271 * @extends Roo.bootstrap.Component
1272 * Bootstrap Menu class - container for MenuItems
1273 * @cfg {String} type type of menu
1277 * @param {Object} config The config object
1281 Roo.bootstrap.Menu = function(config){
1282 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283 if (this.registerMenu) {
1284 Roo.bootstrap.MenuMgr.register(this);
1289 * Fires before this menu is displayed
1290 * @param {Roo.menu.Menu} this
1295 * Fires before this menu is hidden
1296 * @param {Roo.menu.Menu} this
1301 * Fires after this menu is displayed
1302 * @param {Roo.menu.Menu} this
1307 * Fires after this menu is hidden
1308 * @param {Roo.menu.Menu} this
1313 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314 * @param {Roo.menu.Menu} this
1315 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316 * @param {Roo.EventObject} e
1321 * Fires when the mouse is hovering over this menu
1322 * @param {Roo.menu.Menu} this
1323 * @param {Roo.EventObject} e
1324 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1329 * Fires when the mouse exits this menu
1330 * @param {Roo.menu.Menu} this
1331 * @param {Roo.EventObject} e
1332 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1337 * Fires when a menu item contained in this menu is clicked
1338 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339 * @param {Roo.EventObject} e
1343 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1350 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1353 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1355 registerMenu : true,
1357 menuItems :false, // stores the menu items..
1363 getChildContainer : function() {
1367 getAutoCreate : function(){
1369 //if (['right'].indexOf(this.align)!==-1) {
1370 // cfg.cn[1].cls += ' pull-right'
1374 cls : 'dropdown-menu' ,
1375 style : 'z-index:1000'
1379 if (this.type === 'submenu') {
1380 cfg.cls = 'submenu active'
1385 initEvents : function() {
1387 // Roo.log("ADD event");
1388 // Roo.log(this.triggerEl.dom);
1389 this.triggerEl.on('click', this.onTriggerPress, this);
1390 this.triggerEl.addClass('dropdown-toggle');
1391 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1393 this.el.on("mouseover", this.onMouseOver, this);
1394 this.el.on("mouseout", this.onMouseOut, this);
1398 findTargetItem : function(e){
1399 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1403 //Roo.log(t); Roo.log(t.id);
1405 //Roo.log(this.menuitems);
1406 return this.menuitems.get(t.id);
1408 //return this.items.get(t.menuItemId);
1413 onClick : function(e){
1414 Roo.log("menu.onClick");
1415 var t = this.findTargetItem(e);
1421 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1422 if(t == this.activeItem && t.shouldDeactivate(e)){
1423 this.activeItem.deactivate();
1424 delete this.activeItem;
1428 this.setActiveItem(t, true);
1435 Roo.log('pass click event');
1439 this.fireEvent("click", this, t, e);
1443 onMouseOver : function(e){
1444 var t = this.findTargetItem(e);
1447 // if(t.canActivate && !t.disabled){
1448 // this.setActiveItem(t, true);
1452 this.fireEvent("mouseover", this, e, t);
1454 isVisible : function(){
1455 return !this.hidden;
1457 onMouseOut : function(e){
1458 var t = this.findTargetItem(e);
1461 // if(t == this.activeItem && t.shouldDeactivate(e)){
1462 // this.activeItem.deactivate();
1463 // delete this.activeItem;
1466 this.fireEvent("mouseout", this, e, t);
1471 * Displays this menu relative to another element
1472 * @param {String/HTMLElement/Roo.Element} element The element to align to
1473 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474 * the element (defaults to this.defaultAlign)
1475 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1477 show : function(el, pos, parentMenu){
1478 this.parentMenu = parentMenu;
1482 this.fireEvent("beforeshow", this);
1483 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1486 * Displays this menu at a specific xy position
1487 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1490 showAt : function(xy, parentMenu, /* private: */_e){
1491 this.parentMenu = parentMenu;
1496 this.fireEvent("beforeshow", this);
1498 //xy = this.el.adjustForConstraints(xy);
1500 //this.el.setXY(xy);
1502 this.hideMenuItems();
1503 this.hidden = false;
1504 this.triggerEl.addClass('open');
1506 this.fireEvent("show", this);
1512 this.doFocus.defer(50, this);
1516 doFocus : function(){
1518 this.focusEl.focus();
1523 * Hides this menu and optionally all parent menus
1524 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1526 hide : function(deep){
1528 this.hideMenuItems();
1529 if(this.el && this.isVisible()){
1530 this.fireEvent("beforehide", this);
1531 if(this.activeItem){
1532 this.activeItem.deactivate();
1533 this.activeItem = null;
1535 this.triggerEl.removeClass('open');;
1537 this.fireEvent("hide", this);
1539 if(deep === true && this.parentMenu){
1540 this.parentMenu.hide(true);
1544 onTriggerPress : function(e)
1547 Roo.log('trigger press');
1548 //Roo.log(e.getTarget());
1549 // Roo.log(this.triggerEl.dom);
1550 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1553 if (this.isVisible()) {
1557 this.show(this.triggerEl, false, false);
1566 hideMenuItems : function()
1568 //$(backdrop).remove()
1569 Roo.select('.open',true).each(function(aa) {
1571 aa.removeClass('open');
1572 //var parent = getParent($(this))
1573 //var relatedTarget = { relatedTarget: this }
1575 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576 //if (e.isDefaultPrevented()) return
1577 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1580 addxtypeChild : function (tree, cntr) {
1581 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1583 this.menuitems.add(comp);
1604 * @class Roo.bootstrap.MenuItem
1605 * @extends Roo.bootstrap.Component
1606 * Bootstrap MenuItem class
1607 * @cfg {String} html the menu label
1608 * @cfg {String} href the link
1609 * @cfg {Boolean} preventDefault (true | false) default true
1613 * Create a new MenuItem
1614 * @param {Object} config The config object
1618 Roo.bootstrap.MenuItem = function(config){
1619 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1624 * The raw click event for the entire grid.
1625 * @param {Roo.EventObject} e
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1635 preventDefault: true,
1637 getAutoCreate : function(){
1640 cls: 'dropdown-menu-item',
1650 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1655 initEvents: function() {
1657 //this.el.select('a').on('click', this.onClick, this);
1660 onClick : function(e)
1662 Roo.log('item on click ');
1663 //if(this.preventDefault){
1664 // e.preventDefault();
1666 //this.parent().hideMenuItems();
1668 this.fireEvent('click', this, e);
1687 * @class Roo.bootstrap.MenuSeparator
1688 * @extends Roo.bootstrap.Component
1689 * Bootstrap MenuSeparator class
1692 * Create a new MenuItem
1693 * @param {Object} config The config object
1697 Roo.bootstrap.MenuSeparator = function(config){
1698 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1703 getAutoCreate : function(){
1718 <div class="modal fade">
1719 <div class="modal-dialog">
1720 <div class="modal-content">
1721 <div class="modal-header">
1722 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1723 <h4 class="modal-title">Modal title</h4>
1725 <div class="modal-body">
1726 <p>One fine body…</p>
1728 <div class="modal-footer">
1729 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730 <button type="button" class="btn btn-primary">Save changes</button>
1732 </div><!-- /.modal-content -->
1733 </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1744 * @class Roo.bootstrap.Modal
1745 * @extends Roo.bootstrap.Component
1746 * Bootstrap Modal class
1747 * @cfg {String} title Title of dialog
1748 * @cfg {Array} buttons Array of buttons or standard button set..
1751 * Create a new Modal Dialog
1752 * @param {Object} config The config object
1755 Roo.bootstrap.Modal = function(config){
1756 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1761 * The raw btnclick event for the button
1762 * @param {Roo.EventObject} e
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1770 title : 'test dialog',
1774 onRender : function(ct, position)
1776 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1779 var cfg = Roo.apply({}, this.getAutoCreate());
1782 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1784 //if (!cfg.name.length) {
1788 cfg.cls += ' ' + this.cls;
1791 cfg.style = this.style;
1793 this.el = Roo.get(document.body).createChild(cfg, position);
1795 //var type = this.el.dom.type;
1797 if(this.tabIndex !== undefined){
1798 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1803 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804 this.maskEl.enableDisplayMode("block");
1806 //this.el.addClass("x-dlg-modal");
1809 Roo.each(this.buttons, function(bb) {
1810 b = Roo.apply({}, bb);
1811 b.xns = b.xns || Roo.bootstrap;
1812 b.xtype = b.xtype || 'Button';
1813 if (typeof(b.listeners) == 'undefined') {
1814 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1817 var btn = Roo.factory(b);
1819 btn.onRender(this.el.select('.modal-footer').first());
1823 // render the children.
1826 if(typeof(this.items) != 'undefined'){
1827 var items = this.items;
1830 for(var i =0;i < items.length;i++) {
1831 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1835 this.items = nitems;
1837 //this.el.addClass([this.fieldClass, this.cls]);
1840 getAutoCreate : function(){
1845 html : this.html || ''
1853 cls: "modal-dialog",
1856 cls : "modal-content",
1859 cls : 'modal-header',
1868 cls : 'modal-title',
1876 cls : 'modal-footer'
1892 getChildContainer : function() {
1894 return this.el.select('.modal-body',true).first();
1897 getButtonContainer : function() {
1898 return this.el.select('.modal-footer',true).first();
1901 initEvents : function()
1903 this.el.select('.modal-header .close').on('click', this.hide, this);
1905 // this.addxtype(this);
1909 if (!this.rendered) {
1913 this.el.addClass('on');
1914 this.el.removeClass('fade');
1915 this.el.setStyle('display', 'block');
1916 Roo.get(document.body).addClass("x-body-masked");
1917 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1919 this.el.setStyle('zIndex', '10001');
1920 this.fireEvent('show', this);
1926 Roo.log('Modal hide?!');
1928 Roo.get(document.body).removeClass("x-body-masked");
1929 this.el.removeClass('on');
1930 this.el.addClass('fade');
1931 this.el.setStyle('display', 'none');
1932 this.fireEvent('hide', this);
1934 onButtonClick: function(btn,e)
1937 this.fireEvent('btnclick', btn.name, e);
1942 Roo.apply(Roo.bootstrap.Modal, {
1944 * Button config that displays a single OK button
1953 * Button config that displays Yes and No buttons
1969 * Button config that displays OK and Cancel buttons
1984 * Button config that displays Yes, No and Cancel buttons
2011 * @class Roo.bootstrap.Navbar
2012 * @extends Roo.bootstrap.Component
2013 * Bootstrap Navbar class
2014 * @cfg {Boolean} sidebar has side bar
2015 * @cfg {Boolean} bar is a bar?
2016 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017 * @cfg {String} brand what is brand
2018 * @cfg {Boolean} inverse is inverted color
2019 * @cfg {String} type (nav | pills | tabs)
2020 * @cfg {Boolean} arrangement stacked | justified
2021 * @cfg {String} align (left | right) alignment
2022 * @cfg {String} brand_href href of the brand
2023 * @cfg {Boolean} main (true|false) main nav bar? default false
2024 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2028 * Create a new Navbar
2029 * @param {Object} config The config object
2033 Roo.bootstrap.Navbar = function(config){
2034 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2052 getAutoCreate : function(){
2057 if (this.sidebar === true) {
2065 if (this.bar === true) {
2073 cls: 'navbar-header',
2078 cls: 'navbar-toggle',
2079 'data-toggle': 'collapse',
2084 html: 'Toggle navigation'
2104 cls: 'collapse navbar-collapse'
2109 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2111 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112 cfg.cls += ' navbar-' + this.position;
2113 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2116 if (this.brand !== '') {
2119 href: this.brand_href ? this.brand_href : '#',
2120 cls: 'navbar-brand',
2128 cfg.cls += ' main-nav';
2134 } else if (this.bar === false) {
2137 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2147 if (['tabs','pills'].indexOf(this.type)!==-1) {
2148 cfg.cn[0].cls += ' nav-' + this.type
2150 if (this.type!=='nav') {
2151 Roo.log('nav type must be nav/tabs/pills')
2153 cfg.cn[0].cls += ' navbar-nav'
2156 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157 cfg.cn[0].cls += ' nav-' + this.arrangement;
2160 if (this.align === 'right') {
2161 cfg.cn[0].cls += ' navbar-right';
2164 cfg.cls += ' navbar-inverse';
2172 initEvents :function ()
2174 //Roo.log(this.el.select('.navbar-toggle',true));
2175 this.el.select('.navbar-toggle',true).on('click', function() {
2176 // Roo.log('click');
2177 this.el.select('.navbar-collapse',true).toggleClass('in');
2185 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2187 var size = this.el.getSize();
2188 this.maskEl.setSize(size.width, size.height);
2189 this.maskEl.enableDisplayMode("block");
2198 getChildContainer : function()
2200 if (this.bar === true) {
2201 return this.el.select('.collapse',true).first();
2229 * @class Roo.bootstrap.NavGroup
2230 * @extends Roo.bootstrap.Component
2231 * Bootstrap NavGroup class
2232 * @cfg {String} align left | right
2233 * @cfg {Boolean} inverse false | true
2234 * @cfg {String} type (nav|pills|tab) default nav
2237 * Create a new nav group
2238 * @param {Object} config The config object
2241 Roo.bootstrap.NavGroup = function(config){
2242 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2252 getAutoCreate : function(){
2253 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2260 if (['tabs','pills'].indexOf(this.type)!==-1) {
2261 cfg.cls += ' nav-' + this.type
2263 if (this.type!=='nav') {
2264 Roo.log('nav type must be nav/tabs/pills')
2266 cfg.cls += ' navbar-nav'
2269 if (this.parent().sidebar === true) {
2272 cls: 'dashboard-menu'
2278 if (this.form === true) {
2284 if (this.align === 'right') {
2285 cfg.cls += ' navbar-right';
2287 cfg.cls += ' navbar-left';
2291 if (this.align === 'right') {
2292 cfg.cls += ' navbar-right';
2296 cfg.cls += ' navbar-inverse';
2316 * @class Roo.bootstrap.Navbar.Item
2317 * @extends Roo.bootstrap.Component
2318 * Bootstrap Navbar.Button class
2319 * @cfg {String} href link to
2320 * @cfg {String} html content of button
2321 * @cfg {String} badge text inside badge
2322 * @cfg {String} glyphicon name of glyphicon
2323 * @cfg {String} icon name of font awesome icon
2324 * @cfg {Boolena} active Is item active
2325 * @cfg {Boolean} preventDefault (true | false) default false
2328 * Create a new Navbar Button
2329 * @param {Object} config The config object
2331 Roo.bootstrap.Navbar.Item = function(config){
2332 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2337 * The raw click event for the entire grid.
2338 * @param {Roo.EventObject} e
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
2353 preventDefault : false,
2355 getAutoCreate : function(){
2357 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2359 if (this.parent().parent().sidebar === true) {
2372 cfg.cn[0].html = this.html;
2376 this.cls += ' active';
2380 cfg.cn[0].cls += ' dropdown-toggle';
2381 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2385 cfg.cn[0].tag = 'a',
2386 cfg.cn[0].href = this.href;
2389 if (this.glyphicon) {
2390 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2394 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2406 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2416 if (this.glyphicon) {
2417 if(cfg.html){cfg.html = ' ' + this.html};
2421 cls: 'glyphicon glyphicon-' + this.glyphicon
2426 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2431 cfg.cn[0].html += " <span class='caret'></span>";
2432 //}else if (!this.href) {
2433 // cfg.cn[0].tag='p';
2434 // cfg.cn[0].cls='navbar-text';
2437 cfg.cn[0].href=this.href||'#';
2438 cfg.cn[0].html=this.html;
2441 if (this.badge !== '') {
2444 cfg.cn[0].html + ' ',
2455 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2460 initEvents: function() {
2461 // Roo.log('init events?');
2462 // Roo.log(this.el.dom);
2463 this.el.select('a',true).on('click', this.onClick, this);
2466 onClick : function(e)
2468 if(this.preventDefault){
2472 if(this.fireEvent('click', this, e) === false){
2476 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477 this.onTabsClick(e);
2481 onTabsClick : function(e)
2483 Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484 v.removeClass('active');
2487 this.el.addClass('active');
2489 if(this.href && this.href.substring(0,1) == '#'){
2490 var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2492 Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493 v.removeClass('active');
2496 tab.addClass('active');
2511 * @class Roo.bootstrap.Row
2512 * @extends Roo.bootstrap.Component
2513 * Bootstrap Row class (contains columns...)
2517 * @param {Object} config The config object
2520 Roo.bootstrap.Row = function(config){
2521 Roo.bootstrap.Row.superclass.constructor.call(this, config);
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
2526 getAutoCreate : function(){
2545 * @class Roo.bootstrap.Element
2546 * @extends Roo.bootstrap.Component
2547 * Bootstrap Element class
2548 * @cfg {String} html contents of the element
2549 * @cfg {String} tag tag of the element
2550 * @cfg {String} cls class of the element
2553 * Create a new Element
2554 * @param {Object} config The config object
2557 Roo.bootstrap.Element = function(config){
2558 Roo.bootstrap.Element.superclass.constructor.call(this, config);
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
2568 getAutoCreate : function(){
2593 * @class Roo.bootstrap.Pagination
2594 * @extends Roo.bootstrap.Component
2595 * Bootstrap Pagination class
2596 * @cfg {String} size xs | sm | md | lg
2597 * @cfg {Boolean} inverse false | true
2600 * Create a new Pagination
2601 * @param {Object} config The config object
2604 Roo.bootstrap.Pagination = function(config){
2605 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
2614 getAutoCreate : function(){
2620 cfg.cls += ' inverse';
2626 cfg.cls += " " + this.cls;
2644 * @class Roo.bootstrap.PaginationItem
2645 * @extends Roo.bootstrap.Component
2646 * Bootstrap PaginationItem class
2647 * @cfg {String} html text
2648 * @cfg {String} href the link
2649 * @cfg {Boolean} preventDefault (true | false) default true
2650 * @cfg {Boolean} active (true | false) default false
2654 * Create a new PaginationItem
2655 * @param {Object} config The config object
2659 Roo.bootstrap.PaginationItem = function(config){
2660 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2665 * The raw click event for the entire grid.
2666 * @param {Roo.EventObject} e
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
2676 preventDefault: true,
2680 getAutoCreate : function(){
2686 href : this.href ? this.href : '#',
2687 html : this.html ? this.html : ''
2697 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2703 initEvents: function() {
2705 this.el.on('click', this.onClick, this);
2708 onClick : function(e)
2710 Roo.log('PaginationItem on click ');
2711 if(this.preventDefault){
2715 this.fireEvent('click', this, e);
2731 * @class Roo.bootstrap.Slider
2732 * @extends Roo.bootstrap.Component
2733 * Bootstrap Slider class
2736 * Create a new Slider
2737 * @param {Object} config The config object
2740 Roo.bootstrap.Slider = function(config){
2741 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
2746 getAutoCreate : function(){
2750 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2754 cls: 'ui-slider-handle ui-state-default ui-corner-all'
2772 * @class Roo.bootstrap.Table
2773 * @extends Roo.bootstrap.Component
2774 * Bootstrap Table class
2775 * @cfg {String} cls table class
2776 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777 * @cfg {String} bgcolor Specifies the background color for a table
2778 * @cfg {Number} border Specifies whether the table cells should have borders or not
2779 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780 * @cfg {Number} cellspacing Specifies the space between cells
2781 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783 * @cfg {String} sortable Specifies that the table should be sortable
2784 * @cfg {String} summary Specifies a summary of the content of a table
2785 * @cfg {Number} width Specifies the width of a table
2787 * @cfg {boolean} striped Should the rows be alternative striped
2788 * @cfg {boolean} bordered Add borders to the table
2789 * @cfg {boolean} hover Add hover highlighting
2790 * @cfg {boolean} condensed Format condensed
2791 * @cfg {boolean} responsive Format condensed
2797 * Create a new Table
2798 * @param {Object} config The config object
2801 Roo.bootstrap.Table = function(config){
2802 Roo.bootstrap.Table.superclass.constructor.call(this, config);
2805 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806 this.sm = this.selModel;
2807 this.sm.xmodule = this.xmodule || false;
2809 if (this.cm && typeof(this.cm.config) == 'undefined') {
2810 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811 this.cm = this.colModel;
2812 this.cm.xmodule = this.xmodule || false;
2815 this.store= Roo.factory(this.store, Roo.data);
2816 this.ds = this.store;
2817 this.ds.xmodule = this.xmodule || false;
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
2844 getAutoCreate : function(){
2845 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2854 cfg.cls += ' table-striped';
2857 cfg.cls += ' table-hover';
2859 if (this.bordered) {
2860 cfg.cls += ' table-bordered';
2862 if (this.condensed) {
2863 cfg.cls += ' table-condensed';
2865 if (this.responsive) {
2866 cfg.cls += ' table-responsive';
2873 cfg.cls+= ' ' +this.cls;
2876 // this lot should be simplifed...
2879 cfg.align=this.align;
2882 cfg.bgcolor=this.bgcolor;
2885 cfg.border=this.border;
2887 if (this.cellpadding) {
2888 cfg.cellpadding=this.cellpadding;
2890 if (this.cellspacing) {
2891 cfg.cellspacing=this.cellspacing;
2894 cfg.frame=this.frame;
2897 cfg.rules=this.rules;
2899 if (this.sortable) {
2900 cfg.sortable=this.sortable;
2903 cfg.summary=this.summary;
2906 cfg.width=this.width;
2909 if(this.store || this.cm){
2910 cfg.cn.push(this.renderHeader());
2911 cfg.cn.push(this.renderBody());
2912 cfg.cn.push(this.renderFooter());
2914 cfg.cls+= ' TableGrid';
2920 // initTableGrid : function()
2929 // var cm = this.cm;
2931 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2934 // html: cm.getColumnHeader(i)
2938 // cfg.push(header);
2945 initEvents : function()
2947 if(!this.store || !this.cm){
2951 Roo.log('initEvents with ds!!!!');
2955 Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
2956 e.on('click', _this.sort, _this);
2958 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2959 // this.maskEl.enableDisplayMode("block");
2960 // this.maskEl.show();
2962 this.store.on('load', this.onLoad, this);
2963 this.store.on('beforeload', this.onBeforeLoad, this);
2971 sort : function(e,el)
2973 var sort = Roo.get(el).attr('sort');
2975 //this.store.sortInfo = {field:'created_dt',direction:'DESC'};
2977 //this.store.load();
2980 renderHeader : function()
2989 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2991 var config = cm.config[i];
2995 html: cm.getColumnHeader(i)
2998 if(typeof(config.dataIndex) != 'undefined'){
2999 c.sort = config.dataIndex;
3002 if(typeof(config.sortable) != 'undefined' && config.sortable){
3012 renderBody : function()
3022 renderFooter : function()
3034 Roo.log('ds onload');
3038 var tbody = this.el.select('tbody', true).first();
3042 if(this.store.getCount() > 0){
3043 this.store.data.each(function(d){
3049 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3050 var renderer = cm.getRenderer(i);
3051 var config = cm.config[i];
3055 if(typeof(renderer) !== 'undefined'){
3056 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3059 if(typeof(value) === 'object'){
3069 html: (typeof(value) === 'object') ? '' : value
3072 if(typeof(config.width) != 'undefined'){
3073 td.width = config.width;
3080 tbody.createChild(row);
3088 Roo.each(renders, function(r){
3089 _this.renderColumn(r);
3093 // if(this.loadMask){
3094 // this.maskEl.hide();
3098 onBeforeLoad : function()
3100 Roo.log('ds onBeforeLoad');
3104 // if(this.loadMask){
3105 // this.maskEl.show();
3111 this.el.select('tbody', true).first().dom.innerHTML = '';
3114 getSelectionModel : function(){
3116 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3118 return this.selModel;
3121 renderColumn : function(r)
3124 r.cfg.render(Roo.get(r.id));
3127 Roo.each(r.cfg.cn, function(c){
3132 _this.renderColumn(child);
3149 * @class Roo.bootstrap.TableCell
3150 * @extends Roo.bootstrap.Component
3151 * Bootstrap TableCell class
3152 * @cfg {String} html cell contain text
3153 * @cfg {String} cls cell class
3154 * @cfg {String} tag cell tag (td|th) default td
3155 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3156 * @cfg {String} align Aligns the content in a cell
3157 * @cfg {String} axis Categorizes cells
3158 * @cfg {String} bgcolor Specifies the background color of a cell
3159 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3160 * @cfg {Number} colspan Specifies the number of columns a cell should span
3161 * @cfg {String} headers Specifies one or more header cells a cell is related to
3162 * @cfg {Number} height Sets the height of a cell
3163 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3164 * @cfg {Number} rowspan Sets the number of rows a cell should span
3165 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3166 * @cfg {String} valign Vertical aligns the content in a cell
3167 * @cfg {Number} width Specifies the width of a cell
3170 * Create a new TableCell
3171 * @param {Object} config The config object
3174 Roo.bootstrap.TableCell = function(config){
3175 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3178 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3198 getAutoCreate : function(){
3199 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3219 cfg.align=this.align
3225 cfg.bgcolor=this.bgcolor
3228 cfg.charoff=this.charoff
3231 cfg.colspan=this.colspan
3234 cfg.headers=this.headers
3237 cfg.height=this.height
3240 cfg.nowrap=this.nowrap
3243 cfg.rowspan=this.rowspan
3246 cfg.scope=this.scope
3249 cfg.valign=this.valign
3252 cfg.width=this.width
3271 * @class Roo.bootstrap.TableRow
3272 * @extends Roo.bootstrap.Component
3273 * Bootstrap TableRow class
3274 * @cfg {String} cls row class
3275 * @cfg {String} align Aligns the content in a table row
3276 * @cfg {String} bgcolor Specifies a background color for a table row
3277 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3278 * @cfg {String} valign Vertical aligns the content in a table row
3281 * Create a new TableRow
3282 * @param {Object} config The config object
3285 Roo.bootstrap.TableRow = function(config){
3286 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3289 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3297 getAutoCreate : function(){
3298 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3308 cfg.align = this.align;
3311 cfg.bgcolor = this.bgcolor;
3314 cfg.charoff = this.charoff;
3317 cfg.valign = this.valign;
3335 * @class Roo.bootstrap.TableBody
3336 * @extends Roo.bootstrap.Component
3337 * Bootstrap TableBody class
3338 * @cfg {String} cls element class
3339 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3340 * @cfg {String} align Aligns the content inside the element
3341 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3342 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3345 * Create a new TableBody
3346 * @param {Object} config The config object
3349 Roo.bootstrap.TableBody = function(config){
3350 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3353 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3361 getAutoCreate : function(){
3362 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3376 cfg.align = this.align;
3379 cfg.charoff = this.charoff;
3382 cfg.valign = this.valign;
3389 // initEvents : function()
3396 // this.store = Roo.factory(this.store, Roo.data);
3397 // this.store.on('load', this.onLoad, this);
3399 // this.store.load();
3403 // onLoad: function ()
3405 // this.fireEvent('load', this);
3415 * Ext JS Library 1.1.1
3416 * Copyright(c) 2006-2007, Ext JS, LLC.
3418 * Originally Released Under LGPL - original licence link has changed is not relivant.
3421 * <script type="text/javascript">
3424 // as we use this in bootstrap.
3425 Roo.namespace('Roo.form');
3427 * @class Roo.form.Action
3428 * Internal Class used to handle form actions
3430 * @param {Roo.form.BasicForm} el The form element or its id
3431 * @param {Object} config Configuration options
3436 // define the action interface
3437 Roo.form.Action = function(form, options){
3439 this.options = options || {};
3442 * Client Validation Failed
3445 Roo.form.Action.CLIENT_INVALID = 'client';
3447 * Server Validation Failed
3450 Roo.form.Action.SERVER_INVALID = 'server';
3452 * Connect to Server Failed
3455 Roo.form.Action.CONNECT_FAILURE = 'connect';
3457 * Reading Data from Server Failed
3460 Roo.form.Action.LOAD_FAILURE = 'load';
3462 Roo.form.Action.prototype = {
3464 failureType : undefined,
3465 response : undefined,
3469 run : function(options){
3474 success : function(response){
3479 handleResponse : function(response){
3483 // default connection failure
3484 failure : function(response){
3486 this.response = response;
3487 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3488 this.form.afterAction(this, false);
3491 processResponse : function(response){
3492 this.response = response;
3493 if(!response.responseText){
3496 this.result = this.handleResponse(response);
3500 // utility functions used internally
3501 getUrl : function(appendParams){
3502 var url = this.options.url || this.form.url || this.form.el.dom.action;
3504 var p = this.getParams();
3506 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3512 getMethod : function(){
3513 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3516 getParams : function(){
3517 var bp = this.form.baseParams;
3518 var p = this.options.params;
3520 if(typeof p == "object"){
3521 p = Roo.urlEncode(Roo.applyIf(p, bp));
3522 }else if(typeof p == 'string' && bp){
3523 p += '&' + Roo.urlEncode(bp);
3526 p = Roo.urlEncode(bp);
3531 createCallback : function(){
3533 success: this.success,
3534 failure: this.failure,
3536 timeout: (this.form.timeout*1000),
3537 upload: this.form.fileUpload ? this.success : undefined
3542 Roo.form.Action.Submit = function(form, options){
3543 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3546 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3549 haveProgress : false,
3550 uploadComplete : false,
3552 // uploadProgress indicator.
3553 uploadProgress : function()
3555 if (!this.form.progressUrl) {
3559 if (!this.haveProgress) {
3560 Roo.MessageBox.progress("Uploading", "Uploading");
3562 if (this.uploadComplete) {
3563 Roo.MessageBox.hide();
3567 this.haveProgress = true;
3569 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3571 var c = new Roo.data.Connection();
3573 url : this.form.progressUrl,
3578 success : function(req){
3579 //console.log(data);
3583 rdata = Roo.decode(req.responseText)
3585 Roo.log("Invalid data from server..");
3589 if (!rdata || !rdata.success) {
3591 Roo.MessageBox.alert(Roo.encode(rdata));
3594 var data = rdata.data;
3596 if (this.uploadComplete) {
3597 Roo.MessageBox.hide();
3602 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3603 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3606 this.uploadProgress.defer(2000,this);
3609 failure: function(data) {
3610 Roo.log('progress url failed ');
3621 // run get Values on the form, so it syncs any secondary forms.
3622 this.form.getValues();
3624 var o = this.options;
3625 var method = this.getMethod();
3626 var isPost = method == 'POST';
3627 if(o.clientValidation === false || this.form.isValid()){
3629 if (this.form.progressUrl) {
3630 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3631 (new Date() * 1) + '' + Math.random());
3636 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3637 form:this.form.el.dom,
3638 url:this.getUrl(!isPost),
3640 params:isPost ? this.getParams() : null,
3641 isUpload: this.form.fileUpload
3644 this.uploadProgress();
3646 }else if (o.clientValidation !== false){ // client validation failed
3647 this.failureType = Roo.form.Action.CLIENT_INVALID;
3648 this.form.afterAction(this, false);
3652 success : function(response)
3654 this.uploadComplete= true;
3655 if (this.haveProgress) {
3656 Roo.MessageBox.hide();
3660 var result = this.processResponse(response);
3661 if(result === true || result.success){
3662 this.form.afterAction(this, true);
3666 this.form.markInvalid(result.errors);
3667 this.failureType = Roo.form.Action.SERVER_INVALID;
3669 this.form.afterAction(this, false);
3671 failure : function(response)
3673 this.uploadComplete= true;
3674 if (this.haveProgress) {
3675 Roo.MessageBox.hide();
3678 this.response = response;
3679 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3680 this.form.afterAction(this, false);
3683 handleResponse : function(response){
3684 if(this.form.errorReader){
3685 var rs = this.form.errorReader.read(response);
3688 for(var i = 0, len = rs.records.length; i < len; i++) {
3689 var r = rs.records[i];
3693 if(errors.length < 1){
3697 success : rs.success,
3703 ret = Roo.decode(response.responseText);
3707 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3717 Roo.form.Action.Load = function(form, options){
3718 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3719 this.reader = this.form.reader;
3722 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3727 Roo.Ajax.request(Roo.apply(
3728 this.createCallback(), {
3729 method:this.getMethod(),
3730 url:this.getUrl(false),
3731 params:this.getParams()
3735 success : function(response){
3737 var result = this.processResponse(response);
3738 if(result === true || !result.success || !result.data){
3739 this.failureType = Roo.form.Action.LOAD_FAILURE;
3740 this.form.afterAction(this, false);
3743 this.form.clearInvalid();
3744 this.form.setValues(result.data);
3745 this.form.afterAction(this, true);
3748 handleResponse : function(response){
3749 if(this.form.reader){
3750 var rs = this.form.reader.read(response);
3751 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3753 success : rs.success,
3757 return Roo.decode(response.responseText);
3761 Roo.form.Action.ACTION_TYPES = {
3762 'load' : Roo.form.Action.Load,
3763 'submit' : Roo.form.Action.Submit
3772 * @class Roo.bootstrap.Form
3773 * @extends Roo.bootstrap.Component
3774 * Bootstrap Form class
3775 * @cfg {String} method GET | POST (default POST)
3776 * @cfg {String} labelAlign top | left (default top)
3777 * @cfg {String} align left | right - for navbars
3782 * @param {Object} config The config object
3786 Roo.bootstrap.Form = function(config){
3787 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3790 * @event clientvalidation
3791 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3792 * @param {Form} this
3793 * @param {Boolean} valid true if the form has passed client-side validation
3795 clientvalidation: true,
3797 * @event beforeaction
3798 * Fires before any action is performed. Return false to cancel the action.
3799 * @param {Form} this
3800 * @param {Action} action The action to be performed
3804 * @event actionfailed
3805 * Fires when an action fails.
3806 * @param {Form} this
3807 * @param {Action} action The action that failed
3809 actionfailed : true,
3811 * @event actioncomplete
3812 * Fires when an action is completed.
3813 * @param {Form} this
3814 * @param {Action} action The action that completed
3816 actioncomplete : true
3821 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3824 * @cfg {String} method
3825 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3830 * The URL to use for form actions if one isn't supplied in the action options.
3833 * @cfg {Boolean} fileUpload
3834 * Set to true if this form is a file upload.
3838 * @cfg {Object} baseParams
3839 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3843 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3847 * @cfg {Sting} align (left|right) for navbar forms
3852 activeAction : null,
3855 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3856 * element by passing it or its id or mask the form itself by passing in true.
3859 waitMsgTarget : false,
3864 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3865 * element by passing it or its id or mask the form itself by passing in true.
3869 getAutoCreate : function(){
3873 method : this.method || 'POST',
3874 id : this.id || Roo.id(),
3877 if (this.parent().xtype.match(/^Nav/)) {
3878 cfg.cls = 'navbar-form navbar-' + this.align;
3882 if (this.labelAlign == 'left' ) {
3883 cfg.cls += ' form-horizontal';
3889 initEvents : function()
3891 this.el.on('submit', this.onSubmit, this);
3896 onSubmit : function(e){
3901 * Returns true if client-side validation on the form is successful.
3904 isValid : function(){
3905 var items = this.getItems();
3907 items.each(function(f){
3916 * Returns true if any fields in this form have changed since their original load.
3919 isDirty : function(){
3921 var items = this.getItems();
3922 items.each(function(f){
3932 * Performs a predefined action (submit or load) or custom actions you define on this form.
3933 * @param {String} actionName The name of the action type
3934 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3935 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3936 * accept other config options):
3938 Property Type Description
3939 ---------------- --------------- ----------------------------------------------------------------------------------
3940 url String The url for the action (defaults to the form's url)
3941 method String The form method to use (defaults to the form's method, or POST if not defined)
3942 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3943 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3944 validate the form on the client (defaults to false)
3946 * @return {BasicForm} this
3948 doAction : function(action, options){
3949 if(typeof action == 'string'){
3950 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3952 if(this.fireEvent('beforeaction', this, action) !== false){
3953 this.beforeAction(action);
3954 action.run.defer(100, action);
3960 beforeAction : function(action){
3961 var o = action.options;
3963 // not really supported yet.. ??
3965 //if(this.waitMsgTarget === true){
3966 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3967 //}else if(this.waitMsgTarget){
3968 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3969 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3971 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3977 afterAction : function(action, success){
3978 this.activeAction = null;
3979 var o = action.options;
3981 //if(this.waitMsgTarget === true){
3983 //}else if(this.waitMsgTarget){
3984 // this.waitMsgTarget.unmask();
3986 // Roo.MessageBox.updateProgress(1);
3987 // Roo.MessageBox.hide();
3994 Roo.callback(o.success, o.scope, [this, action]);
3995 this.fireEvent('actioncomplete', this, action);
3999 // failure condition..
4000 // we have a scenario where updates need confirming.
4001 // eg. if a locking scenario exists..
4002 // we look for { errors : { needs_confirm : true }} in the response.
4004 (typeof(action.result) != 'undefined') &&
4005 (typeof(action.result.errors) != 'undefined') &&
4006 (typeof(action.result.errors.needs_confirm) != 'undefined')
4009 Roo.log("not supported yet");
4012 Roo.MessageBox.confirm(
4013 "Change requires confirmation",
4014 action.result.errorMsg,
4019 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4029 Roo.callback(o.failure, o.scope, [this, action]);
4030 // show an error message if no failed handler is set..
4031 if (!this.hasListener('actionfailed')) {
4032 Roo.log("need to add dialog support");
4034 Roo.MessageBox.alert("Error",
4035 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4036 action.result.errorMsg :
4037 "Saving Failed, please check your entries or try again"
4042 this.fireEvent('actionfailed', this, action);
4047 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4048 * @param {String} id The value to search for
4051 findField : function(id){
4052 var items = this.getItems();
4053 var field = items.get(id);
4055 items.each(function(f){
4056 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4063 return field || null;
4066 * Mark fields in this form invalid in bulk.
4067 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4068 * @return {BasicForm} this
4070 markInvalid : function(errors){
4071 if(errors instanceof Array){
4072 for(var i = 0, len = errors.length; i < len; i++){
4073 var fieldError = errors[i];
4074 var f = this.findField(fieldError.id);
4076 f.markInvalid(fieldError.msg);
4082 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4083 field.markInvalid(errors[id]);
4087 //Roo.each(this.childForms || [], function (f) {
4088 // f.markInvalid(errors);
4095 * Set values for fields in this form in bulk.
4096 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4097 * @return {BasicForm} this
4099 setValues : function(values){
4100 if(values instanceof Array){ // array of objects
4101 for(var i = 0, len = values.length; i < len; i++){
4103 var f = this.findField(v.id);
4105 f.setValue(v.value);
4106 if(this.trackResetOnLoad){
4107 f.originalValue = f.getValue();
4111 }else{ // object hash
4114 if(typeof values[id] != 'function' && (field = this.findField(id))){
4116 if (field.setFromData &&
4118 field.displayField &&
4119 // combos' with local stores can
4120 // be queried via setValue()
4121 // to set their value..
4122 (field.store && !field.store.isLocal)
4126 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4127 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4128 field.setFromData(sd);
4131 field.setValue(values[id]);
4135 if(this.trackResetOnLoad){
4136 field.originalValue = field.getValue();
4142 //Roo.each(this.childForms || [], function (f) {
4143 // f.setValues(values);
4150 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4151 * they are returned as an array.
4152 * @param {Boolean} asString
4155 getValues : function(asString){
4156 //if (this.childForms) {
4157 // copy values from the child forms
4158 // Roo.each(this.childForms, function (f) {
4159 // this.setValues(f.getValues());
4165 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4166 if(asString === true){
4169 return Roo.urlDecode(fs);
4173 * Returns the fields in this form as an object with key/value pairs.
4174 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4177 getFieldValues : function(with_hidden)
4179 var items = this.getItems();
4181 items.each(function(f){
4185 var v = f.getValue();
4186 if (f.inputType =='radio') {
4187 if (typeof(ret[f.getName()]) == 'undefined') {
4188 ret[f.getName()] = ''; // empty..
4191 if (!f.el.dom.checked) {
4199 // not sure if this supported any more..
4200 if ((typeof(v) == 'object') && f.getRawValue) {
4201 v = f.getRawValue() ; // dates..
4203 // combo boxes where name != hiddenName...
4204 if (f.name != f.getName()) {
4205 ret[f.name] = f.getRawValue();
4207 ret[f.getName()] = v;
4214 * Clears all invalid messages in this form.
4215 * @return {BasicForm} this
4217 clearInvalid : function(){
4218 var items = this.getItems();
4220 items.each(function(f){
4231 * @return {BasicForm} this
4234 var items = this.getItems();
4235 items.each(function(f){
4239 Roo.each(this.childForms || [], function (f) {
4246 getItems : function()
4248 var r=new Roo.util.MixedCollection(false, function(o){
4249 return o.id || (o.id = Roo.id());
4251 var iter = function(el) {
4258 Roo.each(el.items,function(e) {
4277 * Ext JS Library 1.1.1
4278 * Copyright(c) 2006-2007, Ext JS, LLC.
4280 * Originally Released Under LGPL - original licence link has changed is not relivant.
4283 * <script type="text/javascript">
4286 * @class Roo.form.VTypes
4287 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4290 Roo.form.VTypes = function(){
4291 // closure these in so they are only created once.
4292 var alpha = /^[a-zA-Z_]+$/;
4293 var alphanum = /^[a-zA-Z0-9_]+$/;
4294 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4295 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4297 // All these messages and functions are configurable
4300 * The function used to validate email addresses
4301 * @param {String} value The email address
4303 'email' : function(v){
4304 return email.test(v);
4307 * The error text to display when the email validation function returns false
4310 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4312 * The keystroke filter mask to be applied on email input
4315 'emailMask' : /[a-z0-9_\.\-@]/i,
4318 * The function used to validate URLs
4319 * @param {String} value The URL
4321 'url' : function(v){
4325 * The error text to display when the url validation function returns false
4328 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4331 * The function used to validate alpha values
4332 * @param {String} value The value
4334 'alpha' : function(v){
4335 return alpha.test(v);
4338 * The error text to display when the alpha validation function returns false
4341 'alphaText' : 'This field should only contain letters and _',
4343 * The keystroke filter mask to be applied on alpha input
4346 'alphaMask' : /[a-z_]/i,
4349 * The function used to validate alphanumeric values
4350 * @param {String} value The value
4352 'alphanum' : function(v){
4353 return alphanum.test(v);
4356 * The error text to display when the alphanumeric validation function returns false
4359 'alphanumText' : 'This field should only contain letters, numbers and _',
4361 * The keystroke filter mask to be applied on alphanumeric input
4364 'alphanumMask' : /[a-z0-9_]/i
4374 * @class Roo.bootstrap.Input
4375 * @extends Roo.bootstrap.Component
4376 * Bootstrap Input class
4377 * @cfg {Boolean} disabled is it disabled
4378 * @cfg {String} fieldLabel - the label associated
4379 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4380 * @cfg {String} name name of the input
4381 * @cfg {string} fieldLabel - the label associated
4382 * @cfg {string} inputType - input / file submit ...
4383 * @cfg {string} placeholder - placeholder to put in text.
4384 * @cfg {string} before - input group add on before
4385 * @cfg {string} after - input group add on after
4386 * @cfg {string} size - (lg|sm) or leave empty..
4387 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4388 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4389 * @cfg {Number} md colspan out of 12 for computer-sized screens
4390 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4391 * @cfg {string} value default value of the input
4392 * @cfg {Number} labelWidth set the width of label (0-12)
4393 * @cfg {String} labelAlign (top|left)
4394 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4398 * Create a new Input
4399 * @param {Object} config The config object
4402 Roo.bootstrap.Input = function(config){
4403 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4408 * Fires when this field receives input focus.
4409 * @param {Roo.form.Field} this
4414 * Fires when this field loses input focus.
4415 * @param {Roo.form.Field} this
4420 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4421 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4422 * @param {Roo.form.Field} this
4423 * @param {Roo.EventObject} e The event object
4428 * Fires just before the field blurs if the field value has changed.
4429 * @param {Roo.form.Field} this
4430 * @param {Mixed} newValue The new value
4431 * @param {Mixed} oldValue The original value
4436 * Fires after the field has been marked as invalid.
4437 * @param {Roo.form.Field} this
4438 * @param {String} msg The validation message
4443 * Fires after the field has been validated with no errors.
4444 * @param {Roo.form.Field} this
4449 * Fires after the key up
4450 * @param {Roo.form.Field} this
4451 * @param {Roo.EventObject} e The event Object
4457 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4459 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4460 automatic validation (defaults to "keyup").
4462 validationEvent : "keyup",
4464 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4466 validateOnBlur : true,
4468 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4470 validationDelay : 250,
4472 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4474 focusClass : "x-form-focus", // not needed???
4478 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4480 invalidClass : "has-error",
4483 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4485 selectOnFocus : false,
4488 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4492 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4497 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4499 disableKeyFilter : false,
4502 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4506 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4510 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4512 blankText : "This field is required",
4515 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4519 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4521 maxLength : Number.MAX_VALUE,
4523 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4525 minLengthText : "The minimum length for this field is {0}",
4527 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4529 maxLengthText : "The maximum length for this field is {0}",
4533 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4534 * If available, this function will be called only after the basic validators all return true, and will be passed the
4535 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4539 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4540 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4541 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4545 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4568 parentLabelAlign : function()
4571 while (parent.parent()) {
4572 parent = parent.parent();
4573 if (typeof(parent.labelAlign) !='undefined') {
4574 return parent.labelAlign;
4581 getAutoCreate : function(){
4583 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4589 if(this.inputType != 'hidden'){
4590 cfg.cls = 'form-group' //input-group
4596 type : this.inputType,
4598 cls : 'form-control',
4599 placeholder : this.placeholder || ''
4603 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4604 input.maxLength = this.maxLength;
4607 if (this.disabled) {
4608 input.disabled=true;
4611 if (this.readOnly) {
4612 input.readonly=true;
4616 input.name = this.name;
4619 input.cls += ' input-' + this.size;
4622 ['xs','sm','md','lg'].map(function(size){
4623 if (settings[size]) {
4624 cfg.cls += ' col-' + size + '-' + settings[size];
4628 var inputblock = input;
4630 if (this.before || this.after) {
4633 cls : 'input-group',
4637 inputblock.cn.push({
4639 cls : 'input-group-addon',
4643 inputblock.cn.push(input);
4645 inputblock.cn.push({
4647 cls : 'input-group-addon',
4654 if (align ==='left' && this.fieldLabel.length) {
4655 Roo.log("left and has label");
4661 cls : 'control-label col-sm-' + this.labelWidth,
4662 html : this.fieldLabel
4666 cls : "col-sm-" + (12 - this.labelWidth),
4673 } else if ( this.fieldLabel.length) {
4679 //cls : 'input-group-addon',
4680 html : this.fieldLabel
4690 Roo.log(" no label && no align");
4699 Roo.log('input-parentType: ' + this.parentType);
4701 if (this.parentType === 'Navbar' && this.parent().bar) {
4702 cfg.cls += ' navbar-form';
4710 * return the real input element.
4712 inputEl: function ()
4714 return this.el.select('input.form-control',true).first();
4716 setDisabled : function(v)
4718 var i = this.inputEl().dom;
4720 i.removeAttribute('disabled');
4724 i.setAttribute('disabled','true');
4726 initEvents : function()
4729 this.inputEl().on("keydown" , this.fireKey, this);
4730 this.inputEl().on("focus", this.onFocus, this);
4731 this.inputEl().on("blur", this.onBlur, this);
4733 this.inputEl().relayEvent('keyup', this);
4735 // reference to original value for reset
4736 this.originalValue = this.getValue();
4737 //Roo.form.TextField.superclass.initEvents.call(this);
4738 if(this.validationEvent == 'keyup'){
4739 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4740 this.inputEl().on('keyup', this.filterValidation, this);
4742 else if(this.validationEvent !== false){
4743 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4746 if(this.selectOnFocus){
4747 this.on("focus", this.preFocus, this);
4750 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4751 this.inputEl().on("keypress", this.filterKeys, this);
4754 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4755 this.el.on("click", this.autoSize, this);
4758 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4759 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4763 filterValidation : function(e){
4764 if(!e.isNavKeyPress()){
4765 this.validationTask.delay(this.validationDelay);
4769 * Validates the field value
4770 * @return {Boolean} True if the value is valid, else false
4772 validate : function(){
4773 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4774 if(this.disabled || this.validateValue(this.getRawValue())){
4775 this.clearInvalid();
4783 * Validates a value according to the field's validation rules and marks the field as invalid
4784 * if the validation fails
4785 * @param {Mixed} value The value to validate
4786 * @return {Boolean} True if the value is valid, else false
4788 validateValue : function(value){
4789 if(value.length < 1) { // if it's blank
4790 if(this.allowBlank){
4791 this.clearInvalid();
4794 this.markInvalid(this.blankText);
4798 if(value.length < this.minLength){
4799 this.markInvalid(String.format(this.minLengthText, this.minLength));
4802 if(value.length > this.maxLength){
4803 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4807 var vt = Roo.form.VTypes;
4808 if(!vt[this.vtype](value, this)){
4809 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4813 if(typeof this.validator == "function"){
4814 var msg = this.validator(value);
4816 this.markInvalid(msg);
4820 if(this.regex && !this.regex.test(value)){
4821 this.markInvalid(this.regexText);
4830 fireKey : function(e){
4831 //Roo.log('field ' + e.getKey());
4832 if(e.isNavKeyPress()){
4833 this.fireEvent("specialkey", this, e);
4836 focus : function (selectText){
4838 this.inputEl().focus();
4839 if(selectText === true){
4840 this.inputEl().dom.select();
4846 onFocus : function(){
4847 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4848 // this.el.addClass(this.focusClass);
4851 this.hasFocus = true;
4852 this.startValue = this.getValue();
4853 this.fireEvent("focus", this);
4857 beforeBlur : Roo.emptyFn,
4861 onBlur : function(){
4863 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4864 //this.el.removeClass(this.focusClass);
4866 this.hasFocus = false;
4867 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4870 var v = this.getValue();
4871 if(String(v) !== String(this.startValue)){
4872 this.fireEvent('change', this, v, this.startValue);
4874 this.fireEvent("blur", this);
4878 * Resets the current field value to the originally loaded value and clears any validation messages
4881 this.setValue(this.originalValue);
4882 this.clearInvalid();
4885 * Returns the name of the field
4886 * @return {Mixed} name The name field
4888 getName: function(){
4892 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4893 * @return {Mixed} value The field value
4895 getValue : function(){
4896 return this.inputEl().getValue();
4899 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4900 * @return {Mixed} value The field value
4902 getRawValue : function(){
4903 var v = this.inputEl().getValue();
4909 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4910 * @param {Mixed} value The value to set
4912 setRawValue : function(v){
4913 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4916 selectText : function(start, end){
4917 var v = this.getRawValue();
4919 start = start === undefined ? 0 : start;
4920 end = end === undefined ? v.length : end;
4921 var d = this.inputEl().dom;
4922 if(d.setSelectionRange){
4923 d.setSelectionRange(start, end);
4924 }else if(d.createTextRange){
4925 var range = d.createTextRange();
4926 range.moveStart("character", start);
4927 range.moveEnd("character", v.length-end);
4934 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4935 * @param {Mixed} value The value to set
4937 setValue : function(v){
4940 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4946 processValue : function(value){
4947 if(this.stripCharsRe){
4948 var newValue = value.replace(this.stripCharsRe, '');
4949 if(newValue !== value){
4950 this.setRawValue(newValue);
4957 preFocus : function(){
4959 if(this.selectOnFocus){
4960 this.inputEl().dom.select();
4963 filterKeys : function(e){
4965 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4968 var c = e.getCharCode(), cc = String.fromCharCode(c);
4969 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4972 if(!this.maskRe.test(cc)){
4977 * Clear any invalid styles/messages for this field
4979 clearInvalid : function(){
4981 if(!this.el || this.preventMark){ // not rendered
4984 this.el.removeClass(this.invalidClass);
4986 switch(this.msgTarget){
4988 this.el.dom.qtip = '';
4991 this.el.dom.title = '';
4995 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
5000 this.errorIcon.dom.qtip = '';
5001 this.errorIcon.hide();
5002 this.un('resize', this.alignErrorIcon, this);
5006 var t = Roo.getDom(this.msgTarget);
5008 t.style.display = 'none';
5012 this.fireEvent('valid', this);
5015 * Mark this field as invalid
5016 * @param {String} msg The validation message
5018 markInvalid : function(msg){
5019 if(!this.el || this.preventMark){ // not rendered
5022 this.el.addClass(this.invalidClass);
5024 msg = msg || this.invalidText;
5025 switch(this.msgTarget){
5027 this.el.dom.qtip = msg;
5028 this.el.dom.qclass = 'x-form-invalid-tip';
5029 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5030 Roo.QuickTips.enable();
5034 this.el.dom.title = msg;
5038 var elp = this.el.findParent('.x-form-element', 5, true);
5039 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5040 this.errorEl.setWidth(elp.getWidth(true)-20);
5042 this.errorEl.update(msg);
5043 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5046 if(!this.errorIcon){
5047 var elp = this.el.findParent('.x-form-element', 5, true);
5048 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5050 this.alignErrorIcon();
5051 this.errorIcon.dom.qtip = msg;
5052 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5053 this.errorIcon.show();
5054 this.on('resize', this.alignErrorIcon, this);
5057 var t = Roo.getDom(this.msgTarget);
5059 t.style.display = this.msgDisplay;
5063 this.fireEvent('invalid', this, msg);
5066 SafariOnKeyDown : function(event)
5068 // this is a workaround for a password hang bug on chrome/ webkit.
5070 var isSelectAll = false;
5072 if(this.inputEl().dom.selectionEnd > 0){
5073 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5075 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5076 event.preventDefault();
5081 if(isSelectAll){ // backspace and delete key
5083 event.preventDefault();
5084 // this is very hacky as keydown always get's upper case.
5086 var cc = String.fromCharCode(event.getCharCode());
5087 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5091 adjustWidth : function(tag, w){
5092 tag = tag.toLowerCase();
5093 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5094 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5098 if(tag == 'textarea'){
5101 }else if(Roo.isOpera){
5105 if(tag == 'textarea'){
5124 * @class Roo.bootstrap.TextArea
5125 * @extends Roo.bootstrap.Input
5126 * Bootstrap TextArea class
5127 * @cfg {Number} cols Specifies the visible width of a text area
5128 * @cfg {Number} rows Specifies the visible number of lines in a text area
5129 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5130 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5131 * @cfg {string} html text
5134 * Create a new TextArea
5135 * @param {Object} config The config object
5138 Roo.bootstrap.TextArea = function(config){
5139 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5143 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5153 getAutoCreate : function(){
5155 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5166 value : this.value || '',
5167 html: this.html || '',
5168 cls : 'form-control',
5169 placeholder : this.placeholder || ''
5173 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5174 input.maxLength = this.maxLength;
5178 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5182 input.cols = this.cols;
5185 if (this.readOnly) {
5186 input.readonly = true;
5190 input.name = this.name;
5194 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5198 ['xs','sm','md','lg'].map(function(size){
5199 if (settings[size]) {
5200 cfg.cls += ' col-' + size + '-' + settings[size];
5204 var inputblock = input;
5206 if (this.before || this.after) {
5209 cls : 'input-group',
5213 inputblock.cn.push({
5215 cls : 'input-group-addon',
5219 inputblock.cn.push(input);
5221 inputblock.cn.push({
5223 cls : 'input-group-addon',
5230 if (align ==='left' && this.fieldLabel.length) {
5231 Roo.log("left and has label");
5237 cls : 'control-label col-sm-' + this.labelWidth,
5238 html : this.fieldLabel
5242 cls : "col-sm-" + (12 - this.labelWidth),
5249 } else if ( this.fieldLabel.length) {
5255 //cls : 'input-group-addon',
5256 html : this.fieldLabel
5266 Roo.log(" no label && no align");
5276 if (this.disabled) {
5277 input.disabled=true;
5284 * return the real textarea element.
5286 inputEl: function ()
5288 return this.el.select('textarea.form-control',true).first();
5296 * trigger field - base class for combo..
5301 * @class Roo.bootstrap.TriggerField
5302 * @extends Roo.bootstrap.Input
5303 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5304 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5305 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5306 * for which you can provide a custom implementation. For example:
5308 var trigger = new Roo.bootstrap.TriggerField();
5309 trigger.onTriggerClick = myTriggerFn;
5310 trigger.applyTo('my-field');
5313 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5314 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5315 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5316 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5318 * Create a new TriggerField.
5319 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5320 * to the base TextField)
5322 Roo.bootstrap.TriggerField = function(config){
5323 this.mimicing = false;
5324 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5327 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5329 * @cfg {String} triggerClass A CSS class to apply to the trigger
5332 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5336 /** @cfg {Boolean} grow @hide */
5337 /** @cfg {Number} growMin @hide */
5338 /** @cfg {Number} growMax @hide */
5344 autoSize: Roo.emptyFn,
5351 actionMode : 'wrap',
5355 getAutoCreate : function(){
5357 var parent = this.parent();
5359 var align = this.parentLabelAlign();
5364 cls: 'form-group' //input-group
5371 type : this.inputType,
5372 cls : 'form-control',
5373 autocomplete: 'off',
5374 placeholder : this.placeholder || ''
5378 input.name = this.name;
5381 input.cls += ' input-' + this.size;
5384 if (this.disabled) {
5385 input.disabled=true;
5388 var inputblock = input;
5390 if (this.before || this.after) {
5393 cls : 'input-group',
5397 inputblock.cn.push({
5399 cls : 'input-group-addon',
5403 inputblock.cn.push(input);
5405 inputblock.cn.push({
5407 cls : 'input-group-addon',
5420 cls: 'form-hidden-field'
5428 Roo.log('multiple');
5436 cls: 'form-hidden-field'
5440 cls: 'select2-choices',
5444 cls: 'select2-search-field',
5457 cls: 'select2-container input-group',
5462 cls: 'typeahead typeahead-long dropdown-menu',
5463 style: 'display:none'
5471 cls : 'input-group-addon btn dropdown-toggle',
5479 cls: 'combobox-clear',
5493 combobox.cls += ' select2-container-multi';
5496 if (align ==='left' && this.fieldLabel.length) {
5498 Roo.log("left and has label");
5504 cls : 'control-label col-sm-' + this.labelWidth,
5505 html : this.fieldLabel
5509 cls : "col-sm-" + (12 - this.labelWidth),
5516 } else if ( this.fieldLabel.length) {
5522 //cls : 'input-group-addon',
5523 html : this.fieldLabel
5533 Roo.log(" no label && no align");
5540 ['xs','sm','md','lg'].map(function(size){
5541 if (settings[size]) {
5542 cfg.cls += ' col-' + size + '-' + settings[size];
5553 onResize : function(w, h){
5554 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5555 // if(typeof w == 'number'){
5556 // var x = w - this.trigger.getWidth();
5557 // this.inputEl().setWidth(this.adjustWidth('input', x));
5558 // this.trigger.setStyle('left', x+'px');
5563 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5566 getResizeEl : function(){
5567 return this.inputEl();
5571 getPositionEl : function(){
5572 return this.inputEl();
5576 alignErrorIcon : function(){
5577 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5581 initEvents : function(){
5583 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5584 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5586 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5587 if(this.hideTrigger){
5588 this.trigger.setDisplayed(false);
5590 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5594 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5597 //this.trigger.addClassOnOver('x-form-trigger-over');
5598 //this.trigger.addClassOnClick('x-form-trigger-click');
5601 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5606 initTrigger : function(){
5611 onDestroy : function(){
5613 this.trigger.removeAllListeners();
5614 // this.trigger.remove();
5617 // this.wrap.remove();
5619 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5623 onFocus : function(){
5624 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5627 this.wrap.addClass('x-trigger-wrap-focus');
5628 this.mimicing = true;
5629 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5630 if(this.monitorTab){
5631 this.el.on("keydown", this.checkTab, this);
5638 checkTab : function(e){
5639 if(e.getKey() == e.TAB){
5645 onBlur : function(){
5650 mimicBlur : function(e, t){
5652 if(!this.wrap.contains(t) && this.validateBlur()){
5659 triggerBlur : function(){
5660 this.mimicing = false;
5661 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5662 if(this.monitorTab){
5663 this.el.un("keydown", this.checkTab, this);
5665 //this.wrap.removeClass('x-trigger-wrap-focus');
5666 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5670 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5671 validateBlur : function(e, t){
5676 onDisable : function(){
5677 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5679 // this.wrap.addClass('x-item-disabled');
5684 onEnable : function(){
5685 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5687 // this.el.removeClass('x-item-disabled');
5692 onShow : function(){
5693 var ae = this.getActionEl();
5696 ae.dom.style.display = '';
5697 ae.dom.style.visibility = 'visible';
5703 onHide : function(){
5704 var ae = this.getActionEl();
5705 ae.dom.style.display = 'none';
5709 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5710 * by an implementing function.
5712 * @param {EventObject} e
5714 onTriggerClick : Roo.emptyFn
5718 * Ext JS Library 1.1.1
5719 * Copyright(c) 2006-2007, Ext JS, LLC.
5721 * Originally Released Under LGPL - original licence link has changed is not relivant.
5724 * <script type="text/javascript">
5729 * @class Roo.data.SortTypes
5731 * Defines the default sorting (casting?) comparison functions used when sorting data.
5733 Roo.data.SortTypes = {
5735 * Default sort that does nothing
5736 * @param {Mixed} s The value being converted
5737 * @return {Mixed} The comparison value
5744 * The regular expression used to strip tags
5748 stripTagsRE : /<\/?[^>]+>/gi,
5751 * Strips all HTML tags to sort on text only
5752 * @param {Mixed} s The value being converted
5753 * @return {String} The comparison value
5755 asText : function(s){
5756 return String(s).replace(this.stripTagsRE, "");
5760 * Strips all HTML tags to sort on text only - Case insensitive
5761 * @param {Mixed} s The value being converted
5762 * @return {String} The comparison value
5764 asUCText : function(s){
5765 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5769 * Case insensitive string
5770 * @param {Mixed} s The value being converted
5771 * @return {String} The comparison value
5773 asUCString : function(s) {
5774 return String(s).toUpperCase();
5779 * @param {Mixed} s The value being converted
5780 * @return {Number} The comparison value
5782 asDate : function(s) {
5786 if(s instanceof Date){
5789 return Date.parse(String(s));
5794 * @param {Mixed} s The value being converted
5795 * @return {Float} The comparison value
5797 asFloat : function(s) {
5798 var val = parseFloat(String(s).replace(/,/g, ""));
5799 if(isNaN(val)) val = 0;
5805 * @param {Mixed} s The value being converted
5806 * @return {Number} The comparison value
5808 asInt : function(s) {
5809 var val = parseInt(String(s).replace(/,/g, ""));
5810 if(isNaN(val)) val = 0;
5815 * Ext JS Library 1.1.1
5816 * Copyright(c) 2006-2007, Ext JS, LLC.
5818 * Originally Released Under LGPL - original licence link has changed is not relivant.
5821 * <script type="text/javascript">
5825 * @class Roo.data.Record
5826 * Instances of this class encapsulate both record <em>definition</em> information, and record
5827 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5828 * to access Records cached in an {@link Roo.data.Store} object.<br>
5830 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5831 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5834 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5836 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5837 * {@link #create}. The parameters are the same.
5838 * @param {Array} data An associative Array of data values keyed by the field name.
5839 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5840 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5841 * not specified an integer id is generated.
5843 Roo.data.Record = function(data, id){
5844 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5849 * Generate a constructor for a specific record layout.
5850 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5851 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5852 * Each field definition object may contain the following properties: <ul>
5853 * <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,
5854 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5855 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5856 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5857 * is being used, then this is a string containing the javascript expression to reference the data relative to
5858 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5859 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5860 * this may be omitted.</p></li>
5861 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5862 * <ul><li>auto (Default, implies no conversion)</li>
5867 * <li>date</li></ul></p></li>
5868 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5869 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5870 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5871 * by the Reader into an object that will be stored in the Record. It is passed the
5872 * following parameters:<ul>
5873 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5875 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5877 * <br>usage:<br><pre><code>
5878 var TopicRecord = Roo.data.Record.create(
5879 {name: 'title', mapping: 'topic_title'},
5880 {name: 'author', mapping: 'username'},
5881 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5882 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5883 {name: 'lastPoster', mapping: 'user2'},
5884 {name: 'excerpt', mapping: 'post_text'}
5887 var myNewRecord = new TopicRecord({
5888 title: 'Do my job please',
5891 lastPost: new Date(),
5892 lastPoster: 'Animal',
5893 excerpt: 'No way dude!'
5895 myStore.add(myNewRecord);
5900 Roo.data.Record.create = function(o){
5902 f.superclass.constructor.apply(this, arguments);
5904 Roo.extend(f, Roo.data.Record);
5905 var p = f.prototype;
5906 p.fields = new Roo.util.MixedCollection(false, function(field){
5909 for(var i = 0, len = o.length; i < len; i++){
5910 p.fields.add(new Roo.data.Field(o[i]));
5912 f.getField = function(name){
5913 return p.fields.get(name);
5918 Roo.data.Record.AUTO_ID = 1000;
5919 Roo.data.Record.EDIT = 'edit';
5920 Roo.data.Record.REJECT = 'reject';
5921 Roo.data.Record.COMMIT = 'commit';
5923 Roo.data.Record.prototype = {
5925 * Readonly flag - true if this record has been modified.
5934 join : function(store){
5939 * Set the named field to the specified value.
5940 * @param {String} name The name of the field to set.
5941 * @param {Object} value The value to set the field to.
5943 set : function(name, value){
5944 if(this.data[name] == value){
5951 if(typeof this.modified[name] == 'undefined'){
5952 this.modified[name] = this.data[name];
5954 this.data[name] = value;
5955 if(!this.editing && this.store){
5956 this.store.afterEdit(this);
5961 * Get the value of the named field.
5962 * @param {String} name The name of the field to get the value of.
5963 * @return {Object} The value of the field.
5965 get : function(name){
5966 return this.data[name];
5970 beginEdit : function(){
5971 this.editing = true;
5976 cancelEdit : function(){
5977 this.editing = false;
5978 delete this.modified;
5982 endEdit : function(){
5983 this.editing = false;
5984 if(this.dirty && this.store){
5985 this.store.afterEdit(this);
5990 * Usually called by the {@link Roo.data.Store} which owns the Record.
5991 * Rejects all changes made to the Record since either creation, or the last commit operation.
5992 * Modified fields are reverted to their original values.
5994 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5995 * of reject operations.
5997 reject : function(){
5998 var m = this.modified;
6000 if(typeof m[n] != "function"){
6001 this.data[n] = m[n];
6005 delete this.modified;
6006 this.editing = false;
6008 this.store.afterReject(this);
6013 * Usually called by the {@link Roo.data.Store} which owns the Record.
6014 * Commits all changes made to the Record since either creation, or the last commit operation.
6016 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6017 * of commit operations.
6019 commit : function(){
6021 delete this.modified;
6022 this.editing = false;
6024 this.store.afterCommit(this);
6029 hasError : function(){
6030 return this.error != null;
6034 clearError : function(){
6039 * Creates a copy of this record.
6040 * @param {String} id (optional) A new record id if you don't want to use this record's id
6043 copy : function(newId) {
6044 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6048 * Ext JS Library 1.1.1
6049 * Copyright(c) 2006-2007, Ext JS, LLC.
6051 * Originally Released Under LGPL - original licence link has changed is not relivant.
6054 * <script type="text/javascript">
6060 * @class Roo.data.Store
6061 * @extends Roo.util.Observable
6062 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6063 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6065 * 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
6066 * has no knowledge of the format of the data returned by the Proxy.<br>
6068 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6069 * instances from the data object. These records are cached and made available through accessor functions.
6071 * Creates a new Store.
6072 * @param {Object} config A config object containing the objects needed for the Store to access data,
6073 * and read the data into Records.
6075 Roo.data.Store = function(config){
6076 this.data = new Roo.util.MixedCollection(false);
6077 this.data.getKey = function(o){
6080 this.baseParams = {};
6087 "multisort" : "_multisort"
6090 if(config && config.data){
6091 this.inlineData = config.data;
6095 Roo.apply(this, config);
6097 if(this.reader){ // reader passed
6098 this.reader = Roo.factory(this.reader, Roo.data);
6099 this.reader.xmodule = this.xmodule || false;
6100 if(!this.recordType){
6101 this.recordType = this.reader.recordType;
6103 if(this.reader.onMetaChange){
6104 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6108 if(this.recordType){
6109 this.fields = this.recordType.prototype.fields;
6115 * @event datachanged
6116 * Fires when the data cache has changed, and a widget which is using this Store
6117 * as a Record cache should refresh its view.
6118 * @param {Store} this
6123 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6124 * @param {Store} this
6125 * @param {Object} meta The JSON metadata
6130 * Fires when Records have been added to the Store
6131 * @param {Store} this
6132 * @param {Roo.data.Record[]} records The array of Records added
6133 * @param {Number} index The index at which the record(s) were added
6138 * Fires when a Record has been removed from the Store
6139 * @param {Store} this
6140 * @param {Roo.data.Record} record The Record that was removed
6141 * @param {Number} index The index at which the record was removed
6146 * Fires when a Record has been updated
6147 * @param {Store} this
6148 * @param {Roo.data.Record} record The Record that was updated
6149 * @param {String} operation The update operation being performed. Value may be one of:
6151 Roo.data.Record.EDIT
6152 Roo.data.Record.REJECT
6153 Roo.data.Record.COMMIT
6159 * Fires when the data cache has been cleared.
6160 * @param {Store} this
6165 * Fires before a request is made for a new data object. If the beforeload handler returns false
6166 * the load action will be canceled.
6167 * @param {Store} this
6168 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6172 * @event beforeloadadd
6173 * Fires after a new set of Records has been loaded.
6174 * @param {Store} this
6175 * @param {Roo.data.Record[]} records The Records that were loaded
6176 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6178 beforeloadadd : true,
6181 * Fires after a new set of Records has been loaded, before they are added to the store.
6182 * @param {Store} this
6183 * @param {Roo.data.Record[]} records The Records that were loaded
6184 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6185 * @params {Object} return from reader
6189 * @event loadexception
6190 * Fires if an exception occurs in the Proxy during loading.
6191 * Called with the signature of the Proxy's "loadexception" event.
6192 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6195 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6196 * @param {Object} load options
6197 * @param {Object} jsonData from your request (normally this contains the Exception)
6199 loadexception : true
6203 this.proxy = Roo.factory(this.proxy, Roo.data);
6204 this.proxy.xmodule = this.xmodule || false;
6205 this.relayEvents(this.proxy, ["loadexception"]);
6207 this.sortToggle = {};
6208 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6210 Roo.data.Store.superclass.constructor.call(this);
6212 if(this.inlineData){
6213 this.loadData(this.inlineData);
6214 delete this.inlineData;
6218 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6220 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6221 * without a remote query - used by combo/forms at present.
6225 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6228 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6231 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6232 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6235 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6236 * on any HTTP request
6239 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6242 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6246 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6247 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6252 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6253 * loaded or when a record is removed. (defaults to false).
6255 pruneModifiedRecords : false,
6261 * Add Records to the Store and fires the add event.
6262 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6264 add : function(records){
6265 records = [].concat(records);
6266 for(var i = 0, len = records.length; i < len; i++){
6267 records[i].join(this);
6269 var index = this.data.length;
6270 this.data.addAll(records);
6271 this.fireEvent("add", this, records, index);
6275 * Remove a Record from the Store and fires the remove event.
6276 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6278 remove : function(record){
6279 var index = this.data.indexOf(record);
6280 this.data.removeAt(index);
6281 if(this.pruneModifiedRecords){
6282 this.modified.remove(record);
6284 this.fireEvent("remove", this, record, index);
6288 * Remove all Records from the Store and fires the clear event.
6290 removeAll : function(){
6292 if(this.pruneModifiedRecords){
6295 this.fireEvent("clear", this);
6299 * Inserts Records to the Store at the given index and fires the add event.
6300 * @param {Number} index The start index at which to insert the passed Records.
6301 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6303 insert : function(index, records){
6304 records = [].concat(records);
6305 for(var i = 0, len = records.length; i < len; i++){
6306 this.data.insert(index, records[i]);
6307 records[i].join(this);
6309 this.fireEvent("add", this, records, index);
6313 * Get the index within the cache of the passed Record.
6314 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6315 * @return {Number} The index of the passed Record. Returns -1 if not found.
6317 indexOf : function(record){
6318 return this.data.indexOf(record);
6322 * Get the index within the cache of the Record with the passed id.
6323 * @param {String} id The id of the Record to find.
6324 * @return {Number} The index of the Record. Returns -1 if not found.
6326 indexOfId : function(id){
6327 return this.data.indexOfKey(id);
6331 * Get the Record with the specified id.
6332 * @param {String} id The id of the Record to find.
6333 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6335 getById : function(id){
6336 return this.data.key(id);
6340 * Get the Record at the specified index.
6341 * @param {Number} index The index of the Record to find.
6342 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6344 getAt : function(index){
6345 return this.data.itemAt(index);
6349 * Returns a range of Records between specified indices.
6350 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6351 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6352 * @return {Roo.data.Record[]} An array of Records
6354 getRange : function(start, end){
6355 return this.data.getRange(start, end);
6359 storeOptions : function(o){
6360 o = Roo.apply({}, o);
6363 this.lastOptions = o;
6367 * Loads the Record cache from the configured Proxy using the configured Reader.
6369 * If using remote paging, then the first load call must specify the <em>start</em>
6370 * and <em>limit</em> properties in the options.params property to establish the initial
6371 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6373 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6374 * and this call will return before the new data has been loaded. Perform any post-processing
6375 * in a callback function, or in a "load" event handler.</strong>
6377 * @param {Object} options An object containing properties which control loading options:<ul>
6378 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6379 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6380 * passed the following arguments:<ul>
6381 * <li>r : Roo.data.Record[]</li>
6382 * <li>options: Options object from the load call</li>
6383 * <li>success: Boolean success indicator</li></ul></li>
6384 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6385 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6388 load : function(options){
6389 options = options || {};
6390 if(this.fireEvent("beforeload", this, options) !== false){
6391 this.storeOptions(options);
6392 var p = Roo.apply(options.params || {}, this.baseParams);
6393 // if meta was not loaded from remote source.. try requesting it.
6394 if (!this.reader.metaFromRemote) {
6397 if(this.sortInfo && this.remoteSort){
6398 var pn = this.paramNames;
6399 p[pn["sort"]] = this.sortInfo.field;
6400 p[pn["dir"]] = this.sortInfo.direction;
6402 if (this.multiSort) {
6403 var pn = this.paramNames;
6404 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6407 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6412 * Reloads the Record cache from the configured Proxy using the configured Reader and
6413 * the options from the last load operation performed.
6414 * @param {Object} options (optional) An object containing properties which may override the options
6415 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6416 * the most recently used options are reused).
6418 reload : function(options){
6419 this.load(Roo.applyIf(options||{}, this.lastOptions));
6423 // Called as a callback by the Reader during a load operation.
6424 loadRecords : function(o, options, success){
6425 if(!o || success === false){
6426 if(success !== false){
6427 this.fireEvent("load", this, [], options, o);
6429 if(options.callback){
6430 options.callback.call(options.scope || this, [], options, false);
6434 // if data returned failure - throw an exception.
6435 if (o.success === false) {
6436 // show a message if no listener is registered.
6437 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6438 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6440 // loadmask wil be hooked into this..
6441 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6444 var r = o.records, t = o.totalRecords || r.length;
6446 this.fireEvent("beforeloadadd", this, r, options, o);
6448 if(!options || options.add !== true){
6449 if(this.pruneModifiedRecords){
6452 for(var i = 0, len = r.length; i < len; i++){
6456 this.data = this.snapshot;
6457 delete this.snapshot;
6460 this.data.addAll(r);
6461 this.totalLength = t;
6463 this.fireEvent("datachanged", this);
6465 this.totalLength = Math.max(t, this.data.length+r.length);
6468 this.fireEvent("load", this, r, options, o);
6469 if(options.callback){
6470 options.callback.call(options.scope || this, r, options, true);
6476 * Loads data from a passed data block. A Reader which understands the format of the data
6477 * must have been configured in the constructor.
6478 * @param {Object} data The data block from which to read the Records. The format of the data expected
6479 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6480 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6482 loadData : function(o, append){
6483 var r = this.reader.readRecords(o);
6484 this.loadRecords(r, {add: append}, true);
6488 * Gets the number of cached records.
6490 * <em>If using paging, this may not be the total size of the dataset. If the data object
6491 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6492 * the data set size</em>
6494 getCount : function(){
6495 return this.data.length || 0;
6499 * Gets the total number of records in the dataset as returned by the server.
6501 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6502 * the dataset size</em>
6504 getTotalCount : function(){
6505 return this.totalLength || 0;
6509 * Returns the sort state of the Store as an object with two properties:
6511 field {String} The name of the field by which the Records are sorted
6512 direction {String} The sort order, "ASC" or "DESC"
6515 getSortState : function(){
6516 return this.sortInfo;
6520 applySort : function(){
6521 if(this.sortInfo && !this.remoteSort){
6522 var s = this.sortInfo, f = s.field;
6523 var st = this.fields.get(f).sortType;
6524 var fn = function(r1, r2){
6525 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6526 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6528 this.data.sort(s.direction, fn);
6529 if(this.snapshot && this.snapshot != this.data){
6530 this.snapshot.sort(s.direction, fn);
6536 * Sets the default sort column and order to be used by the next load operation.
6537 * @param {String} fieldName The name of the field to sort by.
6538 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6540 setDefaultSort : function(field, dir){
6541 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6546 * If remote sorting is used, the sort is performed on the server, and the cache is
6547 * reloaded. If local sorting is used, the cache is sorted internally.
6548 * @param {String} fieldName The name of the field to sort by.
6549 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6551 sort : function(fieldName, dir){
6552 var f = this.fields.get(fieldName);
6554 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6556 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6557 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6562 this.sortToggle[f.name] = dir;
6563 this.sortInfo = {field: f.name, direction: dir};
6564 if(!this.remoteSort){
6566 this.fireEvent("datachanged", this);
6568 this.load(this.lastOptions);
6573 * Calls the specified function for each of the Records in the cache.
6574 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6575 * Returning <em>false</em> aborts and exits the iteration.
6576 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6578 each : function(fn, scope){
6579 this.data.each(fn, scope);
6583 * Gets all records modified since the last commit. Modified records are persisted across load operations
6584 * (e.g., during paging).
6585 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6587 getModifiedRecords : function(){
6588 return this.modified;
6592 createFilterFn : function(property, value, anyMatch){
6593 if(!value.exec){ // not a regex
6594 value = String(value);
6595 if(value.length == 0){
6598 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6601 return value.test(r.data[property]);
6606 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6607 * @param {String} property A field on your records
6608 * @param {Number} start The record index to start at (defaults to 0)
6609 * @param {Number} end The last record index to include (defaults to length - 1)
6610 * @return {Number} The sum
6612 sum : function(property, start, end){
6613 var rs = this.data.items, v = 0;
6615 end = (end || end === 0) ? end : rs.length-1;
6617 for(var i = start; i <= end; i++){
6618 v += (rs[i].data[property] || 0);
6624 * Filter the records by a specified property.
6625 * @param {String} field A field on your records
6626 * @param {String/RegExp} value Either a string that the field
6627 * should start with or a RegExp to test against the field
6628 * @param {Boolean} anyMatch True to match any part not just the beginning
6630 filter : function(property, value, anyMatch){
6631 var fn = this.createFilterFn(property, value, anyMatch);
6632 return fn ? this.filterBy(fn) : this.clearFilter();
6636 * Filter by a function. The specified function will be called with each
6637 * record in this data source. If the function returns true the record is included,
6638 * otherwise it is filtered.
6639 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6640 * @param {Object} scope (optional) The scope of the function (defaults to this)
6642 filterBy : function(fn, scope){
6643 this.snapshot = this.snapshot || this.data;
6644 this.data = this.queryBy(fn, scope||this);
6645 this.fireEvent("datachanged", this);
6649 * Query the records by a specified property.
6650 * @param {String} field A field on your records
6651 * @param {String/RegExp} value Either a string that the field
6652 * should start with or a RegExp to test against the field
6653 * @param {Boolean} anyMatch True to match any part not just the beginning
6654 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6656 query : function(property, value, anyMatch){
6657 var fn = this.createFilterFn(property, value, anyMatch);
6658 return fn ? this.queryBy(fn) : this.data.clone();
6662 * Query by a function. The specified function will be called with each
6663 * record in this data source. If the function returns true the record is included
6665 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6666 * @param {Object} scope (optional) The scope of the function (defaults to this)
6667 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6669 queryBy : function(fn, scope){
6670 var data = this.snapshot || this.data;
6671 return data.filterBy(fn, scope||this);
6675 * Collects unique values for a particular dataIndex from this store.
6676 * @param {String} dataIndex The property to collect
6677 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6678 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6679 * @return {Array} An array of the unique values
6681 collect : function(dataIndex, allowNull, bypassFilter){
6682 var d = (bypassFilter === true && this.snapshot) ?
6683 this.snapshot.items : this.data.items;
6684 var v, sv, r = [], l = {};
6685 for(var i = 0, len = d.length; i < len; i++){
6686 v = d[i].data[dataIndex];
6688 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6697 * Revert to a view of the Record cache with no filtering applied.
6698 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6700 clearFilter : function(suppressEvent){
6701 if(this.snapshot && this.snapshot != this.data){
6702 this.data = this.snapshot;
6703 delete this.snapshot;
6704 if(suppressEvent !== true){
6705 this.fireEvent("datachanged", this);
6711 afterEdit : function(record){
6712 if(this.modified.indexOf(record) == -1){
6713 this.modified.push(record);
6715 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6719 afterReject : function(record){
6720 this.modified.remove(record);
6721 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6725 afterCommit : function(record){
6726 this.modified.remove(record);
6727 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6731 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6732 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6734 commitChanges : function(){
6735 var m = this.modified.slice(0);
6737 for(var i = 0, len = m.length; i < len; i++){
6743 * Cancel outstanding changes on all changed records.
6745 rejectChanges : function(){
6746 var m = this.modified.slice(0);
6748 for(var i = 0, len = m.length; i < len; i++){
6753 onMetaChange : function(meta, rtype, o){
6754 this.recordType = rtype;
6755 this.fields = rtype.prototype.fields;
6756 delete this.snapshot;
6757 this.sortInfo = meta.sortInfo || this.sortInfo;
6759 this.fireEvent('metachange', this, this.reader.meta);
6762 moveIndex : function(data, type)
6764 var index = this.indexOf(data);
6766 var newIndex = index + type;
6770 this.insert(newIndex, data);
6775 * Ext JS Library 1.1.1
6776 * Copyright(c) 2006-2007, Ext JS, LLC.
6778 * Originally Released Under LGPL - original licence link has changed is not relivant.
6781 * <script type="text/javascript">
6785 * @class Roo.data.SimpleStore
6786 * @extends Roo.data.Store
6787 * Small helper class to make creating Stores from Array data easier.
6788 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6789 * @cfg {Array} fields An array of field definition objects, or field name strings.
6790 * @cfg {Array} data The multi-dimensional array of data
6792 * @param {Object} config
6794 Roo.data.SimpleStore = function(config){
6795 Roo.data.SimpleStore.superclass.constructor.call(this, {
6797 reader: new Roo.data.ArrayReader({
6800 Roo.data.Record.create(config.fields)
6802 proxy : new Roo.data.MemoryProxy(config.data)
6806 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6808 * Ext JS Library 1.1.1
6809 * Copyright(c) 2006-2007, Ext JS, LLC.
6811 * Originally Released Under LGPL - original licence link has changed is not relivant.
6814 * <script type="text/javascript">
6819 * @extends Roo.data.Store
6820 * @class Roo.data.JsonStore
6821 * Small helper class to make creating Stores for JSON data easier. <br/>
6823 var store = new Roo.data.JsonStore({
6824 url: 'get-images.php',
6826 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6829 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6830 * JsonReader and HttpProxy (unless inline data is provided).</b>
6831 * @cfg {Array} fields An array of field definition objects, or field name strings.
6833 * @param {Object} config
6835 Roo.data.JsonStore = function(c){
6836 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6837 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6838 reader: new Roo.data.JsonReader(c, c.fields)
6841 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6843 * Ext JS Library 1.1.1
6844 * Copyright(c) 2006-2007, Ext JS, LLC.
6846 * Originally Released Under LGPL - original licence link has changed is not relivant.
6849 * <script type="text/javascript">
6853 Roo.data.Field = function(config){
6854 if(typeof config == "string"){
6855 config = {name: config};
6857 Roo.apply(this, config);
6863 var st = Roo.data.SortTypes;
6864 // named sortTypes are supported, here we look them up
6865 if(typeof this.sortType == "string"){
6866 this.sortType = st[this.sortType];
6869 // set default sortType for strings and dates
6873 this.sortType = st.asUCString;
6876 this.sortType = st.asDate;
6879 this.sortType = st.none;
6884 var stripRe = /[\$,%]/g;
6886 // prebuilt conversion function for this field, instead of
6887 // switching every time we're reading a value
6889 var cv, dateFormat = this.dateFormat;
6894 cv = function(v){ return v; };
6897 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6901 return v !== undefined && v !== null && v !== '' ?
6902 parseInt(String(v).replace(stripRe, ""), 10) : '';
6907 return v !== undefined && v !== null && v !== '' ?
6908 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6913 cv = function(v){ return v === true || v === "true" || v == 1; };
6920 if(v instanceof Date){
6924 if(dateFormat == "timestamp"){
6925 return new Date(v*1000);
6927 return Date.parseDate(v, dateFormat);
6929 var parsed = Date.parse(v);
6930 return parsed ? new Date(parsed) : null;
6939 Roo.data.Field.prototype = {
6947 * Ext JS Library 1.1.1
6948 * Copyright(c) 2006-2007, Ext JS, LLC.
6950 * Originally Released Under LGPL - original licence link has changed is not relivant.
6953 * <script type="text/javascript">
6956 // Base class for reading structured data from a data source. This class is intended to be
6957 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6960 * @class Roo.data.DataReader
6961 * Base class for reading structured data from a data source. This class is intended to be
6962 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6965 Roo.data.DataReader = function(meta, recordType){
6969 this.recordType = recordType instanceof Array ?
6970 Roo.data.Record.create(recordType) : recordType;
6973 Roo.data.DataReader.prototype = {
6975 * Create an empty record
6976 * @param {Object} data (optional) - overlay some values
6977 * @return {Roo.data.Record} record created.
6979 newRow : function(d) {
6981 this.recordType.prototype.fields.each(function(c) {
6983 case 'int' : da[c.name] = 0; break;
6984 case 'date' : da[c.name] = new Date(); break;
6985 case 'float' : da[c.name] = 0.0; break;
6986 case 'boolean' : da[c.name] = false; break;
6987 default : da[c.name] = ""; break;
6991 return new this.recordType(Roo.apply(da, d));
6996 * Ext JS Library 1.1.1
6997 * Copyright(c) 2006-2007, Ext JS, LLC.
6999 * Originally Released Under LGPL - original licence link has changed is not relivant.
7002 * <script type="text/javascript">
7006 * @class Roo.data.DataProxy
7007 * @extends Roo.data.Observable
7008 * This class is an abstract base class for implementations which provide retrieval of
7009 * unformatted data objects.<br>
7011 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7012 * (of the appropriate type which knows how to parse the data object) to provide a block of
7013 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7015 * Custom implementations must implement the load method as described in
7016 * {@link Roo.data.HttpProxy#load}.
7018 Roo.data.DataProxy = function(){
7022 * Fires before a network request is made to retrieve a data object.
7023 * @param {Object} This DataProxy object.
7024 * @param {Object} params The params parameter to the load function.
7029 * Fires before the load method's callback is called.
7030 * @param {Object} This DataProxy object.
7031 * @param {Object} o The data object.
7032 * @param {Object} arg The callback argument object passed to the load function.
7036 * @event loadexception
7037 * Fires if an Exception occurs during data retrieval.
7038 * @param {Object} This DataProxy object.
7039 * @param {Object} o The data object.
7040 * @param {Object} arg The callback argument object passed to the load function.
7041 * @param {Object} e The Exception.
7043 loadexception : true
7045 Roo.data.DataProxy.superclass.constructor.call(this);
7048 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7051 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7055 * Ext JS Library 1.1.1
7056 * Copyright(c) 2006-2007, Ext JS, LLC.
7058 * Originally Released Under LGPL - original licence link has changed is not relivant.
7061 * <script type="text/javascript">
7064 * @class Roo.data.MemoryProxy
7065 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7066 * to the Reader when its load method is called.
7068 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7070 Roo.data.MemoryProxy = function(data){
7074 Roo.data.MemoryProxy.superclass.constructor.call(this);
7078 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7080 * Load data from the requested source (in this case an in-memory
7081 * data object passed to the constructor), read the data object into
7082 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7083 * process that block using the passed callback.
7084 * @param {Object} params This parameter is not used by the MemoryProxy class.
7085 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7086 * object into a block of Roo.data.Records.
7087 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7088 * The function must be passed <ul>
7089 * <li>The Record block object</li>
7090 * <li>The "arg" argument from the load function</li>
7091 * <li>A boolean success indicator</li>
7093 * @param {Object} scope The scope in which to call the callback
7094 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7096 load : function(params, reader, callback, scope, arg){
7097 params = params || {};
7100 result = reader.readRecords(this.data);
7102 this.fireEvent("loadexception", this, arg, null, e);
7103 callback.call(scope, null, arg, false);
7106 callback.call(scope, result, arg, true);
7110 update : function(params, records){
7115 * Ext JS Library 1.1.1
7116 * Copyright(c) 2006-2007, Ext JS, LLC.
7118 * Originally Released Under LGPL - original licence link has changed is not relivant.
7121 * <script type="text/javascript">
7124 * @class Roo.data.HttpProxy
7125 * @extends Roo.data.DataProxy
7126 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7127 * configured to reference a certain URL.<br><br>
7129 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7130 * from which the running page was served.<br><br>
7132 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7134 * Be aware that to enable the browser to parse an XML document, the server must set
7135 * the Content-Type header in the HTTP response to "text/xml".
7137 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7138 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7139 * will be used to make the request.
7141 Roo.data.HttpProxy = function(conn){
7142 Roo.data.HttpProxy.superclass.constructor.call(this);
7143 // is conn a conn config or a real conn?
7145 this.useAjax = !conn || !conn.events;
7149 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7150 // thse are take from connection...
7153 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7156 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7157 * extra parameters to each request made by this object. (defaults to undefined)
7160 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7161 * to each request made by this object. (defaults to undefined)
7164 * @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)
7167 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7170 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7176 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7180 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7181 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7182 * a finer-grained basis than the DataProxy events.
7184 getConnection : function(){
7185 return this.useAjax ? Roo.Ajax : this.conn;
7189 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7190 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7191 * process that block using the passed callback.
7192 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7193 * for the request to the remote server.
7194 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7195 * object into a block of Roo.data.Records.
7196 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7197 * The function must be passed <ul>
7198 * <li>The Record block object</li>
7199 * <li>The "arg" argument from the load function</li>
7200 * <li>A boolean success indicator</li>
7202 * @param {Object} scope The scope in which to call the callback
7203 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7205 load : function(params, reader, callback, scope, arg){
7206 if(this.fireEvent("beforeload", this, params) !== false){
7208 params : params || {},
7210 callback : callback,
7215 callback : this.loadResponse,
7219 Roo.applyIf(o, this.conn);
7220 if(this.activeRequest){
7221 Roo.Ajax.abort(this.activeRequest);
7223 this.activeRequest = Roo.Ajax.request(o);
7225 this.conn.request(o);
7228 callback.call(scope||this, null, arg, false);
7233 loadResponse : function(o, success, response){
7234 delete this.activeRequest;
7236 this.fireEvent("loadexception", this, o, response);
7237 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7242 result = o.reader.read(response);
7244 this.fireEvent("loadexception", this, o, response, e);
7245 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7249 this.fireEvent("load", this, o, o.request.arg);
7250 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7254 update : function(dataSet){
7259 updateResponse : function(dataSet){
7264 * Ext JS Library 1.1.1
7265 * Copyright(c) 2006-2007, Ext JS, LLC.
7267 * Originally Released Under LGPL - original licence link has changed is not relivant.
7270 * <script type="text/javascript">
7274 * @class Roo.data.ScriptTagProxy
7275 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7276 * other than the originating domain of the running page.<br><br>
7278 * <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
7279 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7281 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7282 * source code that is used as the source inside a <script> tag.<br><br>
7284 * In order for the browser to process the returned data, the server must wrap the data object
7285 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7286 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7287 * depending on whether the callback name was passed:
7290 boolean scriptTag = false;
7291 String cb = request.getParameter("callback");
7294 response.setContentType("text/javascript");
7296 response.setContentType("application/x-json");
7298 Writer out = response.getWriter();
7300 out.write(cb + "(");
7302 out.print(dataBlock.toJsonString());
7309 * @param {Object} config A configuration object.
7311 Roo.data.ScriptTagProxy = function(config){
7312 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7313 Roo.apply(this, config);
7314 this.head = document.getElementsByTagName("head")[0];
7317 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7319 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7321 * @cfg {String} url The URL from which to request the data object.
7324 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7328 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7329 * the server the name of the callback function set up by the load call to process the returned data object.
7330 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7331 * javascript output which calls this named function passing the data object as its only parameter.
7333 callbackParam : "callback",
7335 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7336 * name to the request.
7341 * Load data from the configured URL, read the data object into
7342 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7343 * process that block using the passed callback.
7344 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7345 * for the request to the remote server.
7346 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7347 * object into a block of Roo.data.Records.
7348 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7349 * The function must be passed <ul>
7350 * <li>The Record block object</li>
7351 * <li>The "arg" argument from the load function</li>
7352 * <li>A boolean success indicator</li>
7354 * @param {Object} scope The scope in which to call the callback
7355 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7357 load : function(params, reader, callback, scope, arg){
7358 if(this.fireEvent("beforeload", this, params) !== false){
7360 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7363 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7365 url += "&_dc=" + (new Date().getTime());
7367 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7370 cb : "stcCallback"+transId,
7371 scriptId : "stcScript"+transId,
7375 callback : callback,
7381 window[trans.cb] = function(o){
7382 conn.handleResponse(o, trans);
7385 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7387 if(this.autoAbort !== false){
7391 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7393 var script = document.createElement("script");
7394 script.setAttribute("src", url);
7395 script.setAttribute("type", "text/javascript");
7396 script.setAttribute("id", trans.scriptId);
7397 this.head.appendChild(script);
7401 callback.call(scope||this, null, arg, false);
7406 isLoading : function(){
7407 return this.trans ? true : false;
7411 * Abort the current server request.
7414 if(this.isLoading()){
7415 this.destroyTrans(this.trans);
7420 destroyTrans : function(trans, isLoaded){
7421 this.head.removeChild(document.getElementById(trans.scriptId));
7422 clearTimeout(trans.timeoutId);
7424 window[trans.cb] = undefined;
7426 delete window[trans.cb];
7429 // if hasn't been loaded, wait for load to remove it to prevent script error
7430 window[trans.cb] = function(){
7431 window[trans.cb] = undefined;
7433 delete window[trans.cb];
7440 handleResponse : function(o, trans){
7442 this.destroyTrans(trans, true);
7445 result = trans.reader.readRecords(o);
7447 this.fireEvent("loadexception", this, o, trans.arg, e);
7448 trans.callback.call(trans.scope||window, null, trans.arg, false);
7451 this.fireEvent("load", this, o, trans.arg);
7452 trans.callback.call(trans.scope||window, result, trans.arg, true);
7456 handleFailure : function(trans){
7458 this.destroyTrans(trans, false);
7459 this.fireEvent("loadexception", this, null, trans.arg);
7460 trans.callback.call(trans.scope||window, null, trans.arg, false);
7464 * Ext JS Library 1.1.1
7465 * Copyright(c) 2006-2007, Ext JS, LLC.
7467 * Originally Released Under LGPL - original licence link has changed is not relivant.
7470 * <script type="text/javascript">
7474 * @class Roo.data.JsonReader
7475 * @extends Roo.data.DataReader
7476 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7477 * based on mappings in a provided Roo.data.Record constructor.
7479 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7480 * in the reply previously.
7485 var RecordDef = Roo.data.Record.create([
7486 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7487 {name: 'occupation'} // This field will use "occupation" as the mapping.
7489 var myReader = new Roo.data.JsonReader({
7490 totalProperty: "results", // The property which contains the total dataset size (optional)
7491 root: "rows", // The property which contains an Array of row objects
7492 id: "id" // The property within each row object that provides an ID for the record (optional)
7496 * This would consume a JSON file like this:
7498 { 'results': 2, 'rows': [
7499 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7500 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7503 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7504 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7505 * paged from the remote server.
7506 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7507 * @cfg {String} root name of the property which contains the Array of row objects.
7508 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7510 * Create a new JsonReader
7511 * @param {Object} meta Metadata configuration options
7512 * @param {Object} recordType Either an Array of field definition objects,
7513 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7515 Roo.data.JsonReader = function(meta, recordType){
7518 // set some defaults:
7520 totalProperty: 'total',
7521 successProperty : 'success',
7526 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7528 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7531 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7532 * Used by Store query builder to append _requestMeta to params.
7535 metaFromRemote : false,
7537 * This method is only used by a DataProxy which has retrieved data from a remote server.
7538 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7539 * @return {Object} data A data block which is used by an Roo.data.Store object as
7540 * a cache of Roo.data.Records.
7542 read : function(response){
7543 var json = response.responseText;
7545 var o = /* eval:var:o */ eval("("+json+")");
7547 throw {message: "JsonReader.read: Json object not found"};
7553 this.metaFromRemote = true;
7554 this.meta = o.metaData;
7555 this.recordType = Roo.data.Record.create(o.metaData.fields);
7556 this.onMetaChange(this.meta, this.recordType, o);
7558 return this.readRecords(o);
7561 // private function a store will implement
7562 onMetaChange : function(meta, recordType, o){
7569 simpleAccess: function(obj, subsc) {
7576 getJsonAccessor: function(){
7578 return function(expr) {
7580 return(re.test(expr))
7581 ? new Function("obj", "return obj." + expr)
7591 * Create a data block containing Roo.data.Records from an XML document.
7592 * @param {Object} o An object which contains an Array of row objects in the property specified
7593 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7594 * which contains the total size of the dataset.
7595 * @return {Object} data A data block which is used by an Roo.data.Store object as
7596 * a cache of Roo.data.Records.
7598 readRecords : function(o){
7600 * After any data loads, the raw JSON data is available for further custom processing.
7604 var s = this.meta, Record = this.recordType,
7605 f = Record.prototype.fields, fi = f.items, fl = f.length;
7607 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7609 if(s.totalProperty) {
7610 this.getTotal = this.getJsonAccessor(s.totalProperty);
7612 if(s.successProperty) {
7613 this.getSuccess = this.getJsonAccessor(s.successProperty);
7615 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7617 var g = this.getJsonAccessor(s.id);
7618 this.getId = function(rec) {
7620 return (r === undefined || r === "") ? null : r;
7623 this.getId = function(){return null;};
7626 for(var jj = 0; jj < fl; jj++){
7628 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7629 this.ef[jj] = this.getJsonAccessor(map);
7633 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7634 if(s.totalProperty){
7635 var vt = parseInt(this.getTotal(o), 10);
7640 if(s.successProperty){
7641 var vs = this.getSuccess(o);
7642 if(vs === false || vs === 'false'){
7647 for(var i = 0; i < c; i++){
7650 var id = this.getId(n);
7651 for(var j = 0; j < fl; j++){
7653 var v = this.ef[j](n);
7655 Roo.log('missing convert for ' + f.name);
7659 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7661 var record = new Record(values, id);
7663 records[i] = record;
7669 totalRecords : totalRecords
7674 * Ext JS Library 1.1.1
7675 * Copyright(c) 2006-2007, Ext JS, LLC.
7677 * Originally Released Under LGPL - original licence link has changed is not relivant.
7680 * <script type="text/javascript">
7684 * @class Roo.data.ArrayReader
7685 * @extends Roo.data.DataReader
7686 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7687 * Each element of that Array represents a row of data fields. The
7688 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7689 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7693 var RecordDef = Roo.data.Record.create([
7694 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7695 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7697 var myReader = new Roo.data.ArrayReader({
7698 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7702 * This would consume an Array like this:
7704 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7706 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7708 * Create a new JsonReader
7709 * @param {Object} meta Metadata configuration options.
7710 * @param {Object} recordType Either an Array of field definition objects
7711 * as specified to {@link Roo.data.Record#create},
7712 * or an {@link Roo.data.Record} object
7713 * created using {@link Roo.data.Record#create}.
7715 Roo.data.ArrayReader = function(meta, recordType){
7716 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7719 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7721 * Create a data block containing Roo.data.Records from an XML document.
7722 * @param {Object} o An Array of row objects which represents the dataset.
7723 * @return {Object} data A data block which is used by an Roo.data.Store object as
7724 * a cache of Roo.data.Records.
7726 readRecords : function(o){
7727 var sid = this.meta ? this.meta.id : null;
7728 var recordType = this.recordType, fields = recordType.prototype.fields;
7731 for(var i = 0; i < root.length; i++){
7734 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7735 for(var j = 0, jlen = fields.length; j < jlen; j++){
7736 var f = fields.items[j];
7737 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7738 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7742 var record = new recordType(values, id);
7744 records[records.length] = record;
7748 totalRecords : records.length
7757 * @class Roo.bootstrap.ComboBox
7758 * @extends Roo.bootstrap.TriggerField
7759 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7760 * @cfg {Boolean} append (true|false) default false
7762 * Create a new ComboBox.
7763 * @param {Object} config Configuration options
7765 Roo.bootstrap.ComboBox = function(config){
7766 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7770 * Fires when the dropdown list is expanded
7771 * @param {Roo.bootstrap.ComboBox} combo This combo box
7776 * Fires when the dropdown list is collapsed
7777 * @param {Roo.bootstrap.ComboBox} combo This combo box
7781 * @event beforeselect
7782 * Fires before a list item is selected. Return false to cancel the selection.
7783 * @param {Roo.bootstrap.ComboBox} combo This combo box
7784 * @param {Roo.data.Record} record The data record returned from the underlying store
7785 * @param {Number} index The index of the selected item in the dropdown list
7787 'beforeselect' : true,
7790 * Fires when a list item is selected
7791 * @param {Roo.bootstrap.ComboBox} combo This combo box
7792 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7793 * @param {Number} index The index of the selected item in the dropdown list
7797 * @event beforequery
7798 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7799 * The event object passed has these properties:
7800 * @param {Roo.bootstrap.ComboBox} combo This combo box
7801 * @param {String} query The query
7802 * @param {Boolean} forceAll true to force "all" query
7803 * @param {Boolean} cancel true to cancel the query
7804 * @param {Object} e The query event object
7806 'beforequery': true,
7809 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7810 * @param {Roo.bootstrap.ComboBox} combo This combo box
7815 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7816 * @param {Roo.bootstrap.ComboBox} combo This combo box
7817 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7822 * Fires when the remove value from the combobox array
7823 * @param {Roo.bootstrap.ComboBox} combo This combo box
7830 this.selectedIndex = -1;
7831 if(this.mode == 'local'){
7832 if(config.queryDelay === undefined){
7833 this.queryDelay = 10;
7835 if(config.minChars === undefined){
7841 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7844 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7845 * rendering into an Roo.Editor, defaults to false)
7848 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7849 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7852 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7855 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7856 * the dropdown list (defaults to undefined, with no header element)
7860 * @cfg {String/Roo.Template} tpl The template to use to render the output
7864 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7866 listWidth: undefined,
7868 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7869 * mode = 'remote' or 'text' if mode = 'local')
7871 displayField: undefined,
7873 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7874 * mode = 'remote' or 'value' if mode = 'local').
7875 * Note: use of a valueField requires the user make a selection
7876 * in order for a value to be mapped.
7878 valueField: undefined,
7882 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7883 * field's data value (defaults to the underlying DOM element's name)
7885 hiddenName: undefined,
7887 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7891 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7893 selectedClass: 'active',
7896 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7900 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7901 * anchor positions (defaults to 'tl-bl')
7903 listAlign: 'tl-bl?',
7905 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7909 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7910 * query specified by the allQuery config option (defaults to 'query')
7912 triggerAction: 'query',
7914 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7915 * (defaults to 4, does not apply if editable = false)
7919 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7920 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7924 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7925 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7929 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7930 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7934 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7935 * when editable = true (defaults to false)
7937 selectOnFocus:false,
7939 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7941 queryParam: 'query',
7943 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7944 * when mode = 'remote' (defaults to 'Loading...')
7946 loadingText: 'Loading...',
7948 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7952 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7956 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7957 * traditional select (defaults to true)
7961 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7965 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7969 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7970 * listWidth has a higher value)
7974 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7975 * allow the user to set arbitrary text into the field (defaults to false)
7977 forceSelection:false,
7979 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7980 * if typeAhead = true (defaults to 250)
7982 typeAheadDelay : 250,
7984 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7985 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7987 valueNotFoundText : undefined,
7989 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7994 * @cfg {Boolean} disableClear Disable showing of clear button.
7996 disableClear : false,
7998 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
8000 alwaysQuery : false,
8003 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8017 // element that contains real text value.. (when hidden is used..)
8020 initEvents: function(){
8023 throw "can not find store for combo";
8025 this.store = Roo.factory(this.store, Roo.data);
8029 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8032 if(this.hiddenName){
8034 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8036 this.hiddenField.dom.value =
8037 this.hiddenValue !== undefined ? this.hiddenValue :
8038 this.value !== undefined ? this.value : '';
8040 // prevent input submission
8041 this.el.dom.removeAttribute('name');
8042 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8047 // this.el.dom.setAttribute('autocomplete', 'off');
8050 var cls = 'x-combo-list';
8051 this.list = this.el.select('ul.dropdown-menu',true).first();
8053 //this.list = new Roo.Layer({
8054 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8057 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8058 this.list.setWidth(lw);
8060 this.list.on('mouseover', this.onViewOver, this);
8061 this.list.on('mousemove', this.onViewMove, this);
8063 this.list.on('scroll', this.onViewScroll, this);
8066 this.list.swallowEvent('mousewheel');
8067 this.assetHeight = 0;
8070 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8071 this.assetHeight += this.header.getHeight();
8074 this.innerList = this.list.createChild({cls:cls+'-inner'});
8075 this.innerList.on('mouseover', this.onViewOver, this);
8076 this.innerList.on('mousemove', this.onViewMove, this);
8077 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8079 if(this.allowBlank && !this.pageSize && !this.disableClear){
8080 this.footer = this.list.createChild({cls:cls+'-ft'});
8081 this.pageTb = new Roo.Toolbar(this.footer);
8085 this.footer = this.list.createChild({cls:cls+'-ft'});
8086 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8087 {pageSize: this.pageSize});
8091 if (this.pageTb && this.allowBlank && !this.disableClear) {
8093 this.pageTb.add(new Roo.Toolbar.Fill(), {
8094 cls: 'x-btn-icon x-btn-clear',
8100 _this.onSelect(false, -1);
8105 this.assetHeight += this.footer.getHeight();
8110 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8113 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8114 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8116 //this.view.wrapEl.setDisplayed(false);
8117 this.view.on('click', this.onViewClick, this);
8121 this.store.on('beforeload', this.onBeforeLoad, this);
8122 this.store.on('load', this.onLoad, this);
8123 this.store.on('loadexception', this.onLoadException, this);
8126 this.resizer = new Roo.Resizable(this.list, {
8127 pinned:true, handles:'se'
8129 this.resizer.on('resize', function(r, w, h){
8130 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8132 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8133 this.restrictHeight();
8135 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8139 this.editable = true;
8140 this.setEditable(false);
8145 if (typeof(this.events.add.listeners) != 'undefined') {
8147 this.addicon = this.wrap.createChild(
8148 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8150 this.addicon.on('click', function(e) {
8151 this.fireEvent('add', this);
8154 if (typeof(this.events.edit.listeners) != 'undefined') {
8156 this.editicon = this.wrap.createChild(
8157 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8159 this.editicon.setStyle('margin-left', '40px');
8161 this.editicon.on('click', function(e) {
8163 // we fire even if inothing is selected..
8164 this.fireEvent('edit', this, this.lastData );
8170 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8172 this.inKeyMode = true;
8176 "down" : function(e){
8177 if(!this.isExpanded()){
8178 this.onTriggerClick();
8180 this.inKeyMode = true;
8185 "enter" : function(e){
8190 "esc" : function(e){
8194 "tab" : function(e){
8197 if(this.fireEvent("specialkey", this, e)){
8198 this.onViewClick(false);
8206 doRelay : function(foo, bar, hname){
8207 if(hname == 'down' || this.scope.isExpanded()){
8208 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8217 this.queryDelay = Math.max(this.queryDelay || 10,
8218 this.mode == 'local' ? 10 : 250);
8221 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8224 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8226 if(this.editable !== false){
8227 this.inputEl().on("keyup", this.onKeyUp, this);
8229 if(this.forceSelection){
8230 this.on('blur', this.doForce, this);
8234 this.choices = this.el.select('ul.select2-choices', true).first();
8235 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8239 onDestroy : function(){
8241 this.view.setStore(null);
8242 this.view.el.removeAllListeners();
8243 this.view.el.remove();
8244 this.view.purgeListeners();
8247 this.list.dom.innerHTML = '';
8250 this.store.un('beforeload', this.onBeforeLoad, this);
8251 this.store.un('load', this.onLoad, this);
8252 this.store.un('loadexception', this.onLoadException, this);
8254 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8258 fireKey : function(e){
8259 if(e.isNavKeyPress() && !this.list.isVisible()){
8260 this.fireEvent("specialkey", this, e);
8265 onResize: function(w, h){
8266 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8268 // if(typeof w != 'number'){
8269 // // we do not handle it!?!?
8272 // var tw = this.trigger.getWidth();
8273 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8274 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8276 // this.inputEl().setWidth( this.adjustWidth('input', x));
8278 // //this.trigger.setStyle('left', x+'px');
8280 // if(this.list && this.listWidth === undefined){
8281 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8282 // this.list.setWidth(lw);
8283 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8291 * Allow or prevent the user from directly editing the field text. If false is passed,
8292 * the user will only be able to select from the items defined in the dropdown list. This method
8293 * is the runtime equivalent of setting the 'editable' config option at config time.
8294 * @param {Boolean} value True to allow the user to directly edit the field text
8296 setEditable : function(value){
8297 if(value == this.editable){
8300 this.editable = value;
8302 this.inputEl().dom.setAttribute('readOnly', true);
8303 this.inputEl().on('mousedown', this.onTriggerClick, this);
8304 this.inputEl().addClass('x-combo-noedit');
8306 this.inputEl().dom.setAttribute('readOnly', false);
8307 this.inputEl().un('mousedown', this.onTriggerClick, this);
8308 this.inputEl().removeClass('x-combo-noedit');
8314 onBeforeLoad : function(combo,opts){
8319 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8321 this.restrictHeight();
8322 this.selectedIndex = -1;
8326 onLoad : function(){
8328 this.hasQuery = false;
8334 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8335 this.loading.hide();
8338 if(this.store.getCount() > 0){
8340 this.restrictHeight();
8341 if(this.lastQuery == this.allQuery){
8343 this.inputEl().dom.select();
8345 if(!this.selectByValue(this.value, true)){
8346 this.select(0, true);
8350 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8351 this.taTask.delay(this.typeAheadDelay);
8355 this.onEmptyResults();
8361 onLoadException : function()
8363 this.hasQuery = false;
8365 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8366 this.loading.hide();
8370 Roo.log(this.store.reader.jsonData);
8371 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8373 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8379 onTypeAhead : function(){
8380 if(this.store.getCount() > 0){
8381 var r = this.store.getAt(0);
8382 var newValue = r.data[this.displayField];
8383 var len = newValue.length;
8384 var selStart = this.getRawValue().length;
8386 if(selStart != len){
8387 this.setRawValue(newValue);
8388 this.selectText(selStart, newValue.length);
8394 onSelect : function(record, index){
8396 if(this.fireEvent('beforeselect', this, record, index) !== false){
8398 this.setFromData(index > -1 ? record.data : false);
8401 this.fireEvent('select', this, record, index);
8406 * Returns the currently selected field value or empty string if no value is set.
8407 * @return {String} value The selected value
8409 getValue : function(){
8412 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8415 if(this.valueField){
8416 return typeof this.value != 'undefined' ? this.value : '';
8418 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8423 * Clears any text/value currently set in the field
8425 clearValue : function(){
8426 if(this.hiddenField){
8427 this.hiddenField.dom.value = '';
8430 this.setRawValue('');
8431 this.lastSelectionText = '';
8436 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8437 * will be displayed in the field. If the value does not match the data value of an existing item,
8438 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8439 * Otherwise the field will be blank (although the value will still be set).
8440 * @param {String} value The value to match
8442 setValue : function(v){
8449 if(this.valueField){
8450 var r = this.findRecord(this.valueField, v);
8452 text = r.data[this.displayField];
8453 }else if(this.valueNotFoundText !== undefined){
8454 text = this.valueNotFoundText;
8457 this.lastSelectionText = text;
8458 if(this.hiddenField){
8459 this.hiddenField.dom.value = v;
8461 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8465 * @property {Object} the last set data for the element
8470 * Sets the value of the field based on a object which is related to the record format for the store.
8471 * @param {Object} value the value to set as. or false on reset?
8473 setFromData : function(o){
8480 var dv = ''; // display value
8481 var vv = ''; // value value..
8483 if (this.displayField) {
8484 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8486 // this is an error condition!!!
8487 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8490 if(this.valueField){
8491 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8494 if(this.hiddenField){
8495 this.hiddenField.dom.value = vv;
8497 this.lastSelectionText = dv;
8498 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8502 // no hidden field.. - we store the value in 'value', but still display
8503 // display field!!!!
8504 this.lastSelectionText = dv;
8505 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8512 // overridden so that last data is reset..
8513 this.setValue(this.originalValue);
8514 this.clearInvalid();
8515 this.lastData = false;
8517 this.view.clearSelections();
8521 findRecord : function(prop, value){
8523 if(this.store.getCount() > 0){
8524 this.store.each(function(r){
8525 if(r.data[prop] == value){
8537 // returns hidden if it's set..
8538 if (!this.rendered) {return ''};
8539 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8543 onViewMove : function(e, t){
8544 this.inKeyMode = false;
8548 onViewOver : function(e, t){
8549 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8552 var item = this.view.findItemFromChild(t);
8554 var index = this.view.indexOf(item);
8555 this.select(index, false);
8560 onViewClick : function(doFocus)
8562 var index = this.view.getSelectedIndexes()[0];
8563 var r = this.store.getAt(index);
8565 this.onSelect(r, index);
8567 if(doFocus !== false && !this.blockFocus){
8568 this.inputEl().focus();
8573 restrictHeight : function(){
8574 //this.innerList.dom.style.height = '';
8575 //var inner = this.innerList.dom;
8576 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8577 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8578 //this.list.beginUpdate();
8579 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8580 this.list.alignTo(this.inputEl(), this.listAlign);
8581 //this.list.endUpdate();
8585 onEmptyResults : function(){
8590 * Returns true if the dropdown list is expanded, else false.
8592 isExpanded : function(){
8593 return this.list.isVisible();
8597 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8598 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8599 * @param {String} value The data value of the item to select
8600 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8601 * selected item if it is not currently in view (defaults to true)
8602 * @return {Boolean} True if the value matched an item in the list, else false
8604 selectByValue : function(v, scrollIntoView){
8605 if(v !== undefined && v !== null){
8606 var r = this.findRecord(this.valueField || this.displayField, v);
8608 this.select(this.store.indexOf(r), scrollIntoView);
8616 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8617 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8618 * @param {Number} index The zero-based index of the list item to select
8619 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8620 * selected item if it is not currently in view (defaults to true)
8622 select : function(index, scrollIntoView){
8623 this.selectedIndex = index;
8624 this.view.select(index);
8625 if(scrollIntoView !== false){
8626 var el = this.view.getNode(index);
8628 //this.innerList.scrollChildIntoView(el, false);
8635 selectNext : function(){
8636 var ct = this.store.getCount();
8638 if(this.selectedIndex == -1){
8640 }else if(this.selectedIndex < ct-1){
8641 this.select(this.selectedIndex+1);
8647 selectPrev : function(){
8648 var ct = this.store.getCount();
8650 if(this.selectedIndex == -1){
8652 }else if(this.selectedIndex != 0){
8653 this.select(this.selectedIndex-1);
8659 onKeyUp : function(e){
8660 if(this.editable !== false && !e.isSpecialKey()){
8661 this.lastKey = e.getKey();
8662 this.dqTask.delay(this.queryDelay);
8667 validateBlur : function(){
8668 return !this.list || !this.list.isVisible();
8672 initQuery : function(){
8673 this.doQuery(this.getRawValue());
8677 doForce : function(){
8678 if(this.el.dom.value.length > 0){
8680 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8686 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8687 * query allowing the query action to be canceled if needed.
8688 * @param {String} query The SQL query to execute
8689 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8690 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8691 * saved in the current store (defaults to false)
8693 doQuery : function(q, forceAll){
8695 if(q === undefined || q === null){
8704 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8709 forceAll = qe.forceAll;
8710 if(forceAll === true || (q.length >= this.minChars)){
8712 this.hasQuery = true;
8714 if(this.lastQuery != q || this.alwaysQuery){
8716 if(this.mode == 'local'){
8717 this.selectedIndex = -1;
8719 this.store.clearFilter();
8721 this.store.filter(this.displayField, q);
8725 this.store.baseParams[this.queryParam] = q;
8727 var options = {params : this.getParams(q)};
8731 options.params.start = this.page * this.pageSize;
8734 this.store.load(options);
8738 this.selectedIndex = -1;
8743 this.loadNext = false;
8747 getParams : function(q){
8749 //p[this.queryParam] = q;
8753 p.limit = this.pageSize;
8759 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8761 collapse : function(){
8762 if(!this.isExpanded()){
8767 Roo.get(document).un('mousedown', this.collapseIf, this);
8768 Roo.get(document).un('mousewheel', this.collapseIf, this);
8769 if (!this.editable) {
8770 Roo.get(document).un('keydown', this.listKeyPress, this);
8772 this.fireEvent('collapse', this);
8776 collapseIf : function(e){
8777 var in_combo = e.within(this.el);
8778 var in_list = e.within(this.list);
8780 if (in_combo || in_list) {
8781 //e.stopPropagation();
8790 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8792 expand : function(){
8794 if(this.isExpanded() || !this.hasFocus){
8798 this.list.alignTo(this.inputEl(), this.listAlign);
8800 Roo.get(document).on('mousedown', this.collapseIf, this);
8801 Roo.get(document).on('mousewheel', this.collapseIf, this);
8802 if (!this.editable) {
8803 Roo.get(document).on('keydown', this.listKeyPress, this);
8806 this.fireEvent('expand', this);
8810 // Implements the default empty TriggerField.onTriggerClick function
8811 onTriggerClick : function()
8813 Roo.log('trigger click');
8820 this.loadNext = false;
8822 if(this.isExpanded()){
8824 if (!this.blockFocus) {
8825 this.inputEl().focus();
8829 this.hasFocus = true;
8830 if(this.triggerAction == 'all') {
8831 this.doQuery(this.allQuery, true);
8833 this.doQuery(this.getRawValue());
8835 if (!this.blockFocus) {
8836 this.inputEl().focus();
8840 listKeyPress : function(e)
8842 //Roo.log('listkeypress');
8843 // scroll to first matching element based on key pres..
8844 if (e.isSpecialKey()) {
8847 var k = String.fromCharCode(e.getKey()).toUpperCase();
8850 var csel = this.view.getSelectedNodes();
8851 var cselitem = false;
8853 var ix = this.view.indexOf(csel[0]);
8854 cselitem = this.store.getAt(ix);
8855 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8861 this.store.each(function(v) {
8863 // start at existing selection.
8864 if (cselitem.id == v.id) {
8870 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8871 match = this.store.indexOf(v);
8877 if (match === false) {
8878 return true; // no more action?
8881 this.view.select(match);
8882 var sn = Roo.get(this.view.getSelectedNodes()[0])
8883 //sn.scrollIntoView(sn.dom.parentNode, false);
8886 onViewScroll : function(e, t){
8888 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8892 this.hasQuery = true;
8894 this.loading = this.list.select('.loading', true).first();
8896 if(this.loading === null){
8897 this.list.createChild({
8899 cls: 'loading select2-more-results select2-active',
8900 html: 'Loading more results...'
8903 this.loading = this.list.select('.loading', true).first();
8905 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8907 this.loading.hide();
8910 this.loading.show();
8915 this.loadNext = true;
8917 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8922 addItem : function(o)
8924 var dv = ''; // display value
8926 if (this.displayField) {
8927 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8929 // this is an error condition!!!
8930 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8937 var choice = this.choices.createChild({
8939 cls: 'select2-search-choice',
8948 cls: 'select2-search-choice-close',
8953 }, this.searchField);
8955 var close = choice.select('a.select2-search-choice-close', true).first()
8957 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8964 this.inputEl().dom.value = '';
8968 onRemoveItem : function(e, _self, o)
8970 Roo.log('remove item');
8971 var index = this.item.indexOf(o.data) * 1;
8974 Roo.log('not this item?!');
8978 this.item.splice(index, 1);
8983 this.fireEvent('remove', this);
8987 syncValue : function()
8989 if(!this.item.length){
8996 Roo.each(this.item, function(i){
8997 if(_this.valueField){
8998 value.push(i[_this.valueField]);
9005 this.value = value.join(',');
9007 if(this.hiddenField){
9008 this.hiddenField.dom.value = this.value;
9012 clearItem : function()
9020 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9030 * @cfg {Boolean} grow
9034 * @cfg {Number} growMin
9038 * @cfg {Number} growMax
9048 * Ext JS Library 1.1.1
9049 * Copyright(c) 2006-2007, Ext JS, LLC.
9051 * Originally Released Under LGPL - original licence link has changed is not relivant.
9054 * <script type="text/javascript">
9059 * @extends Roo.util.Observable
9060 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9061 * This class also supports single and multi selection modes. <br>
9062 * Create a data model bound view:
9064 var store = new Roo.data.Store(...);
9066 var view = new Roo.View({
9068 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9071 selectedClass: "ydataview-selected",
9075 // listen for node click?
9076 view.on("click", function(vw, index, node, e){
9077 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9081 dataModel.load("foobar.xml");
9083 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9085 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9086 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9088 * Note: old style constructor is still suported (container, template, config)
9092 * @param {Object} config The config object
9095 Roo.View = function(config, depreciated_tpl, depreciated_config){
9097 if (typeof(depreciated_tpl) == 'undefined') {
9098 // new way.. - universal constructor.
9099 Roo.apply(this, config);
9100 this.el = Roo.get(this.el);
9103 this.el = Roo.get(config);
9104 this.tpl = depreciated_tpl;
9105 Roo.apply(this, depreciated_config);
9107 this.wrapEl = this.el.wrap().wrap();
9108 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9111 if(typeof(this.tpl) == "string"){
9112 this.tpl = new Roo.Template(this.tpl);
9114 // support xtype ctors..
9115 this.tpl = new Roo.factory(this.tpl, Roo);
9127 * @event beforeclick
9128 * Fires before a click is processed. Returns false to cancel the default action.
9129 * @param {Roo.View} this
9130 * @param {Number} index The index of the target node
9131 * @param {HTMLElement} node The target node
9132 * @param {Roo.EventObject} e The raw event object
9134 "beforeclick" : true,
9137 * Fires when a template node is clicked.
9138 * @param {Roo.View} this
9139 * @param {Number} index The index of the target node
9140 * @param {HTMLElement} node The target node
9141 * @param {Roo.EventObject} e The raw event object
9146 * Fires when a template node is double clicked.
9147 * @param {Roo.View} this
9148 * @param {Number} index The index of the target node
9149 * @param {HTMLElement} node The target node
9150 * @param {Roo.EventObject} e The raw event object
9154 * @event contextmenu
9155 * Fires when a template node is right clicked.
9156 * @param {Roo.View} this
9157 * @param {Number} index The index of the target node
9158 * @param {HTMLElement} node The target node
9159 * @param {Roo.EventObject} e The raw event object
9161 "contextmenu" : true,
9163 * @event selectionchange
9164 * Fires when the selected nodes change.
9165 * @param {Roo.View} this
9166 * @param {Array} selections Array of the selected nodes
9168 "selectionchange" : true,
9171 * @event beforeselect
9172 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9173 * @param {Roo.View} this
9174 * @param {HTMLElement} node The node to be selected
9175 * @param {Array} selections Array of currently selected nodes
9177 "beforeselect" : true,
9179 * @event preparedata
9180 * Fires on every row to render, to allow you to change the data.
9181 * @param {Roo.View} this
9182 * @param {Object} data to be rendered (change this)
9184 "preparedata" : true
9192 "click": this.onClick,
9193 "dblclick": this.onDblClick,
9194 "contextmenu": this.onContextMenu,
9198 this.selections = [];
9200 this.cmp = new Roo.CompositeElementLite([]);
9202 this.store = Roo.factory(this.store, Roo.data);
9203 this.setStore(this.store, true);
9206 if ( this.footer && this.footer.xtype) {
9208 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9210 this.footer.dataSource = this.store
9211 this.footer.container = fctr;
9212 this.footer = Roo.factory(this.footer, Roo);
9213 fctr.insertFirst(this.el);
9215 // this is a bit insane - as the paging toolbar seems to detach the el..
9216 // dom.parentNode.parentNode.parentNode
9217 // they get detached?
9221 Roo.View.superclass.constructor.call(this);
9226 Roo.extend(Roo.View, Roo.util.Observable, {
9229 * @cfg {Roo.data.Store} store Data store to load data from.
9234 * @cfg {String|Roo.Element} el The container element.
9239 * @cfg {String|Roo.Template} tpl The template used by this View
9243 * @cfg {String} dataName the named area of the template to use as the data area
9244 * Works with domtemplates roo-name="name"
9248 * @cfg {String} selectedClass The css class to add to selected nodes
9250 selectedClass : "x-view-selected",
9252 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9257 * @cfg {String} text to display on mask (default Loading)
9261 * @cfg {Boolean} multiSelect Allow multiple selection
9263 multiSelect : false,
9265 * @cfg {Boolean} singleSelect Allow single selection
9267 singleSelect: false,
9270 * @cfg {Boolean} toggleSelect - selecting
9272 toggleSelect : false,
9275 * Returns the element this view is bound to.
9276 * @return {Roo.Element}
9285 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9287 refresh : function(){
9291 // if we are using something like 'domtemplate', then
9292 // the what gets used is:
9293 // t.applySubtemplate(NAME, data, wrapping data..)
9294 // the outer template then get' applied with
9295 // the store 'extra data'
9296 // and the body get's added to the
9297 // roo-name="data" node?
9298 // <span class='roo-tpl-{name}'></span> ?????
9302 this.clearSelections();
9305 var records = this.store.getRange();
9306 if(records.length < 1) {
9308 // is this valid?? = should it render a template??
9310 this.el.update(this.emptyText);
9314 if (this.dataName) {
9315 this.el.update(t.apply(this.store.meta)); //????
9316 el = this.el.child('.roo-tpl-' + this.dataName);
9319 for(var i = 0, len = records.length; i < len; i++){
9320 var data = this.prepareData(records[i].data, i, records[i]);
9321 this.fireEvent("preparedata", this, data, i, records[i]);
9322 html[html.length] = Roo.util.Format.trim(
9324 t.applySubtemplate(this.dataName, data, this.store.meta) :
9331 el.update(html.join(""));
9332 this.nodes = el.dom.childNodes;
9333 this.updateIndexes(0);
9338 * Function to override to reformat the data that is sent to
9339 * the template for each node.
9340 * DEPRICATED - use the preparedata event handler.
9341 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9342 * a JSON object for an UpdateManager bound view).
9344 prepareData : function(data, index, record)
9346 this.fireEvent("preparedata", this, data, index, record);
9350 onUpdate : function(ds, record){
9351 Roo.log('on update');
9352 this.clearSelections();
9353 var index = this.store.indexOf(record);
9354 var n = this.nodes[index];
9355 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9356 n.parentNode.removeChild(n);
9357 this.updateIndexes(index, index);
9363 onAdd : function(ds, records, index)
9365 Roo.log(['on Add', ds, records, index] );
9366 this.clearSelections();
9367 if(this.nodes.length == 0){
9371 var n = this.nodes[index];
9372 for(var i = 0, len = records.length; i < len; i++){
9373 var d = this.prepareData(records[i].data, i, records[i]);
9375 this.tpl.insertBefore(n, d);
9378 this.tpl.append(this.el, d);
9381 this.updateIndexes(index);
9384 onRemove : function(ds, record, index){
9385 Roo.log('onRemove');
9386 this.clearSelections();
9387 var el = this.dataName ?
9388 this.el.child('.roo-tpl-' + this.dataName) :
9391 el.dom.removeChild(this.nodes[index]);
9392 this.updateIndexes(index);
9396 * Refresh an individual node.
9397 * @param {Number} index
9399 refreshNode : function(index){
9400 this.onUpdate(this.store, this.store.getAt(index));
9403 updateIndexes : function(startIndex, endIndex){
9404 var ns = this.nodes;
9405 startIndex = startIndex || 0;
9406 endIndex = endIndex || ns.length - 1;
9407 for(var i = startIndex; i <= endIndex; i++){
9408 ns[i].nodeIndex = i;
9413 * Changes the data store this view uses and refresh the view.
9414 * @param {Store} store
9416 setStore : function(store, initial){
9417 if(!initial && this.store){
9418 this.store.un("datachanged", this.refresh);
9419 this.store.un("add", this.onAdd);
9420 this.store.un("remove", this.onRemove);
9421 this.store.un("update", this.onUpdate);
9422 this.store.un("clear", this.refresh);
9423 this.store.un("beforeload", this.onBeforeLoad);
9424 this.store.un("load", this.onLoad);
9425 this.store.un("loadexception", this.onLoad);
9429 store.on("datachanged", this.refresh, this);
9430 store.on("add", this.onAdd, this);
9431 store.on("remove", this.onRemove, this);
9432 store.on("update", this.onUpdate, this);
9433 store.on("clear", this.refresh, this);
9434 store.on("beforeload", this.onBeforeLoad, this);
9435 store.on("load", this.onLoad, this);
9436 store.on("loadexception", this.onLoad, this);
9444 * onbeforeLoad - masks the loading area.
9447 onBeforeLoad : function(store,opts)
9449 Roo.log('onBeforeLoad');
9453 this.el.mask(this.mask ? this.mask : "Loading" );
9455 onLoad : function ()
9462 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9463 * @param {HTMLElement} node
9464 * @return {HTMLElement} The template node
9466 findItemFromChild : function(node){
9467 var el = this.dataName ?
9468 this.el.child('.roo-tpl-' + this.dataName,true) :
9471 if(!node || node.parentNode == el){
9474 var p = node.parentNode;
9475 while(p && p != el){
9476 if(p.parentNode == el){
9485 onClick : function(e){
9486 var item = this.findItemFromChild(e.getTarget());
9488 var index = this.indexOf(item);
9489 if(this.onItemClick(item, index, e) !== false){
9490 this.fireEvent("click", this, index, item, e);
9493 this.clearSelections();
9498 onContextMenu : function(e){
9499 var item = this.findItemFromChild(e.getTarget());
9501 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9506 onDblClick : function(e){
9507 var item = this.findItemFromChild(e.getTarget());
9509 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9513 onItemClick : function(item, index, e)
9515 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9518 if (this.toggleSelect) {
9519 var m = this.isSelected(item) ? 'unselect' : 'select';
9522 _t[m](item, true, false);
9525 if(this.multiSelect || this.singleSelect){
9526 if(this.multiSelect && e.shiftKey && this.lastSelection){
9527 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9529 this.select(item, this.multiSelect && e.ctrlKey);
9530 this.lastSelection = item;
9538 * Get the number of selected nodes.
9541 getSelectionCount : function(){
9542 return this.selections.length;
9546 * Get the currently selected nodes.
9547 * @return {Array} An array of HTMLElements
9549 getSelectedNodes : function(){
9550 return this.selections;
9554 * Get the indexes of the selected nodes.
9557 getSelectedIndexes : function(){
9558 var indexes = [], s = this.selections;
9559 for(var i = 0, len = s.length; i < len; i++){
9560 indexes.push(s[i].nodeIndex);
9566 * Clear all selections
9567 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9569 clearSelections : function(suppressEvent){
9570 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9571 this.cmp.elements = this.selections;
9572 this.cmp.removeClass(this.selectedClass);
9573 this.selections = [];
9575 this.fireEvent("selectionchange", this, this.selections);
9581 * Returns true if the passed node is selected
9582 * @param {HTMLElement/Number} node The node or node index
9585 isSelected : function(node){
9586 var s = this.selections;
9590 node = this.getNode(node);
9591 return s.indexOf(node) !== -1;
9596 * @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
9597 * @param {Boolean} keepExisting (optional) true to keep existing selections
9598 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9600 select : function(nodeInfo, keepExisting, suppressEvent){
9601 if(nodeInfo instanceof Array){
9603 this.clearSelections(true);
9605 for(var i = 0, len = nodeInfo.length; i < len; i++){
9606 this.select(nodeInfo[i], true, true);
9610 var node = this.getNode(nodeInfo);
9611 if(!node || this.isSelected(node)){
9612 return; // already selected.
9615 this.clearSelections(true);
9617 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9618 Roo.fly(node).addClass(this.selectedClass);
9619 this.selections.push(node);
9621 this.fireEvent("selectionchange", this, this.selections);
9629 * @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
9630 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9631 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9633 unselect : function(nodeInfo, keepExisting, suppressEvent)
9635 if(nodeInfo instanceof Array){
9636 Roo.each(this.selections, function(s) {
9637 this.unselect(s, nodeInfo);
9641 var node = this.getNode(nodeInfo);
9642 if(!node || !this.isSelected(node)){
9643 Roo.log("not selected");
9644 return; // not selected.
9648 Roo.each(this.selections, function(s) {
9650 Roo.fly(node).removeClass(this.selectedClass);
9657 this.selections= ns;
9658 this.fireEvent("selectionchange", this, this.selections);
9662 * Gets a template node.
9663 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9664 * @return {HTMLElement} The node or null if it wasn't found
9666 getNode : function(nodeInfo){
9667 if(typeof nodeInfo == "string"){
9668 return document.getElementById(nodeInfo);
9669 }else if(typeof nodeInfo == "number"){
9670 return this.nodes[nodeInfo];
9676 * Gets a range template nodes.
9677 * @param {Number} startIndex
9678 * @param {Number} endIndex
9679 * @return {Array} An array of nodes
9681 getNodes : function(start, end){
9682 var ns = this.nodes;
9684 end = typeof end == "undefined" ? ns.length - 1 : end;
9687 for(var i = start; i <= end; i++){
9691 for(var i = start; i >= end; i--){
9699 * Finds the index of the passed node
9700 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9701 * @return {Number} The index of the node or -1
9703 indexOf : function(node){
9704 node = this.getNode(node);
9705 if(typeof node.nodeIndex == "number"){
9706 return node.nodeIndex;
9708 var ns = this.nodes;
9709 for(var i = 0, len = ns.length; i < len; i++){
9720 * based on jquery fullcalendar
9724 Roo.bootstrap = Roo.bootstrap || {};
9726 * @class Roo.bootstrap.Calendar
9727 * @extends Roo.bootstrap.Component
9728 * Bootstrap Calendar class
9729 * @cfg {Boolean} loadMask (true|false) default false
9732 * Create a new Container
9733 * @param {Object} config The config object
9738 Roo.bootstrap.Calendar = function(config){
9739 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9743 * Fires when a date is selected
9744 * @param {DatePicker} this
9745 * @param {Date} date The selected date
9749 * @event monthchange
9750 * Fires when the displayed month changes
9751 * @param {DatePicker} this
9752 * @param {Date} date The selected month
9754 'monthchange': true,
9757 * Fires when mouse over an event
9758 * @param {Calendar} this
9759 * @param {event} Event
9764 * Fires when the mouse leaves an
9765 * @param {Calendar} this
9771 * Fires when the mouse click an
9772 * @param {Calendar} this
9781 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9784 * @cfg {Number} startDay
9785 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9791 getAutoCreate : function(){
9794 var fc_button = function(name, corner, style, content ) {
9795 return Roo.apply({},{
9797 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9799 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9802 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9810 style : 'width:100%',
9817 cls : 'fc-header-left',
9819 fc_button('prev', 'left', 'arrow', '‹' ),
9820 fc_button('next', 'right', 'arrow', '›' ),
9821 { tag: 'span', cls: 'fc-header-space' },
9822 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9830 cls : 'fc-header-center',
9834 cls: 'fc-header-title',
9837 html : 'month / year'
9845 cls : 'fc-header-right',
9847 /* fc_button('month', 'left', '', 'month' ),
9848 fc_button('week', '', '', 'week' ),
9849 fc_button('day', 'right', '', 'day' )
9861 var cal_heads = function() {
9863 // fixme - handle this.
9865 for (var i =0; i < Date.dayNames.length; i++) {
9866 var d = Date.dayNames[i];
9869 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9870 html : d.substring(0,3)
9874 ret[0].cls += ' fc-first';
9875 ret[6].cls += ' fc-last';
9878 var cal_cell = function(n) {
9881 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9886 cls: 'fc-day-number',
9890 cls: 'fc-day-content',
9894 style: 'position: relative;' // height: 17px;
9906 var cal_rows = function() {
9909 for (var r = 0; r < 6; r++) {
9916 for (var i =0; i < Date.dayNames.length; i++) {
9917 var d = Date.dayNames[i];
9918 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9921 row.cn[0].cls+=' fc-first';
9922 row.cn[0].cn[0].style = 'min-height:90px';
9923 row.cn[6].cls+=' fc-last';
9927 ret[0].cls += ' fc-first';
9928 ret[4].cls += ' fc-prev-last';
9929 ret[5].cls += ' fc-last';
9936 cls: 'fc-border-separate',
9937 style : 'width:100%',
9945 cls : 'fc-first fc-last',
9964 style : "position: relative;",
9967 cls : 'fc-view fc-view-month fc-grid',
9968 style : 'position: relative',
9969 unselectable : 'on',
9972 cls : 'fc-event-container',
9973 style : 'position:absolute;z-index:8;top:0;left:0;'
9991 initEvents : function()
9994 throw "can not find store for calendar";
10000 style: "text-align:center",
10004 style: "background-color:white;width:50%;margin:250 auto",
10008 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10019 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10021 var size = this.el.select('.fc-content', true).first().getSize();
10022 this.maskEl.setSize(size.width, size.height);
10023 this.maskEl.enableDisplayMode("block");
10024 if(!this.loadMask){
10025 this.maskEl.hide();
10028 this.store = Roo.factory(this.store, Roo.data);
10029 this.store.on('load', this.onLoad, this);
10030 this.store.on('beforeload', this.onBeforeLoad, this);
10034 this.cells = this.el.select('.fc-day',true);
10035 //Roo.log(this.cells);
10036 this.textNodes = this.el.query('.fc-day-number');
10037 this.cells.addClassOnOver('fc-state-hover');
10039 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10040 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10041 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10042 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10044 this.on('monthchange', this.onMonthChange, this);
10046 this.update(new Date().clearTime());
10049 resize : function() {
10050 var sz = this.el.getSize();
10052 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10053 this.el.select('.fc-day-content div',true).setHeight(34);
10058 showPrevMonth : function(e){
10059 this.update(this.activeDate.add("mo", -1));
10061 showToday : function(e){
10062 this.update(new Date().clearTime());
10065 showNextMonth : function(e){
10066 this.update(this.activeDate.add("mo", 1));
10070 showPrevYear : function(){
10071 this.update(this.activeDate.add("y", -1));
10075 showNextYear : function(){
10076 this.update(this.activeDate.add("y", 1));
10081 update : function(date)
10083 var vd = this.activeDate;
10084 this.activeDate = date;
10085 // if(vd && this.el){
10086 // var t = date.getTime();
10087 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10088 // Roo.log('using add remove');
10090 // this.fireEvent('monthchange', this, date);
10092 // this.cells.removeClass("fc-state-highlight");
10093 // this.cells.each(function(c){
10094 // if(c.dateValue == t){
10095 // c.addClass("fc-state-highlight");
10096 // setTimeout(function(){
10097 // try{c.dom.firstChild.focus();}catch(e){}
10107 var days = date.getDaysInMonth();
10109 var firstOfMonth = date.getFirstDateOfMonth();
10110 var startingPos = firstOfMonth.getDay()-this.startDay;
10112 if(startingPos < this.startDay){
10116 var pm = date.add(Date.MONTH, -1);
10117 var prevStart = pm.getDaysInMonth()-startingPos;
10119 this.cells = this.el.select('.fc-day',true);
10120 this.textNodes = this.el.query('.fc-day-number');
10121 this.cells.addClassOnOver('fc-state-hover');
10123 var cells = this.cells.elements;
10124 var textEls = this.textNodes;
10126 Roo.each(cells, function(cell){
10127 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10130 days += startingPos;
10132 // convert everything to numbers so it's fast
10133 var day = 86400000;
10134 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10137 //Roo.log(prevStart);
10139 var today = new Date().clearTime().getTime();
10140 var sel = date.clearTime().getTime();
10141 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10142 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10143 var ddMatch = this.disabledDatesRE;
10144 var ddText = this.disabledDatesText;
10145 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10146 var ddaysText = this.disabledDaysText;
10147 var format = this.format;
10149 var setCellClass = function(cal, cell){
10151 //Roo.log('set Cell Class');
10153 var t = d.getTime();
10157 cell.dateValue = t;
10159 cell.className += " fc-today";
10160 cell.className += " fc-state-highlight";
10161 cell.title = cal.todayText;
10164 // disable highlight in other month..
10165 //cell.className += " fc-state-highlight";
10170 cell.className = " fc-state-disabled";
10171 cell.title = cal.minText;
10175 cell.className = " fc-state-disabled";
10176 cell.title = cal.maxText;
10180 if(ddays.indexOf(d.getDay()) != -1){
10181 cell.title = ddaysText;
10182 cell.className = " fc-state-disabled";
10185 if(ddMatch && format){
10186 var fvalue = d.dateFormat(format);
10187 if(ddMatch.test(fvalue)){
10188 cell.title = ddText.replace("%0", fvalue);
10189 cell.className = " fc-state-disabled";
10193 if (!cell.initialClassName) {
10194 cell.initialClassName = cell.dom.className;
10197 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10202 for(; i < startingPos; i++) {
10203 textEls[i].innerHTML = (++prevStart);
10204 d.setDate(d.getDate()+1);
10206 cells[i].className = "fc-past fc-other-month";
10207 setCellClass(this, cells[i]);
10212 for(; i < days; i++){
10213 intDay = i - startingPos + 1;
10214 textEls[i].innerHTML = (intDay);
10215 d.setDate(d.getDate()+1);
10217 cells[i].className = ''; // "x-date-active";
10218 setCellClass(this, cells[i]);
10222 for(; i < 42; i++) {
10223 textEls[i].innerHTML = (++extraDays);
10224 d.setDate(d.getDate()+1);
10226 cells[i].className = "fc-future fc-other-month";
10227 setCellClass(this, cells[i]);
10230 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10232 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10234 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10235 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10237 if(totalRows != 6){
10238 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10239 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10242 this.fireEvent('monthchange', this, date);
10246 if(!this.internalRender){
10247 var main = this.el.dom.firstChild;
10248 var w = main.offsetWidth;
10249 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10250 Roo.fly(main).setWidth(w);
10251 this.internalRender = true;
10252 // opera does not respect the auto grow header center column
10253 // then, after it gets a width opera refuses to recalculate
10254 // without a second pass
10255 if(Roo.isOpera && !this.secondPass){
10256 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10257 this.secondPass = true;
10258 this.update.defer(10, this, [date]);
10265 findCell : function(dt) {
10266 dt = dt.clearTime().getTime();
10268 this.cells.each(function(c){
10269 //Roo.log("check " +c.dateValue + '?=' + dt);
10270 if(c.dateValue == dt){
10280 findCells : function(ev) {
10281 var s = ev.start.clone().clearTime().getTime();
10283 var e= ev.end.clone().clearTime().getTime();
10286 this.cells.each(function(c){
10287 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10289 if(c.dateValue > e){
10292 if(c.dateValue < s){
10301 findBestRow: function(cells)
10305 for (var i =0 ; i < cells.length;i++) {
10306 ret = Math.max(cells[i].rows || 0,ret);
10313 addItem : function(ev)
10315 // look for vertical location slot in
10316 var cells = this.findCells(ev);
10318 ev.row = this.findBestRow(cells);
10320 // work out the location.
10324 for(var i =0; i < cells.length; i++) {
10332 if (crow.start.getY() == cells[i].getY()) {
10334 crow.end = cells[i];
10350 for (var i = 0; i < cells.length;i++) {
10351 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10355 this.calevents.push(ev);
10358 clearEvents: function() {
10360 if(!this.calevents){
10364 Roo.each(this.cells.elements, function(c){
10368 Roo.each(this.calevents, function(e) {
10369 Roo.each(e.els, function(el) {
10370 el.un('mouseenter' ,this.onEventEnter, this);
10371 el.un('mouseleave' ,this.onEventLeave, this);
10378 renderEvents: function()
10380 // first make sure there is enough space..
10382 this.cells.each(function(c) {
10384 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10387 for (var e = 0; e < this.calevents.length; e++) {
10388 var ev = this.calevents[e];
10389 var cells = ev.cells;
10390 var rows = ev.rows;
10392 for(var i =0; i < rows.length; i++) {
10395 // how many rows should it span..
10398 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10399 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10401 unselectable : "on",
10404 cls: 'fc-event-inner',
10408 // cls: 'fc-event-time',
10409 // html : cells.length > 1 ? '' : ev.time
10413 cls: 'fc-event-title',
10414 html : String.format('{0}', ev.title)
10421 cls: 'ui-resizable-handle ui-resizable-e',
10422 html : '  '
10428 cfg.cls += ' fc-event-start';
10430 if ((i+1) == rows.length) {
10431 cfg.cls += ' fc-event-end';
10434 var ctr = this.el.select('.fc-event-container',true).first();
10435 var cg = ctr.createChild(cfg);
10437 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10438 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10439 cg.on('click', this.onEventClick, this, ev);
10443 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10444 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10446 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10447 cg.setWidth(ebox.right - sbox.x -2);
10455 onEventEnter: function (e, el,event,d) {
10456 this.fireEvent('evententer', this, el, event);
10459 onEventLeave: function (e, el,event,d) {
10460 this.fireEvent('eventleave', this, el, event);
10463 onEventClick: function (e, el,event,d) {
10464 this.fireEvent('eventclick', this, el, event);
10467 onMonthChange: function () {
10471 onLoad: function ()
10473 this.calevents = [];
10476 if(this.store.getCount() > 0){
10477 this.store.data.each(function(d){
10480 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10481 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10482 time : d.data.start_time,
10483 title : d.data.title,
10484 description : d.data.description,
10485 venue : d.data.venue
10490 this.renderEvents();
10493 this.maskEl.hide();
10497 onBeforeLoad: function()
10499 this.clearEvents();
10502 this.maskEl.show();
10516 * @class Roo.bootstrap.Popover
10517 * @extends Roo.bootstrap.Component
10518 * Bootstrap Popover class
10519 * @cfg {String} html contents of the popover (or false to use children..)
10520 * @cfg {String} title of popover (or false to hide)
10521 * @cfg {String} placement how it is placed
10522 * @cfg {String} trigger click || hover (or false to trigger manually)
10523 * @cfg {String} over what (parent or false to trigger manually.)
10526 * Create a new Popover
10527 * @param {Object} config The config object
10530 Roo.bootstrap.Popover = function(config){
10531 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10534 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10536 title: 'Fill in a title',
10539 placement : 'right',
10540 trigger : 'hover', // hover
10544 can_build_overlaid : false,
10546 getChildContainer : function()
10548 return this.el.select('.popover-content',true).first();
10551 getAutoCreate : function(){
10552 Roo.log('make popover?');
10554 cls : 'popover roo-dynamic',
10555 style: 'display:block',
10561 cls : 'popover-inner',
10565 cls: 'popover-title',
10569 cls : 'popover-content',
10580 setTitle: function(str)
10582 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10584 setContent: function(str)
10586 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10588 // as it get's added to the bottom of the page.
10589 onRender : function(ct, position)
10591 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10593 var cfg = Roo.apply({}, this.getAutoCreate());
10597 cfg.cls += ' ' + this.cls;
10600 cfg.style = this.style;
10602 Roo.log("adding to ")
10603 this.el = Roo.get(document.body).createChild(cfg, position);
10609 initEvents : function()
10611 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10612 this.el.enableDisplayMode('block');
10614 if (this.over === false) {
10617 if (this.triggers === false) {
10620 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10621 var triggers = this.trigger ? this.trigger.split(' ') : [];
10622 Roo.each(triggers, function(trigger) {
10624 if (trigger == 'click') {
10625 on_el.on('click', this.toggle, this);
10626 } else if (trigger != 'manual') {
10627 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10628 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10630 on_el.on(eventIn ,this.enter, this);
10631 on_el.on(eventOut, this.leave, this);
10642 toggle : function () {
10643 this.hoverState == 'in' ? this.leave() : this.enter();
10646 enter : function () {
10649 clearTimeout(this.timeout);
10651 this.hoverState = 'in'
10653 if (!this.delay || !this.delay.show) {
10658 this.timeout = setTimeout(function () {
10659 if (_t.hoverState == 'in') {
10662 }, this.delay.show)
10664 leave : function() {
10665 clearTimeout(this.timeout);
10667 this.hoverState = 'out'
10669 if (!this.delay || !this.delay.hide) {
10674 this.timeout = setTimeout(function () {
10675 if (_t.hoverState == 'out') {
10678 }, this.delay.hide)
10681 show : function (on_el)
10684 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10687 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10688 if (this.html !== false) {
10689 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10691 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10692 if (!this.title.length) {
10693 this.el.select('.popover-title',true).hide();
10696 var placement = typeof this.placement == 'function' ?
10697 this.placement.call(this, this.el, on_el) :
10700 var autoToken = /\s?auto?\s?/i;
10701 var autoPlace = autoToken.test(placement);
10703 placement = placement.replace(autoToken, '') || 'top';
10707 //this.el.setXY([0,0]);
10709 this.el.dom.style.display='block';
10710 this.el.addClass(placement);
10712 //this.el.appendTo(on_el);
10714 var p = this.getPosition();
10715 var box = this.el.getBox();
10720 var align = Roo.bootstrap.Popover.alignment[placement]
10721 this.el.alignTo(on_el, align[0],align[1]);
10722 //var arrow = this.el.select('.arrow',true).first();
10723 //arrow.set(align[2],
10725 this.el.addClass('in');
10726 this.hoverState = null;
10728 if (this.el.hasClass('fade')) {
10735 this.el.setXY([0,0]);
10736 this.el.removeClass('in');
10743 Roo.bootstrap.Popover.alignment = {
10744 'left' : ['r-l', [-10,0], 'right'],
10745 'right' : ['l-r', [10,0], 'left'],
10746 'bottom' : ['t-b', [0,10], 'top'],
10747 'top' : [ 'b-t', [0,-10], 'bottom']
10758 * @class Roo.bootstrap.Progress
10759 * @extends Roo.bootstrap.Component
10760 * Bootstrap Progress class
10761 * @cfg {Boolean} striped striped of the progress bar
10762 * @cfg {Boolean} active animated of the progress bar
10766 * Create a new Progress
10767 * @param {Object} config The config object
10770 Roo.bootstrap.Progress = function(config){
10771 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10774 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10779 getAutoCreate : function(){
10787 cfg.cls += ' progress-striped';
10791 cfg.cls += ' active';
10810 * @class Roo.bootstrap.ProgressBar
10811 * @extends Roo.bootstrap.Component
10812 * Bootstrap ProgressBar class
10813 * @cfg {Number} aria_valuenow aria-value now
10814 * @cfg {Number} aria_valuemin aria-value min
10815 * @cfg {Number} aria_valuemax aria-value max
10816 * @cfg {String} label label for the progress bar
10817 * @cfg {String} panel (success | info | warning | danger )
10818 * @cfg {String} role role of the progress bar
10819 * @cfg {String} sr_only text
10823 * Create a new ProgressBar
10824 * @param {Object} config The config object
10827 Roo.bootstrap.ProgressBar = function(config){
10828 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10831 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10835 aria_valuemax : 100,
10841 getAutoCreate : function()
10846 cls: 'progress-bar',
10847 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10859 cfg.role = this.role;
10862 if(this.aria_valuenow){
10863 cfg['aria-valuenow'] = this.aria_valuenow;
10866 if(this.aria_valuemin){
10867 cfg['aria-valuemin'] = this.aria_valuemin;
10870 if(this.aria_valuemax){
10871 cfg['aria-valuemax'] = this.aria_valuemax;
10874 if(this.label && !this.sr_only){
10875 cfg.html = this.label;
10879 cfg.cls += ' progress-bar-' + this.panel;
10885 update : function(aria_valuenow)
10887 this.aria_valuenow = aria_valuenow;
10889 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10904 * @class Roo.bootstrap.TabPanel
10905 * @extends Roo.bootstrap.Component
10906 * Bootstrap TabPanel class
10907 * @cfg {Boolean} active panel active
10908 * @cfg {String} html panel content
10909 * @cfg {String} tabId tab relate id
10913 * Create a new TabPanel
10914 * @param {Object} config The config object
10917 Roo.bootstrap.TabPanel = function(config){
10918 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10921 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10927 getAutoCreate : function(){
10931 html: this.html || ''
10935 cfg.cls += ' active';
10939 cfg.tabId = this.tabId;
10957 * @class Roo.bootstrap.DateField
10958 * @extends Roo.bootstrap.Input
10959 * Bootstrap DateField class
10960 * @cfg {Number} weekStart default 0
10961 * @cfg {Number} weekStart default 0
10962 * @cfg {Number} viewMode default empty, (months|years)
10963 * @cfg {Number} minViewMode default empty, (months|years)
10964 * @cfg {Number} startDate default -Infinity
10965 * @cfg {Number} endDate default Infinity
10966 * @cfg {Boolean} todayHighlight default false
10967 * @cfg {Boolean} todayBtn default false
10968 * @cfg {Boolean} calendarWeeks default false
10969 * @cfg {Object} daysOfWeekDisabled default empty
10971 * @cfg {Boolean} keyboardNavigation default true
10972 * @cfg {String} language default en
10975 * Create a new DateField
10976 * @param {Object} config The config object
10979 Roo.bootstrap.DateField = function(config){
10980 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10984 * Fires when this field show.
10985 * @param {Roo.bootstrap.DateField} this
10986 * @param {Mixed} date The date value
10991 * Fires when this field hide.
10992 * @param {Roo.bootstrap.DateField} this
10993 * @param {Mixed} date The date value
10998 * Fires when select a date.
10999 * @param {Roo.bootstrap.DateField} this
11000 * @param {Mixed} date The date value
11006 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
11009 * @cfg {String} format
11010 * The default date format string which can be overriden for localization support. The format must be
11011 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11015 * @cfg {String} altFormats
11016 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11017 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11019 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11027 todayHighlight : false,
11033 keyboardNavigation: true,
11035 calendarWeeks: false,
11037 startDate: -Infinity,
11041 daysOfWeekDisabled: [],
11045 UTCDate: function()
11047 return new Date(Date.UTC.apply(Date, arguments));
11050 UTCToday: function()
11052 var today = new Date();
11053 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11056 getDate: function() {
11057 var d = this.getUTCDate();
11058 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11061 getUTCDate: function() {
11065 setDate: function(d) {
11066 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11069 setUTCDate: function(d) {
11071 this.setValue(this.formatDate(this.date));
11074 onRender: function(ct, position)
11077 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11079 this.language = this.language || 'en';
11080 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11081 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11083 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11084 this.format = this.format || 'm/d/y';
11085 this.isInline = false;
11086 this.isInput = true;
11087 this.component = this.el.select('.add-on', true).first() || false;
11088 this.component = (this.component && this.component.length === 0) ? false : this.component;
11089 this.hasInput = this.component && this.inputEL().length;
11091 if (typeof(this.minViewMode === 'string')) {
11092 switch (this.minViewMode) {
11094 this.minViewMode = 1;
11097 this.minViewMode = 2;
11100 this.minViewMode = 0;
11105 if (typeof(this.viewMode === 'string')) {
11106 switch (this.viewMode) {
11119 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11121 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11123 this.picker().on('mousedown', this.onMousedown, this);
11124 this.picker().on('click', this.onClick, this);
11126 this.picker().addClass('datepicker-dropdown');
11128 this.startViewMode = this.viewMode;
11131 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11132 if(!this.calendarWeeks){
11137 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11138 v.attr('colspan', function(i, val){
11139 return parseInt(val) + 1;
11144 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11146 this.setStartDate(this.startDate);
11147 this.setEndDate(this.endDate);
11149 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11156 if(this.isInline) {
11161 picker : function()
11163 return this.el.select('.datepicker', true).first();
11166 fillDow: function()
11168 var dowCnt = this.weekStart;
11177 if(this.calendarWeeks){
11185 while (dowCnt < this.weekStart + 7) {
11189 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11193 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11196 fillMonths: function()
11199 var months = this.picker().select('>.datepicker-months td', true).first();
11201 months.dom.innerHTML = '';
11207 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11210 months.createChild(month);
11215 update: function(){
11217 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11219 if (this.date < this.startDate) {
11220 this.viewDate = new Date(this.startDate);
11221 } else if (this.date > this.endDate) {
11222 this.viewDate = new Date(this.endDate);
11224 this.viewDate = new Date(this.date);
11231 var d = new Date(this.viewDate),
11232 year = d.getUTCFullYear(),
11233 month = d.getUTCMonth(),
11234 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11235 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11236 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11237 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11238 currentDate = this.date && this.date.valueOf(),
11239 today = this.UTCToday();
11241 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11243 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11245 // this.picker.select('>tfoot th.today').
11246 // .text(dates[this.language].today)
11247 // .toggle(this.todayBtn !== false);
11249 this.updateNavArrows();
11252 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11254 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11256 prevMonth.setUTCDate(day);
11258 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11260 var nextMonth = new Date(prevMonth);
11262 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11264 nextMonth = nextMonth.valueOf();
11266 var fillMonths = false;
11268 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11270 while(prevMonth.valueOf() < nextMonth) {
11273 if (prevMonth.getUTCDay() === this.weekStart) {
11275 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11283 if(this.calendarWeeks){
11284 // ISO 8601: First week contains first thursday.
11285 // ISO also states week starts on Monday, but we can be more abstract here.
11287 // Start of current week: based on weekstart/current date
11288 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11289 // Thursday of this week
11290 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11291 // First Thursday of year, year from thursday
11292 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11293 // Calendar week: ms between thursdays, div ms per day, div 7 days
11294 calWeek = (th - yth) / 864e5 / 7 + 1;
11296 fillMonths.cn.push({
11304 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11306 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11309 if (this.todayHighlight &&
11310 prevMonth.getUTCFullYear() == today.getFullYear() &&
11311 prevMonth.getUTCMonth() == today.getMonth() &&
11312 prevMonth.getUTCDate() == today.getDate()) {
11313 clsName += ' today';
11316 if (currentDate && prevMonth.valueOf() === currentDate) {
11317 clsName += ' active';
11320 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11321 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11322 clsName += ' disabled';
11325 fillMonths.cn.push({
11327 cls: 'day ' + clsName,
11328 html: prevMonth.getDate()
11331 prevMonth.setDate(prevMonth.getDate()+1);
11334 var currentYear = this.date && this.date.getUTCFullYear();
11335 var currentMonth = this.date && this.date.getUTCMonth();
11337 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11339 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11340 v.removeClass('active');
11342 if(currentYear === year && k === currentMonth){
11343 v.addClass('active');
11346 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11347 v.addClass('disabled');
11353 year = parseInt(year/10, 10) * 10;
11355 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11357 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11360 for (var i = -1; i < 11; i++) {
11361 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11363 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11371 showMode: function(dir) {
11373 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11375 Roo.each(this.picker().select('>div',true).elements, function(v){
11376 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11379 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11384 if(this.isInline) return;
11386 this.picker().removeClass(['bottom', 'top']);
11388 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11390 * place to the top of element!
11394 this.picker().addClass('top');
11395 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11400 this.picker().addClass('bottom');
11402 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11405 parseDate : function(value){
11406 if(!value || value instanceof Date){
11409 var v = Date.parseDate(value, this.format);
11410 if (!v && this.useIso) {
11411 v = Date.parseDate(value, 'Y-m-d');
11413 if(!v && this.altFormats){
11414 if(!this.altFormatsArray){
11415 this.altFormatsArray = this.altFormats.split("|");
11417 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11418 v = Date.parseDate(value, this.altFormatsArray[i]);
11424 formatDate : function(date, fmt){
11425 return (!date || !(date instanceof Date)) ?
11426 date : date.dateFormat(fmt || this.format);
11429 onFocus : function()
11431 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11435 onBlur : function()
11437 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11443 this.picker().show();
11447 this.fireEvent('show', this, this.date);
11452 if(this.isInline) return;
11453 this.picker().hide();
11454 this.viewMode = this.startViewMode;
11457 this.fireEvent('hide', this, this.date);
11461 onMousedown: function(e){
11462 e.stopPropagation();
11463 e.preventDefault();
11466 keyup: function(e){
11467 Roo.bootstrap.DateField.superclass.keyup.call(this);
11472 setValue: function(v){
11473 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11475 this.fireEvent('select', this, this.date);
11479 fireKey: function(e){
11480 if (!this.picker().isVisible()){
11481 if (e.keyCode == 27) // allow escape to hide and re-show picker
11485 var dateChanged = false,
11487 newDate, newViewDate;
11491 e.preventDefault();
11495 if (!this.keyboardNavigation) break;
11496 dir = e.keyCode == 37 ? -1 : 1;
11499 newDate = this.moveYear(this.date, dir);
11500 newViewDate = this.moveYear(this.viewDate, dir);
11501 } else if (e.shiftKey){
11502 newDate = this.moveMonth(this.date, dir);
11503 newViewDate = this.moveMonth(this.viewDate, dir);
11505 newDate = new Date(this.date);
11506 newDate.setUTCDate(this.date.getUTCDate() + dir);
11507 newViewDate = new Date(this.viewDate);
11508 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11510 if (this.dateWithinRange(newDate)){
11511 this.date = newDate;
11512 this.viewDate = newViewDate;
11513 this.setValue(this.formatDate(this.date));
11515 e.preventDefault();
11516 dateChanged = true;
11521 if (!this.keyboardNavigation) break;
11522 dir = e.keyCode == 38 ? -1 : 1;
11524 newDate = this.moveYear(this.date, dir);
11525 newViewDate = this.moveYear(this.viewDate, dir);
11526 } else if (e.shiftKey){
11527 newDate = this.moveMonth(this.date, dir);
11528 newViewDate = this.moveMonth(this.viewDate, dir);
11530 newDate = new Date(this.date);
11531 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11532 newViewDate = new Date(this.viewDate);
11533 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11535 if (this.dateWithinRange(newDate)){
11536 this.date = newDate;
11537 this.viewDate = newViewDate;
11538 this.setValue(this.formatDate(this.date));
11540 e.preventDefault();
11541 dateChanged = true;
11545 this.setValue(this.formatDate(this.date));
11547 e.preventDefault();
11550 this.setValue(this.formatDate(this.date));
11557 onClick: function(e) {
11558 e.stopPropagation();
11559 e.preventDefault();
11561 var target = e.getTarget();
11563 if(target.nodeName.toLowerCase() === 'i'){
11564 target = Roo.get(target).dom.parentNode;
11567 var nodeName = target.nodeName;
11568 var className = target.className;
11569 var html = target.innerHTML;
11571 switch(nodeName.toLowerCase()) {
11573 switch(className) {
11579 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11580 switch(this.viewMode){
11582 this.viewDate = this.moveMonth(this.viewDate, dir);
11586 this.viewDate = this.moveYear(this.viewDate, dir);
11592 var date = new Date();
11593 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11595 this.setValue(this.formatDate(this.date));
11601 if (className.indexOf('disabled') === -1) {
11602 this.viewDate.setUTCDate(1);
11603 if (className.indexOf('month') !== -1) {
11604 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11606 var year = parseInt(html, 10) || 0;
11607 this.viewDate.setUTCFullYear(year);
11616 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11617 var day = parseInt(html, 10) || 1;
11618 var year = this.viewDate.getUTCFullYear(),
11619 month = this.viewDate.getUTCMonth();
11621 if (className.indexOf('old') !== -1) {
11628 } else if (className.indexOf('new') !== -1) {
11636 this.date = this.UTCDate(year, month, day,0,0,0,0);
11637 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11639 this.setValue(this.formatDate(this.date));
11646 setStartDate: function(startDate){
11647 this.startDate = startDate || -Infinity;
11648 if (this.startDate !== -Infinity) {
11649 this.startDate = this.parseDate(this.startDate);
11652 this.updateNavArrows();
11655 setEndDate: function(endDate){
11656 this.endDate = endDate || Infinity;
11657 if (this.endDate !== Infinity) {
11658 this.endDate = this.parseDate(this.endDate);
11661 this.updateNavArrows();
11664 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11665 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11666 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11667 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11669 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11670 return parseInt(d, 10);
11673 this.updateNavArrows();
11676 updateNavArrows: function() {
11677 var d = new Date(this.viewDate),
11678 year = d.getUTCFullYear(),
11679 month = d.getUTCMonth();
11681 Roo.each(this.picker().select('.prev', true).elements, function(v){
11683 switch (this.viewMode) {
11686 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11692 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11699 Roo.each(this.picker().select('.next', true).elements, function(v){
11701 switch (this.viewMode) {
11704 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11710 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11718 moveMonth: function(date, dir){
11719 if (!dir) return date;
11720 var new_date = new Date(date.valueOf()),
11721 day = new_date.getUTCDate(),
11722 month = new_date.getUTCMonth(),
11723 mag = Math.abs(dir),
11725 dir = dir > 0 ? 1 : -1;
11728 // If going back one month, make sure month is not current month
11729 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11731 return new_date.getUTCMonth() == month;
11733 // If going forward one month, make sure month is as expected
11734 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11736 return new_date.getUTCMonth() != new_month;
11738 new_month = month + dir;
11739 new_date.setUTCMonth(new_month);
11740 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11741 if (new_month < 0 || new_month > 11)
11742 new_month = (new_month + 12) % 12;
11744 // For magnitudes >1, move one month at a time...
11745 for (var i=0; i<mag; i++)
11746 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11747 new_date = this.moveMonth(new_date, dir);
11748 // ...then reset the day, keeping it in the new month
11749 new_month = new_date.getUTCMonth();
11750 new_date.setUTCDate(day);
11752 return new_month != new_date.getUTCMonth();
11755 // Common date-resetting loop -- if date is beyond end of month, make it
11758 new_date.setUTCDate(--day);
11759 new_date.setUTCMonth(new_month);
11764 moveYear: function(date, dir){
11765 return this.moveMonth(date, dir*12);
11768 dateWithinRange: function(date){
11769 return date >= this.startDate && date <= this.endDate;
11773 remove: function() {
11774 this.picker().remove();
11779 Roo.apply(Roo.bootstrap.DateField, {
11790 html: '<i class="icon-arrow-left"/>'
11800 html: '<i class="icon-arrow-right"/>'
11842 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11843 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11844 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11845 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11846 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11859 navFnc: 'FullYear',
11864 navFnc: 'FullYear',
11869 Roo.apply(Roo.bootstrap.DateField, {
11873 cls: 'datepicker dropdown-menu',
11877 cls: 'datepicker-days',
11881 cls: 'table-condensed',
11883 Roo.bootstrap.DateField.head,
11887 Roo.bootstrap.DateField.footer
11894 cls: 'datepicker-months',
11898 cls: 'table-condensed',
11900 Roo.bootstrap.DateField.head,
11901 Roo.bootstrap.DateField.content,
11902 Roo.bootstrap.DateField.footer
11909 cls: 'datepicker-years',
11913 cls: 'table-condensed',
11915 Roo.bootstrap.DateField.head,
11916 Roo.bootstrap.DateField.content,
11917 Roo.bootstrap.DateField.footer
11936 * @class Roo.bootstrap.TimeField
11937 * @extends Roo.bootstrap.Input
11938 * Bootstrap DateField class
11942 * Create a new TimeField
11943 * @param {Object} config The config object
11946 Roo.bootstrap.TimeField = function(config){
11947 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11951 * Fires when this field show.
11952 * @param {Roo.bootstrap.DateField} this
11953 * @param {Mixed} date The date value
11958 * Fires when this field hide.
11959 * @param {Roo.bootstrap.DateField} this
11960 * @param {Mixed} date The date value
11965 * Fires when select a date.
11966 * @param {Roo.bootstrap.DateField} this
11967 * @param {Mixed} date The date value
11973 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
11976 * @cfg {String} format
11977 * The default time format string which can be overriden for localization support. The format must be
11978 * valid according to {@link Date#parseDate} (defaults to 'H:i').
11982 onRender: function(ct, position)
11985 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11987 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11989 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11991 this.pop = this.picker().select('>.datepicker-time',true).first();
11992 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
11994 this.picker().on('mousedown', this.onMousedown, this);
11995 this.picker().on('click', this.onClick, this);
11997 this.picker().addClass('datepicker-dropdown');
12002 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
12003 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
12004 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
12005 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
12006 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
12007 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12011 fireKey: function(e){
12012 if (!this.picker().isVisible()){
12013 if (e.keyCode == 27) // allow escape to hide and re-show picker
12018 e.preventDefault();
12026 this.onTogglePeriod();
12029 this.onIncrementMinutes();
12032 this.onDecrementMinutes();
12041 onClick: function(e) {
12042 e.stopPropagation();
12043 e.preventDefault();
12046 picker : function()
12048 return this.el.select('.datepicker', true).first();
12051 fillTime: function()
12053 var time = this.pop.select('tbody', true).first();
12055 time.dom.innerHTML = '';
12070 cls: 'hours-up glyphicon glyphicon-chevron-up'
12090 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12111 cls: 'timepicker-hour',
12126 cls: 'timepicker-minute',
12141 cls: 'btn btn-primary period',
12163 cls: 'hours-down glyphicon glyphicon-chevron-down'
12183 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12201 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12208 var hours = this.time.getHours();
12209 var minutes = this.time.getMinutes();
12222 hours = hours - 12;
12226 hours = '0' + hours;
12230 minutes = '0' + minutes;
12233 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12234 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12235 this.pop.select('button', true).first().dom.innerHTML = period;
12241 this.picker().removeClass(['bottom', 'top']);
12243 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12245 * place to the top of element!
12249 this.picker().addClass('top');
12250 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12255 this.picker().addClass('bottom');
12257 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12260 onFocus : function()
12262 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12266 onBlur : function()
12268 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12274 this.picker().show();
12279 this.fireEvent('show', this, this.date);
12284 this.picker().hide();
12287 this.fireEvent('hide', this, this.date);
12290 setTime : function()
12293 this.setValue(this.time.format(this.format));
12295 this.fireEvent('select', this, this.date);
12300 onMousedown: function(e){
12301 e.stopPropagation();
12302 e.preventDefault();
12305 onIncrementHours: function()
12307 Roo.log('onIncrementHours');
12308 this.time = this.time.add(Date.HOUR, 1);
12313 onDecrementHours: function()
12315 Roo.log('onDecrementHours');
12316 this.time = this.time.add(Date.HOUR, -1);
12320 onIncrementMinutes: function()
12322 Roo.log('onIncrementMinutes');
12323 this.time = this.time.add(Date.MINUTE, 1);
12327 onDecrementMinutes: function()
12329 Roo.log('onDecrementMinutes');
12330 this.time = this.time.add(Date.MINUTE, -1);
12334 onTogglePeriod: function()
12336 Roo.log('onTogglePeriod');
12337 this.time = this.time.add(Date.HOUR, 12);
12344 Roo.apply(Roo.bootstrap.TimeField, {
12374 cls: 'btn btn-info ok',
12386 Roo.apply(Roo.bootstrap.TimeField, {
12390 cls: 'datepicker dropdown-menu',
12394 cls: 'datepicker-time',
12398 cls: 'table-condensed',
12400 Roo.bootstrap.TimeField.content,
12401 Roo.bootstrap.TimeField.footer
12420 * @class Roo.bootstrap.CheckBox
12421 * @extends Roo.bootstrap.Input
12422 * Bootstrap CheckBox class
12424 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12425 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12426 * @cfg {String} boxLabel The text that appears beside the checkbox
12427 * @cfg {Boolean} checked initnal the element
12430 * Create a new CheckBox
12431 * @param {Object} config The config object
12434 Roo.bootstrap.CheckBox = function(config){
12435 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12440 * Fires when the element is checked or unchecked.
12441 * @param {Roo.bootstrap.CheckBox} this This input
12442 * @param {Boolean} checked The new checked value
12448 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12450 inputType: 'checkbox',
12456 getAutoCreate : function()
12458 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12464 cfg.cls = 'form-group' //input-group
12469 type : this.inputType,
12470 value : (!this.checked) ? this.valueOff : this.inputValue,
12472 placeholder : this.placeholder || ''
12476 if (this.disabled) {
12477 input.disabled=true;
12481 input.checked = this.checked;
12485 input.name = this.name;
12489 input.cls += ' input-' + this.size;
12493 ['xs','sm','md','lg'].map(function(size){
12494 if (settings[size]) {
12495 cfg.cls += ' col-' + size + '-' + settings[size];
12499 var inputblock = input;
12501 if (this.before || this.after) {
12504 cls : 'input-group',
12508 inputblock.cn.push({
12510 cls : 'input-group-addon',
12514 inputblock.cn.push(input);
12516 inputblock.cn.push({
12518 cls : 'input-group-addon',
12525 if (align ==='left' && this.fieldLabel.length) {
12526 Roo.log("left and has label");
12532 cls : 'control-label col-md-' + this.labelWidth,
12533 html : this.fieldLabel
12537 cls : "col-md-" + (12 - this.labelWidth),
12544 } else if ( this.fieldLabel.length) {
12549 tag: this.boxLabel ? 'span' : 'label',
12551 cls: 'control-label box-input-label',
12552 //cls : 'input-group-addon',
12553 html : this.fieldLabel
12563 Roo.log(" no label && no align");
12578 html: this.boxLabel
12587 * return the real input element.
12589 inputEl: function ()
12591 return this.el.select('input.form-box',true).first();
12594 initEvents : function()
12596 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12598 this.inputEl().on('click', this.onClick, this);
12602 onClick : function()
12604 this.setChecked(!this.checked);
12607 setChecked : function(state,suppressEvent)
12609 this.checked = state;
12611 this.inputEl().dom.checked = state;
12613 if(suppressEvent !== true){
12614 this.fireEvent('check', this, state);
12617 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12621 setValue : function(v,suppressEvent)
12623 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12637 * @class Roo.bootstrap.Radio
12638 * @extends Roo.bootstrap.CheckBox
12639 * Bootstrap Radio class
12642 * Create a new Radio
12643 * @param {Object} config The config object
12646 Roo.bootstrap.Radio = function(config){
12647 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12651 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12653 inputType: 'radio',
12657 getAutoCreate : function()
12659 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12665 cfg.cls = 'form-group' //input-group
12670 type : this.inputType,
12671 value : (!this.checked) ? this.valueOff : this.inputValue,
12673 placeholder : this.placeholder || ''
12677 if (this.disabled) {
12678 input.disabled=true;
12682 input.checked = this.checked;
12686 input.name = this.name;
12690 input.cls += ' input-' + this.size;
12694 ['xs','sm','md','lg'].map(function(size){
12695 if (settings[size]) {
12696 cfg.cls += ' col-' + size + '-' + settings[size];
12700 var inputblock = input;
12702 if (this.before || this.after) {
12705 cls : 'input-group',
12709 inputblock.cn.push({
12711 cls : 'input-group-addon',
12715 inputblock.cn.push(input);
12717 inputblock.cn.push({
12719 cls : 'input-group-addon',
12726 if (align ==='left' && this.fieldLabel.length) {
12727 Roo.log("left and has label");
12733 cls : 'control-label col-md-' + this.labelWidth,
12734 html : this.fieldLabel
12738 cls : "col-md-" + (12 - this.labelWidth),
12745 } else if ( this.fieldLabel.length) {
12752 cls: 'control-label box-input-label',
12753 //cls : 'input-group-addon',
12754 html : this.fieldLabel
12764 Roo.log(" no label && no align");
12779 html: this.boxLabel
12787 onClick : function()
12789 this.setChecked(true);
12792 setChecked : function(state,suppressEvent)
12795 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12796 v.dom.checked = false;
12800 this.checked = state;
12801 this.inputEl().dom.checked = state;
12803 if(suppressEvent !== true){
12804 this.fireEvent('check', this, state);
12807 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12811 getGroupValue : function()
12814 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12815 if(v.dom.checked == true){
12816 value = v.dom.value;
12824 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12825 * @return {Mixed} value The field value
12827 getValue : function(){
12828 return this.getGroupValue();
12834 //<script type="text/javascript">
12837 * Based Ext JS Library 1.1.1
12838 * Copyright(c) 2006-2007, Ext JS, LLC.
12844 * @class Roo.HtmlEditorCore
12845 * @extends Roo.Component
12846 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12848 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12851 Roo.HtmlEditorCore = function(config){
12854 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12857 * @event initialize
12858 * Fires when the editor is fully initialized (including the iframe)
12859 * @param {Roo.HtmlEditorCore} this
12864 * Fires when the editor is first receives the focus. Any insertion must wait
12865 * until after this event.
12866 * @param {Roo.HtmlEditorCore} this
12870 * @event beforesync
12871 * Fires before the textarea is updated with content from the editor iframe. Return false
12872 * to cancel the sync.
12873 * @param {Roo.HtmlEditorCore} this
12874 * @param {String} html
12878 * @event beforepush
12879 * Fires before the iframe editor is updated with content from the textarea. Return false
12880 * to cancel the push.
12881 * @param {Roo.HtmlEditorCore} this
12882 * @param {String} html
12887 * Fires when the textarea is updated with content from the editor iframe.
12888 * @param {Roo.HtmlEditorCore} this
12889 * @param {String} html
12894 * Fires when the iframe editor is updated with content from the textarea.
12895 * @param {Roo.HtmlEditorCore} this
12896 * @param {String} html
12901 * @event editorevent
12902 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12903 * @param {Roo.HtmlEditorCore} this
12911 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12915 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12921 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12926 * @cfg {Number} height (in pixels)
12930 * @cfg {Number} width (in pixels)
12935 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12938 stylesheets: false,
12943 // private properties
12944 validationEvent : false,
12946 initialized : false,
12948 sourceEditMode : false,
12949 onFocus : Roo.emptyFn,
12951 hideMode:'offsets',
12959 * Protected method that will not generally be called directly. It
12960 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12961 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12963 getDocMarkup : function(){
12966 Roo.log(this.stylesheets);
12968 // inherit styels from page...??
12969 if (this.stylesheets === false) {
12971 Roo.get(document.head).select('style').each(function(node) {
12972 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12975 Roo.get(document.head).select('link').each(function(node) {
12976 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12979 } else if (!this.stylesheets.length) {
12981 st = '<style type="text/css">' +
12982 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12985 Roo.each(this.stylesheets, function(s) {
12986 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12991 st += '<style type="text/css">' +
12992 'IMG { cursor: pointer } ' +
12996 return '<html><head>' + st +
12997 //<style type="text/css">' +
12998 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13000 ' </head><body class="roo-htmleditor-body"></body></html>';
13004 onRender : function(ct, position)
13007 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13008 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13011 this.el.dom.style.border = '0 none';
13012 this.el.dom.setAttribute('tabIndex', -1);
13013 this.el.addClass('x-hidden hide');
13017 if(Roo.isIE){ // fix IE 1px bogus margin
13018 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13022 this.frameId = Roo.id();
13026 var iframe = this.owner.wrap.createChild({
13028 cls: 'form-control', // bootstrap..
13030 name: this.frameId,
13031 frameBorder : 'no',
13032 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13037 this.iframe = iframe.dom;
13039 this.assignDocWin();
13041 this.doc.designMode = 'on';
13044 this.doc.write(this.getDocMarkup());
13048 var task = { // must defer to wait for browser to be ready
13050 //console.log("run task?" + this.doc.readyState);
13051 this.assignDocWin();
13052 if(this.doc.body || this.doc.readyState == 'complete'){
13054 this.doc.designMode="on";
13058 Roo.TaskMgr.stop(task);
13059 this.initEditor.defer(10, this);
13066 Roo.TaskMgr.start(task);
13073 onResize : function(w, h)
13075 Roo.log('resize: ' +w + ',' + h );
13076 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13080 if(typeof w == 'number'){
13082 this.iframe.style.width = w + 'px';
13084 if(typeof h == 'number'){
13086 this.iframe.style.height = h + 'px';
13088 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13095 * Toggles the editor between standard and source edit mode.
13096 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13098 toggleSourceEdit : function(sourceEditMode){
13100 this.sourceEditMode = sourceEditMode === true;
13102 if(this.sourceEditMode){
13104 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13107 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13108 //this.iframe.className = '';
13111 //this.setSize(this.owner.wrap.getSize());
13112 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13119 * Protected method that will not generally be called directly. If you need/want
13120 * custom HTML cleanup, this is the method you should override.
13121 * @param {String} html The HTML to be cleaned
13122 * return {String} The cleaned HTML
13124 cleanHtml : function(html){
13125 html = String(html);
13126 if(html.length > 5){
13127 if(Roo.isSafari){ // strip safari nonsense
13128 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13131 if(html == ' '){
13138 * HTML Editor -> Textarea
13139 * Protected method that will not generally be called directly. Syncs the contents
13140 * of the editor iframe with the textarea.
13142 syncValue : function(){
13143 if(this.initialized){
13144 var bd = (this.doc.body || this.doc.documentElement);
13145 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13146 var html = bd.innerHTML;
13148 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13149 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13151 html = '<div style="'+m[0]+'">' + html + '</div>';
13154 html = this.cleanHtml(html);
13155 // fix up the special chars.. normaly like back quotes in word...
13156 // however we do not want to do this with chinese..
13157 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13158 var cc = b.charCodeAt();
13160 (cc >= 0x4E00 && cc < 0xA000 ) ||
13161 (cc >= 0x3400 && cc < 0x4E00 ) ||
13162 (cc >= 0xf900 && cc < 0xfb00 )
13168 if(this.owner.fireEvent('beforesync', this, html) !== false){
13169 this.el.dom.value = html;
13170 this.owner.fireEvent('sync', this, html);
13176 * Protected method that will not generally be called directly. Pushes the value of the textarea
13177 * into the iframe editor.
13179 pushValue : function(){
13180 if(this.initialized){
13181 var v = this.el.dom.value.trim();
13183 // if(v.length < 1){
13187 if(this.owner.fireEvent('beforepush', this, v) !== false){
13188 var d = (this.doc.body || this.doc.documentElement);
13190 this.cleanUpPaste();
13191 this.el.dom.value = d.innerHTML;
13192 this.owner.fireEvent('push', this, v);
13198 deferFocus : function(){
13199 this.focus.defer(10, this);
13203 focus : function(){
13204 if(this.win && !this.sourceEditMode){
13211 assignDocWin: function()
13213 var iframe = this.iframe;
13216 this.doc = iframe.contentWindow.document;
13217 this.win = iframe.contentWindow;
13219 if (!Roo.get(this.frameId)) {
13222 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13223 this.win = Roo.get(this.frameId).dom.contentWindow;
13228 initEditor : function(){
13229 //console.log("INIT EDITOR");
13230 this.assignDocWin();
13234 this.doc.designMode="on";
13236 this.doc.write(this.getDocMarkup());
13239 var dbody = (this.doc.body || this.doc.documentElement);
13240 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13241 // this copies styles from the containing element into thsi one..
13242 // not sure why we need all of this..
13243 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13244 ss['background-attachment'] = 'fixed'; // w3c
13245 dbody.bgProperties = 'fixed'; // ie
13246 Roo.DomHelper.applyStyles(dbody, ss);
13247 Roo.EventManager.on(this.doc, {
13248 //'mousedown': this.onEditorEvent,
13249 'mouseup': this.onEditorEvent,
13250 'dblclick': this.onEditorEvent,
13251 'click': this.onEditorEvent,
13252 'keyup': this.onEditorEvent,
13257 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13259 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13260 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13262 this.initialized = true;
13264 this.owner.fireEvent('initialize', this);
13269 onDestroy : function(){
13275 //for (var i =0; i < this.toolbars.length;i++) {
13276 // // fixme - ask toolbars for heights?
13277 // this.toolbars[i].onDestroy();
13280 //this.wrap.dom.innerHTML = '';
13281 //this.wrap.remove();
13286 onFirstFocus : function(){
13288 this.assignDocWin();
13291 this.activated = true;
13294 if(Roo.isGecko){ // prevent silly gecko errors
13296 var s = this.win.getSelection();
13297 if(!s.focusNode || s.focusNode.nodeType != 3){
13298 var r = s.getRangeAt(0);
13299 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13304 this.execCmd('useCSS', true);
13305 this.execCmd('styleWithCSS', false);
13308 this.owner.fireEvent('activate', this);
13312 adjustFont: function(btn){
13313 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13314 //if(Roo.isSafari){ // safari
13317 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13318 if(Roo.isSafari){ // safari
13319 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13320 v = (v < 10) ? 10 : v;
13321 v = (v > 48) ? 48 : v;
13322 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13327 v = Math.max(1, v+adjust);
13329 this.execCmd('FontSize', v );
13332 onEditorEvent : function(e){
13333 this.owner.fireEvent('editorevent', this, e);
13334 // this.updateToolbar();
13335 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13338 insertTag : function(tg)
13340 // could be a bit smarter... -> wrap the current selected tRoo..
13341 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13343 range = this.createRange(this.getSelection());
13344 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13345 wrappingNode.appendChild(range.extractContents());
13346 range.insertNode(wrappingNode);
13353 this.execCmd("formatblock", tg);
13357 insertText : function(txt)
13361 var range = this.createRange();
13362 range.deleteContents();
13363 //alert(Sender.getAttribute('label'));
13365 range.insertNode(this.doc.createTextNode(txt));
13371 * Executes a Midas editor command on the editor document and performs necessary focus and
13372 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13373 * @param {String} cmd The Midas command
13374 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13376 relayCmd : function(cmd, value){
13378 this.execCmd(cmd, value);
13379 this.owner.fireEvent('editorevent', this);
13380 //this.updateToolbar();
13381 this.owner.deferFocus();
13385 * Executes a Midas editor command directly on the editor document.
13386 * For visual commands, you should use {@link #relayCmd} instead.
13387 * <b>This should only be called after the editor is initialized.</b>
13388 * @param {String} cmd The Midas command
13389 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13391 execCmd : function(cmd, value){
13392 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13399 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13401 * @param {String} text | dom node..
13403 insertAtCursor : function(text)
13408 if(!this.activated){
13414 var r = this.doc.selection.createRange();
13425 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13429 // from jquery ui (MIT licenced)
13431 var win = this.win;
13433 if (win.getSelection && win.getSelection().getRangeAt) {
13434 range = win.getSelection().getRangeAt(0);
13435 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13436 range.insertNode(node);
13437 } else if (win.document.selection && win.document.selection.createRange) {
13438 // no firefox support
13439 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13440 win.document.selection.createRange().pasteHTML(txt);
13442 // no firefox support
13443 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13444 this.execCmd('InsertHTML', txt);
13453 mozKeyPress : function(e){
13455 var c = e.getCharCode(), cmd;
13458 c = String.fromCharCode(c).toLowerCase();
13472 this.cleanUpPaste.defer(100, this);
13480 e.preventDefault();
13488 fixKeys : function(){ // load time branching for fastest keydown performance
13490 return function(e){
13491 var k = e.getKey(), r;
13494 r = this.doc.selection.createRange();
13497 r.pasteHTML('    ');
13504 r = this.doc.selection.createRange();
13506 var target = r.parentElement();
13507 if(!target || target.tagName.toLowerCase() != 'li'){
13509 r.pasteHTML('<br />');
13515 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13516 this.cleanUpPaste.defer(100, this);
13522 }else if(Roo.isOpera){
13523 return function(e){
13524 var k = e.getKey();
13528 this.execCmd('InsertHTML','    ');
13531 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13532 this.cleanUpPaste.defer(100, this);
13537 }else if(Roo.isSafari){
13538 return function(e){
13539 var k = e.getKey();
13543 this.execCmd('InsertText','\t');
13547 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13548 this.cleanUpPaste.defer(100, this);
13556 getAllAncestors: function()
13558 var p = this.getSelectedNode();
13561 a.push(p); // push blank onto stack..
13562 p = this.getParentElement();
13566 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13570 a.push(this.doc.body);
13574 lastSelNode : false,
13577 getSelection : function()
13579 this.assignDocWin();
13580 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13583 getSelectedNode: function()
13585 // this may only work on Gecko!!!
13587 // should we cache this!!!!
13592 var range = this.createRange(this.getSelection()).cloneRange();
13595 var parent = range.parentElement();
13597 var testRange = range.duplicate();
13598 testRange.moveToElementText(parent);
13599 if (testRange.inRange(range)) {
13602 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13605 parent = parent.parentElement;
13610 // is ancestor a text element.
13611 var ac = range.commonAncestorContainer;
13612 if (ac.nodeType == 3) {
13613 ac = ac.parentNode;
13616 var ar = ac.childNodes;
13619 var other_nodes = [];
13620 var has_other_nodes = false;
13621 for (var i=0;i<ar.length;i++) {
13622 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13625 // fullly contained node.
13627 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13632 // probably selected..
13633 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13634 other_nodes.push(ar[i]);
13638 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13643 has_other_nodes = true;
13645 if (!nodes.length && other_nodes.length) {
13646 nodes= other_nodes;
13648 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13654 createRange: function(sel)
13656 // this has strange effects when using with
13657 // top toolbar - not sure if it's a great idea.
13658 //this.editor.contentWindow.focus();
13659 if (typeof sel != "undefined") {
13661 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13663 return this.doc.createRange();
13666 return this.doc.createRange();
13669 getParentElement: function()
13672 this.assignDocWin();
13673 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13675 var range = this.createRange(sel);
13678 var p = range.commonAncestorContainer;
13679 while (p.nodeType == 3) { // text node
13690 * Range intersection.. the hard stuff...
13694 * [ -- selected range --- ]
13698 * if end is before start or hits it. fail.
13699 * if start is after end or hits it fail.
13701 * if either hits (but other is outside. - then it's not
13707 // @see http://www.thismuchiknow.co.uk/?p=64.
13708 rangeIntersectsNode : function(range, node)
13710 var nodeRange = node.ownerDocument.createRange();
13712 nodeRange.selectNode(node);
13714 nodeRange.selectNodeContents(node);
13717 var rangeStartRange = range.cloneRange();
13718 rangeStartRange.collapse(true);
13720 var rangeEndRange = range.cloneRange();
13721 rangeEndRange.collapse(false);
13723 var nodeStartRange = nodeRange.cloneRange();
13724 nodeStartRange.collapse(true);
13726 var nodeEndRange = nodeRange.cloneRange();
13727 nodeEndRange.collapse(false);
13729 return rangeStartRange.compareBoundaryPoints(
13730 Range.START_TO_START, nodeEndRange) == -1 &&
13731 rangeEndRange.compareBoundaryPoints(
13732 Range.START_TO_START, nodeStartRange) == 1;
13736 rangeCompareNode : function(range, node)
13738 var nodeRange = node.ownerDocument.createRange();
13740 nodeRange.selectNode(node);
13742 nodeRange.selectNodeContents(node);
13746 range.collapse(true);
13748 nodeRange.collapse(true);
13750 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13751 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13753 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13755 var nodeIsBefore = ss == 1;
13756 var nodeIsAfter = ee == -1;
13758 if (nodeIsBefore && nodeIsAfter)
13760 if (!nodeIsBefore && nodeIsAfter)
13761 return 1; //right trailed.
13763 if (nodeIsBefore && !nodeIsAfter)
13764 return 2; // left trailed.
13769 // private? - in a new class?
13770 cleanUpPaste : function()
13772 // cleans up the whole document..
13773 Roo.log('cleanuppaste');
13775 this.cleanUpChildren(this.doc.body);
13776 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13777 if (clean != this.doc.body.innerHTML) {
13778 this.doc.body.innerHTML = clean;
13783 cleanWordChars : function(input) {// change the chars to hex code
13784 var he = Roo.HtmlEditorCore;
13786 var output = input;
13787 Roo.each(he.swapCodes, function(sw) {
13788 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13790 output = output.replace(swapper, sw[1]);
13797 cleanUpChildren : function (n)
13799 if (!n.childNodes.length) {
13802 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13803 this.cleanUpChild(n.childNodes[i]);
13810 cleanUpChild : function (node)
13813 //console.log(node);
13814 if (node.nodeName == "#text") {
13815 // clean up silly Windows -- stuff?
13818 if (node.nodeName == "#comment") {
13819 node.parentNode.removeChild(node);
13820 // clean up silly Windows -- stuff?
13824 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13826 node.parentNode.removeChild(node);
13831 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13833 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13834 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13836 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13837 // remove_keep_children = true;
13840 if (remove_keep_children) {
13841 this.cleanUpChildren(node);
13842 // inserts everything just before this node...
13843 while (node.childNodes.length) {
13844 var cn = node.childNodes[0];
13845 node.removeChild(cn);
13846 node.parentNode.insertBefore(cn, node);
13848 node.parentNode.removeChild(node);
13852 if (!node.attributes || !node.attributes.length) {
13853 this.cleanUpChildren(node);
13857 function cleanAttr(n,v)
13860 if (v.match(/^\./) || v.match(/^\//)) {
13863 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13866 if (v.match(/^#/)) {
13869 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13870 node.removeAttribute(n);
13874 function cleanStyle(n,v)
13876 if (v.match(/expression/)) { //XSS?? should we even bother..
13877 node.removeAttribute(n);
13880 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13881 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13884 var parts = v.split(/;/);
13887 Roo.each(parts, function(p) {
13888 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13892 var l = p.split(':').shift().replace(/\s+/g,'');
13893 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13895 if ( cblack.indexOf(l) > -1) {
13896 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13897 //node.removeAttribute(n);
13901 // only allow 'c whitelisted system attributes'
13902 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13903 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13904 //node.removeAttribute(n);
13914 if (clean.length) {
13915 node.setAttribute(n, clean.join(';'));
13917 node.removeAttribute(n);
13923 for (var i = node.attributes.length-1; i > -1 ; i--) {
13924 var a = node.attributes[i];
13927 if (a.name.toLowerCase().substr(0,2)=='on') {
13928 node.removeAttribute(a.name);
13931 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13932 node.removeAttribute(a.name);
13935 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13936 cleanAttr(a.name,a.value); // fixme..
13939 if (a.name == 'style') {
13940 cleanStyle(a.name,a.value);
13943 /// clean up MS crap..
13944 // tecnically this should be a list of valid class'es..
13947 if (a.name == 'class') {
13948 if (a.value.match(/^Mso/)) {
13949 node.className = '';
13952 if (a.value.match(/body/)) {
13953 node.className = '';
13964 this.cleanUpChildren(node);
13970 // hide stuff that is not compatible
13984 * @event specialkey
13988 * @cfg {String} fieldClass @hide
13991 * @cfg {String} focusClass @hide
13994 * @cfg {String} autoCreate @hide
13997 * @cfg {String} inputType @hide
14000 * @cfg {String} invalidClass @hide
14003 * @cfg {String} invalidText @hide
14006 * @cfg {String} msgFx @hide
14009 * @cfg {String} validateOnBlur @hide
14013 Roo.HtmlEditorCore.white = [
14014 'area', 'br', 'img', 'input', 'hr', 'wbr',
14016 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14017 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14018 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14019 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14020 'table', 'ul', 'xmp',
14022 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14025 'dir', 'menu', 'ol', 'ul', 'dl',
14031 Roo.HtmlEditorCore.black = [
14032 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14034 'base', 'basefont', 'bgsound', 'blink', 'body',
14035 'frame', 'frameset', 'head', 'html', 'ilayer',
14036 'iframe', 'layer', 'link', 'meta', 'object',
14037 'script', 'style' ,'title', 'xml' // clean later..
14039 Roo.HtmlEditorCore.clean = [
14040 'script', 'style', 'title', 'xml'
14042 Roo.HtmlEditorCore.remove = [
14047 Roo.HtmlEditorCore.ablack = [
14051 Roo.HtmlEditorCore.aclean = [
14052 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14056 Roo.HtmlEditorCore.pwhite= [
14057 'http', 'https', 'mailto'
14060 // white listed style attributes.
14061 Roo.HtmlEditorCore.cwhite= [
14062 // 'text-align', /// default is to allow most things..
14068 // black listed style attributes.
14069 Roo.HtmlEditorCore.cblack= [
14070 // 'font-size' -- this can be set by the project
14074 Roo.HtmlEditorCore.swapCodes =[
14093 * @class Roo.bootstrap.HtmlEditor
14094 * @extends Roo.bootstrap.TextArea
14095 * Bootstrap HtmlEditor class
14098 * Create a new HtmlEditor
14099 * @param {Object} config The config object
14102 Roo.bootstrap.HtmlEditor = function(config){
14103 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14104 if (!this.toolbars) {
14105 this.toolbars = [];
14107 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14110 * @event initialize
14111 * Fires when the editor is fully initialized (including the iframe)
14112 * @param {HtmlEditor} this
14117 * Fires when the editor is first receives the focus. Any insertion must wait
14118 * until after this event.
14119 * @param {HtmlEditor} this
14123 * @event beforesync
14124 * Fires before the textarea is updated with content from the editor iframe. Return false
14125 * to cancel the sync.
14126 * @param {HtmlEditor} this
14127 * @param {String} html
14131 * @event beforepush
14132 * Fires before the iframe editor is updated with content from the textarea. Return false
14133 * to cancel the push.
14134 * @param {HtmlEditor} this
14135 * @param {String} html
14140 * Fires when the textarea is updated with content from the editor iframe.
14141 * @param {HtmlEditor} this
14142 * @param {String} html
14147 * Fires when the iframe editor is updated with content from the textarea.
14148 * @param {HtmlEditor} this
14149 * @param {String} html
14153 * @event editmodechange
14154 * Fires when the editor switches edit modes
14155 * @param {HtmlEditor} this
14156 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14158 editmodechange: true,
14160 * @event editorevent
14161 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14162 * @param {HtmlEditor} this
14166 * @event firstfocus
14167 * Fires when on first focus - needed by toolbars..
14168 * @param {HtmlEditor} this
14173 * Auto save the htmlEditor value as a file into Events
14174 * @param {HtmlEditor} this
14178 * @event savedpreview
14179 * preview the saved version of htmlEditor
14180 * @param {HtmlEditor} this
14187 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14191 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14196 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14201 * @cfg {Number} height (in pixels)
14205 * @cfg {Number} width (in pixels)
14210 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14213 stylesheets: false,
14218 // private properties
14219 validationEvent : false,
14221 initialized : false,
14224 onFocus : Roo.emptyFn,
14226 hideMode:'offsets',
14229 tbContainer : false,
14231 toolbarContainer :function() {
14232 return this.wrap.select('.x-html-editor-tb',true).first();
14236 * Protected method that will not generally be called directly. It
14237 * is called when the editor creates its toolbar. Override this method if you need to
14238 * add custom toolbar buttons.
14239 * @param {HtmlEditor} editor
14241 createToolbar : function(){
14243 Roo.log("create toolbars");
14245 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14246 this.toolbars[0].render(this.toolbarContainer());
14250 // if (!editor.toolbars || !editor.toolbars.length) {
14251 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14254 // for (var i =0 ; i < editor.toolbars.length;i++) {
14255 // editor.toolbars[i] = Roo.factory(
14256 // typeof(editor.toolbars[i]) == 'string' ?
14257 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14258 // Roo.bootstrap.HtmlEditor);
14259 // editor.toolbars[i].init(editor);
14265 onRender : function(ct, position)
14267 // Roo.log("Call onRender: " + this.xtype);
14269 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14271 this.wrap = this.inputEl().wrap({
14272 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14275 this.editorcore.onRender(ct, position);
14277 if (this.resizable) {
14278 this.resizeEl = new Roo.Resizable(this.wrap, {
14282 minHeight : this.height,
14283 height: this.height,
14284 handles : this.resizable,
14287 resize : function(r, w, h) {
14288 _t.onResize(w,h); // -something
14294 this.createToolbar(this);
14297 if(!this.width && this.resizable){
14298 this.setSize(this.wrap.getSize());
14300 if (this.resizeEl) {
14301 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14302 // should trigger onReize..
14308 onResize : function(w, h)
14310 Roo.log('resize: ' +w + ',' + h );
14311 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14315 if(this.inputEl() ){
14316 if(typeof w == 'number'){
14317 var aw = w - this.wrap.getFrameWidth('lr');
14318 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14321 if(typeof h == 'number'){
14322 var tbh = -11; // fixme it needs to tool bar size!
14323 for (var i =0; i < this.toolbars.length;i++) {
14324 // fixme - ask toolbars for heights?
14325 tbh += this.toolbars[i].el.getHeight();
14326 //if (this.toolbars[i].footer) {
14327 // tbh += this.toolbars[i].footer.el.getHeight();
14335 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14336 ah -= 5; // knock a few pixes off for look..
14337 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14341 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14342 this.editorcore.onResize(ew,eh);
14347 * Toggles the editor between standard and source edit mode.
14348 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14350 toggleSourceEdit : function(sourceEditMode)
14352 this.editorcore.toggleSourceEdit(sourceEditMode);
14354 if(this.editorcore.sourceEditMode){
14355 Roo.log('editor - showing textarea');
14358 // Roo.log(this.syncValue());
14360 this.inputEl().removeClass('hide');
14361 this.inputEl().dom.removeAttribute('tabIndex');
14362 this.inputEl().focus();
14364 Roo.log('editor - hiding textarea');
14366 // Roo.log(this.pushValue());
14369 this.inputEl().addClass('hide');
14370 this.inputEl().dom.setAttribute('tabIndex', -1);
14371 //this.deferFocus();
14374 if(this.resizable){
14375 this.setSize(this.wrap.getSize());
14378 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14381 // private (for BoxComponent)
14382 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14384 // private (for BoxComponent)
14385 getResizeEl : function(){
14389 // private (for BoxComponent)
14390 getPositionEl : function(){
14395 initEvents : function(){
14396 this.originalValue = this.getValue();
14400 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14403 // markInvalid : Roo.emptyFn,
14405 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14408 // clearInvalid : Roo.emptyFn,
14410 setValue : function(v){
14411 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14412 this.editorcore.pushValue();
14417 deferFocus : function(){
14418 this.focus.defer(10, this);
14422 focus : function(){
14423 this.editorcore.focus();
14429 onDestroy : function(){
14435 for (var i =0; i < this.toolbars.length;i++) {
14436 // fixme - ask toolbars for heights?
14437 this.toolbars[i].onDestroy();
14440 this.wrap.dom.innerHTML = '';
14441 this.wrap.remove();
14446 onFirstFocus : function(){
14447 //Roo.log("onFirstFocus");
14448 this.editorcore.onFirstFocus();
14449 for (var i =0; i < this.toolbars.length;i++) {
14450 this.toolbars[i].onFirstFocus();
14456 syncValue : function()
14458 this.editorcore.syncValue();
14461 pushValue : function()
14463 this.editorcore.pushValue();
14467 // hide stuff that is not compatible
14481 * @event specialkey
14485 * @cfg {String} fieldClass @hide
14488 * @cfg {String} focusClass @hide
14491 * @cfg {String} autoCreate @hide
14494 * @cfg {String} inputType @hide
14497 * @cfg {String} invalidClass @hide
14500 * @cfg {String} invalidText @hide
14503 * @cfg {String} msgFx @hide
14506 * @cfg {String} validateOnBlur @hide
14517 * @class Roo.bootstrap.HtmlEditorToolbar1
14522 new Roo.bootstrap.HtmlEditor({
14525 new Roo.bootstrap.HtmlEditorToolbar1({
14526 disable : { fonts: 1 , format: 1, ..., ... , ...],
14532 * @cfg {Object} disable List of elements to disable..
14533 * @cfg {Array} btns List of additional buttons.
14537 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14540 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14543 Roo.apply(this, config);
14545 // default disabled, based on 'good practice'..
14546 this.disable = this.disable || {};
14547 Roo.applyIf(this.disable, {
14550 specialElements : true
14552 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14554 this.editor = config.editor;
14555 this.editorcore = config.editor.editorcore;
14557 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14559 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14560 // dont call parent... till later.
14562 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14568 editorcore : false,
14573 "h1","h2","h3","h4","h5","h6",
14575 "abbr", "acronym", "address", "cite", "samp", "var",
14579 onRender : function(ct, position)
14581 // Roo.log("Call onRender: " + this.xtype);
14583 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14585 this.el.dom.style.marginBottom = '0';
14587 var editorcore = this.editorcore;
14588 var editor= this.editor;
14591 var btn = function(id,cmd , toggle, handler){
14593 var event = toggle ? 'toggle' : 'click';
14598 xns: Roo.bootstrap,
14601 enableToggle:toggle !== false,
14603 pressed : toggle ? false : null,
14606 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14607 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14616 xns: Roo.bootstrap,
14617 glyphicon : 'font',
14621 xns: Roo.bootstrap,
14625 Roo.each(this.formats, function(f) {
14626 style.menu.items.push({
14628 xns: Roo.bootstrap,
14629 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14634 editorcore.insertTag(this.tagname);
14641 children.push(style);
14644 btn('bold',false,true);
14645 btn('italic',false,true);
14646 btn('align-left', 'justifyleft',true);
14647 btn('align-center', 'justifycenter',true);
14648 btn('align-right' , 'justifyright',true);
14649 btn('link', false, false, function(btn) {
14650 //Roo.log("create link?");
14651 var url = prompt(this.createLinkText, this.defaultLinkValue);
14652 if(url && url != 'http:/'+'/'){
14653 this.editorcore.relayCmd('createlink', url);
14656 btn('list','insertunorderedlist',true);
14657 btn('pencil', false,true, function(btn){
14660 this.toggleSourceEdit(btn.pressed);
14666 xns: Roo.bootstrap,
14671 xns: Roo.bootstrap,
14676 cog.menu.items.push({
14678 xns: Roo.bootstrap,
14679 html : Clean styles,
14684 editorcore.insertTag(this.tagname);
14693 this.xtype = 'Navbar';
14695 for(var i=0;i< children.length;i++) {
14697 this.buttons.add(this.addxtypeChild(children[i]));
14701 editor.on('editorevent', this.updateToolbar, this);
14703 onBtnClick : function(id)
14705 this.editorcore.relayCmd(id);
14706 this.editorcore.focus();
14710 * Protected method that will not generally be called directly. It triggers
14711 * a toolbar update by reading the markup state of the current selection in the editor.
14713 updateToolbar: function(){
14715 if(!this.editorcore.activated){
14716 this.editor.onFirstFocus(); // is this neeed?
14720 var btns = this.buttons;
14721 var doc = this.editorcore.doc;
14722 btns.get('bold').setActive(doc.queryCommandState('bold'));
14723 btns.get('italic').setActive(doc.queryCommandState('italic'));
14724 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14726 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14727 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14728 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14730 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14731 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14734 var ans = this.editorcore.getAllAncestors();
14735 if (this.formatCombo) {
14738 var store = this.formatCombo.store;
14739 this.formatCombo.setValue("");
14740 for (var i =0; i < ans.length;i++) {
14741 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14743 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14751 // hides menus... - so this cant be on a menu...
14752 Roo.bootstrap.MenuMgr.hideAll();
14754 Roo.bootstrap.MenuMgr.hideAll();
14755 //this.editorsyncValue();
14757 onFirstFocus: function() {
14758 this.buttons.each(function(item){
14762 toggleSourceEdit : function(sourceEditMode){
14765 if(sourceEditMode){
14766 Roo.log("disabling buttons");
14767 this.buttons.each( function(item){
14768 if(item.cmd != 'pencil'){
14774 Roo.log("enabling buttons");
14775 if(this.editorcore.initialized){
14776 this.buttons.each( function(item){
14782 Roo.log("calling toggole on editor");
14783 // tell the editor that it's been pressed..
14784 this.editor.toggleSourceEdit(sourceEditMode);
14794 * @class Roo.bootstrap.Table.AbstractSelectionModel
14795 * @extends Roo.util.Observable
14796 * Abstract base class for grid SelectionModels. It provides the interface that should be
14797 * implemented by descendant classes. This class should not be directly instantiated.
14800 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14801 this.locked = false;
14802 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14806 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14807 /** @ignore Called by the grid automatically. Do not call directly. */
14808 init : function(grid){
14814 * Locks the selections.
14817 this.locked = true;
14821 * Unlocks the selections.
14823 unlock : function(){
14824 this.locked = false;
14828 * Returns true if the selections are locked.
14829 * @return {Boolean}
14831 isLocked : function(){
14832 return this.locked;
14836 * @class Roo.bootstrap.Table.ColumnModel
14837 * @extends Roo.util.Observable
14838 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14839 * the columns in the table.
14842 * @param {Object} config An Array of column config objects. See this class's
14843 * config objects for details.
14845 Roo.bootstrap.Table.ColumnModel = function(config){
14847 * The config passed into the constructor
14849 this.config = config;
14852 // if no id, create one
14853 // if the column does not have a dataIndex mapping,
14854 // map it to the order it is in the config
14855 for(var i = 0, len = config.length; i < len; i++){
14857 if(typeof c.dataIndex == "undefined"){
14860 if(typeof c.renderer == "string"){
14861 c.renderer = Roo.util.Format[c.renderer];
14863 if(typeof c.id == "undefined"){
14866 // if(c.editor && c.editor.xtype){
14867 // c.editor = Roo.factory(c.editor, Roo.grid);
14869 // if(c.editor && c.editor.isFormField){
14870 // c.editor = new Roo.grid.GridEditor(c.editor);
14873 this.lookup[c.id] = c;
14877 * The width of columns which have no width specified (defaults to 100)
14880 this.defaultWidth = 100;
14883 * Default sortable of columns which have no sortable specified (defaults to false)
14886 this.defaultSortable = false;
14890 * @event widthchange
14891 * Fires when the width of a column changes.
14892 * @param {ColumnModel} this
14893 * @param {Number} columnIndex The column index
14894 * @param {Number} newWidth The new width
14896 "widthchange": true,
14898 * @event headerchange
14899 * Fires when the text of a header changes.
14900 * @param {ColumnModel} this
14901 * @param {Number} columnIndex The column index
14902 * @param {Number} newText The new header text
14904 "headerchange": true,
14906 * @event hiddenchange
14907 * Fires when a column is hidden or "unhidden".
14908 * @param {ColumnModel} this
14909 * @param {Number} columnIndex The column index
14910 * @param {Boolean} hidden true if hidden, false otherwise
14912 "hiddenchange": true,
14914 * @event columnmoved
14915 * Fires when a column is moved.
14916 * @param {ColumnModel} this
14917 * @param {Number} oldIndex
14918 * @param {Number} newIndex
14920 "columnmoved" : true,
14922 * @event columlockchange
14923 * Fires when a column's locked state is changed
14924 * @param {ColumnModel} this
14925 * @param {Number} colIndex
14926 * @param {Boolean} locked true if locked
14928 "columnlockchange" : true
14930 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14932 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14934 * @cfg {String} header The header text to display in the Grid view.
14937 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14938 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14939 * specified, the column's index is used as an index into the Record's data Array.
14942 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14943 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14946 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14947 * Defaults to the value of the {@link #defaultSortable} property.
14948 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14951 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14954 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14957 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14960 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14963 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14964 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14965 * default renderer uses the raw data value.
14968 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14972 * Returns the id of the column at the specified index.
14973 * @param {Number} index The column index
14974 * @return {String} the id
14976 getColumnId : function(index){
14977 return this.config[index].id;
14981 * Returns the column for a specified id.
14982 * @param {String} id The column id
14983 * @return {Object} the column
14985 getColumnById : function(id){
14986 return this.lookup[id];
14991 * Returns the column for a specified dataIndex.
14992 * @param {String} dataIndex The column dataIndex
14993 * @return {Object|Boolean} the column or false if not found
14995 getColumnByDataIndex: function(dataIndex){
14996 var index = this.findColumnIndex(dataIndex);
14997 return index > -1 ? this.config[index] : false;
15001 * Returns the index for a specified column id.
15002 * @param {String} id The column id
15003 * @return {Number} the index, or -1 if not found
15005 getIndexById : function(id){
15006 for(var i = 0, len = this.config.length; i < len; i++){
15007 if(this.config[i].id == id){
15015 * Returns the index for a specified column dataIndex.
15016 * @param {String} dataIndex The column dataIndex
15017 * @return {Number} the index, or -1 if not found
15020 findColumnIndex : function(dataIndex){
15021 for(var i = 0, len = this.config.length; i < len; i++){
15022 if(this.config[i].dataIndex == dataIndex){
15030 moveColumn : function(oldIndex, newIndex){
15031 var c = this.config[oldIndex];
15032 this.config.splice(oldIndex, 1);
15033 this.config.splice(newIndex, 0, c);
15034 this.dataMap = null;
15035 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15038 isLocked : function(colIndex){
15039 return this.config[colIndex].locked === true;
15042 setLocked : function(colIndex, value, suppressEvent){
15043 if(this.isLocked(colIndex) == value){
15046 this.config[colIndex].locked = value;
15047 if(!suppressEvent){
15048 this.fireEvent("columnlockchange", this, colIndex, value);
15052 getTotalLockedWidth : function(){
15053 var totalWidth = 0;
15054 for(var i = 0; i < this.config.length; i++){
15055 if(this.isLocked(i) && !this.isHidden(i)){
15056 this.totalWidth += this.getColumnWidth(i);
15062 getLockedCount : function(){
15063 for(var i = 0, len = this.config.length; i < len; i++){
15064 if(!this.isLocked(i)){
15071 * Returns the number of columns.
15074 getColumnCount : function(visibleOnly){
15075 if(visibleOnly === true){
15077 for(var i = 0, len = this.config.length; i < len; i++){
15078 if(!this.isHidden(i)){
15084 return this.config.length;
15088 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15089 * @param {Function} fn
15090 * @param {Object} scope (optional)
15091 * @return {Array} result
15093 getColumnsBy : function(fn, scope){
15095 for(var i = 0, len = this.config.length; i < len; i++){
15096 var c = this.config[i];
15097 if(fn.call(scope||this, c, i) === true){
15105 * Returns true if the specified column is sortable.
15106 * @param {Number} col The column index
15107 * @return {Boolean}
15109 isSortable : function(col){
15110 if(typeof this.config[col].sortable == "undefined"){
15111 return this.defaultSortable;
15113 return this.config[col].sortable;
15117 * Returns the rendering (formatting) function defined for the column.
15118 * @param {Number} col The column index.
15119 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15121 getRenderer : function(col){
15122 if(!this.config[col].renderer){
15123 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15125 return this.config[col].renderer;
15129 * Sets the rendering (formatting) function for a column.
15130 * @param {Number} col The column index
15131 * @param {Function} fn The function to use to process the cell's raw data
15132 * to return HTML markup for the grid view. The render function is called with
15133 * the following parameters:<ul>
15134 * <li>Data value.</li>
15135 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15136 * <li>css A CSS style string to apply to the table cell.</li>
15137 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15138 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15139 * <li>Row index</li>
15140 * <li>Column index</li>
15141 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15143 setRenderer : function(col, fn){
15144 this.config[col].renderer = fn;
15148 * Returns the width for the specified column.
15149 * @param {Number} col The column index
15152 getColumnWidth : function(col){
15153 return this.config[col].width * 1 || this.defaultWidth;
15157 * Sets the width for a column.
15158 * @param {Number} col The column index
15159 * @param {Number} width The new width
15161 setColumnWidth : function(col, width, suppressEvent){
15162 this.config[col].width = width;
15163 this.totalWidth = null;
15164 if(!suppressEvent){
15165 this.fireEvent("widthchange", this, col, width);
15170 * Returns the total width of all columns.
15171 * @param {Boolean} includeHidden True to include hidden column widths
15174 getTotalWidth : function(includeHidden){
15175 if(!this.totalWidth){
15176 this.totalWidth = 0;
15177 for(var i = 0, len = this.config.length; i < len; i++){
15178 if(includeHidden || !this.isHidden(i)){
15179 this.totalWidth += this.getColumnWidth(i);
15183 return this.totalWidth;
15187 * Returns the header for the specified column.
15188 * @param {Number} col The column index
15191 getColumnHeader : function(col){
15192 return this.config[col].header;
15196 * Sets the header for a column.
15197 * @param {Number} col The column index
15198 * @param {String} header The new header
15200 setColumnHeader : function(col, header){
15201 this.config[col].header = header;
15202 this.fireEvent("headerchange", this, col, header);
15206 * Returns the tooltip for the specified column.
15207 * @param {Number} col The column index
15210 getColumnTooltip : function(col){
15211 return this.config[col].tooltip;
15214 * Sets the tooltip for a column.
15215 * @param {Number} col The column index
15216 * @param {String} tooltip The new tooltip
15218 setColumnTooltip : function(col, tooltip){
15219 this.config[col].tooltip = tooltip;
15223 * Returns the dataIndex for the specified column.
15224 * @param {Number} col The column index
15227 getDataIndex : function(col){
15228 return this.config[col].dataIndex;
15232 * Sets the dataIndex for a column.
15233 * @param {Number} col The column index
15234 * @param {Number} dataIndex The new dataIndex
15236 setDataIndex : function(col, dataIndex){
15237 this.config[col].dataIndex = dataIndex;
15243 * Returns true if the cell is editable.
15244 * @param {Number} colIndex The column index
15245 * @param {Number} rowIndex The row index
15246 * @return {Boolean}
15248 isCellEditable : function(colIndex, rowIndex){
15249 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15253 * Returns the editor defined for the cell/column.
15254 * return false or null to disable editing.
15255 * @param {Number} colIndex The column index
15256 * @param {Number} rowIndex The row index
15259 getCellEditor : function(colIndex, rowIndex){
15260 return this.config[colIndex].editor;
15264 * Sets if a column is editable.
15265 * @param {Number} col The column index
15266 * @param {Boolean} editable True if the column is editable
15268 setEditable : function(col, editable){
15269 this.config[col].editable = editable;
15274 * Returns true if the column is hidden.
15275 * @param {Number} colIndex The column index
15276 * @return {Boolean}
15278 isHidden : function(colIndex){
15279 return this.config[colIndex].hidden;
15284 * Returns true if the column width cannot be changed
15286 isFixed : function(colIndex){
15287 return this.config[colIndex].fixed;
15291 * Returns true if the column can be resized
15292 * @return {Boolean}
15294 isResizable : function(colIndex){
15295 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15298 * Sets if a column is hidden.
15299 * @param {Number} colIndex The column index
15300 * @param {Boolean} hidden True if the column is hidden
15302 setHidden : function(colIndex, hidden){
15303 this.config[colIndex].hidden = hidden;
15304 this.totalWidth = null;
15305 this.fireEvent("hiddenchange", this, colIndex, hidden);
15309 * Sets the editor for a column.
15310 * @param {Number} col The column index
15311 * @param {Object} editor The editor object
15313 setEditor : function(col, editor){
15314 this.config[col].editor = editor;
15318 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15319 if(typeof value == "string" && value.length < 1){
15325 // Alias for backwards compatibility
15326 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15329 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15330 * @class Roo.bootstrap.Table.RowSelectionModel
15331 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15332 * It supports multiple selections and keyboard selection/navigation.
15334 * @param {Object} config
15337 Roo.bootstrap.Table.RowSelectionModel = function(config){
15338 Roo.apply(this, config);
15339 this.selections = new Roo.util.MixedCollection(false, function(o){
15344 this.lastActive = false;
15348 * @event selectionchange
15349 * Fires when the selection changes
15350 * @param {SelectionModel} this
15352 "selectionchange" : true,
15354 * @event afterselectionchange
15355 * Fires after the selection changes (eg. by key press or clicking)
15356 * @param {SelectionModel} this
15358 "afterselectionchange" : true,
15360 * @event beforerowselect
15361 * Fires when a row is selected being selected, return false to cancel.
15362 * @param {SelectionModel} this
15363 * @param {Number} rowIndex The selected index
15364 * @param {Boolean} keepExisting False if other selections will be cleared
15366 "beforerowselect" : true,
15369 * Fires when a row is selected.
15370 * @param {SelectionModel} this
15371 * @param {Number} rowIndex The selected index
15372 * @param {Roo.data.Record} r The record
15374 "rowselect" : true,
15376 * @event rowdeselect
15377 * Fires when a row is deselected.
15378 * @param {SelectionModel} this
15379 * @param {Number} rowIndex The selected index
15381 "rowdeselect" : true
15383 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15384 this.locked = false;
15387 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15389 * @cfg {Boolean} singleSelect
15390 * True to allow selection of only one row at a time (defaults to false)
15392 singleSelect : false,
15395 initEvents : function(){
15397 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15398 this.grid.on("mousedown", this.handleMouseDown, this);
15399 }else{ // allow click to work like normal
15400 this.grid.on("rowclick", this.handleDragableRowClick, this);
15403 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15404 "up" : function(e){
15406 this.selectPrevious(e.shiftKey);
15407 }else if(this.last !== false && this.lastActive !== false){
15408 var last = this.last;
15409 this.selectRange(this.last, this.lastActive-1);
15410 this.grid.getView().focusRow(this.lastActive);
15411 if(last !== false){
15415 this.selectFirstRow();
15417 this.fireEvent("afterselectionchange", this);
15419 "down" : function(e){
15421 this.selectNext(e.shiftKey);
15422 }else if(this.last !== false && this.lastActive !== false){
15423 var last = this.last;
15424 this.selectRange(this.last, this.lastActive+1);
15425 this.grid.getView().focusRow(this.lastActive);
15426 if(last !== false){
15430 this.selectFirstRow();
15432 this.fireEvent("afterselectionchange", this);
15437 var view = this.grid.view;
15438 view.on("refresh", this.onRefresh, this);
15439 view.on("rowupdated", this.onRowUpdated, this);
15440 view.on("rowremoved", this.onRemove, this);
15444 onRefresh : function(){
15445 var ds = this.grid.dataSource, i, v = this.grid.view;
15446 var s = this.selections;
15447 s.each(function(r){
15448 if((i = ds.indexOfId(r.id)) != -1){
15457 onRemove : function(v, index, r){
15458 this.selections.remove(r);
15462 onRowUpdated : function(v, index, r){
15463 if(this.isSelected(r)){
15464 v.onRowSelect(index);
15470 * @param {Array} records The records to select
15471 * @param {Boolean} keepExisting (optional) True to keep existing selections
15473 selectRecords : function(records, keepExisting){
15475 this.clearSelections();
15477 var ds = this.grid.dataSource;
15478 for(var i = 0, len = records.length; i < len; i++){
15479 this.selectRow(ds.indexOf(records[i]), true);
15484 * Gets the number of selected rows.
15487 getCount : function(){
15488 return this.selections.length;
15492 * Selects the first row in the grid.
15494 selectFirstRow : function(){
15499 * Select the last row.
15500 * @param {Boolean} keepExisting (optional) True to keep existing selections
15502 selectLastRow : function(keepExisting){
15503 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15507 * Selects the row immediately following the last selected row.
15508 * @param {Boolean} keepExisting (optional) True to keep existing selections
15510 selectNext : function(keepExisting){
15511 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15512 this.selectRow(this.last+1, keepExisting);
15513 this.grid.getView().focusRow(this.last);
15518 * Selects the row that precedes the last selected row.
15519 * @param {Boolean} keepExisting (optional) True to keep existing selections
15521 selectPrevious : function(keepExisting){
15523 this.selectRow(this.last-1, keepExisting);
15524 this.grid.getView().focusRow(this.last);
15529 * Returns the selected records
15530 * @return {Array} Array of selected records
15532 getSelections : function(){
15533 return [].concat(this.selections.items);
15537 * Returns the first selected record.
15540 getSelected : function(){
15541 return this.selections.itemAt(0);
15546 * Clears all selections.
15548 clearSelections : function(fast){
15549 if(this.locked) return;
15551 var ds = this.grid.dataSource;
15552 var s = this.selections;
15553 s.each(function(r){
15554 this.deselectRow(ds.indexOfId(r.id));
15558 this.selections.clear();
15565 * Selects all rows.
15567 selectAll : function(){
15568 if(this.locked) return;
15569 this.selections.clear();
15570 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15571 this.selectRow(i, true);
15576 * Returns True if there is a selection.
15577 * @return {Boolean}
15579 hasSelection : function(){
15580 return this.selections.length > 0;
15584 * Returns True if the specified row is selected.
15585 * @param {Number/Record} record The record or index of the record to check
15586 * @return {Boolean}
15588 isSelected : function(index){
15589 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15590 return (r && this.selections.key(r.id) ? true : false);
15594 * Returns True if the specified record id is selected.
15595 * @param {String} id The id of record to check
15596 * @return {Boolean}
15598 isIdSelected : function(id){
15599 return (this.selections.key(id) ? true : false);
15603 handleMouseDown : function(e, t){
15604 var view = this.grid.getView(), rowIndex;
15605 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15608 if(e.shiftKey && this.last !== false){
15609 var last = this.last;
15610 this.selectRange(last, rowIndex, e.ctrlKey);
15611 this.last = last; // reset the last
15612 view.focusRow(rowIndex);
15614 var isSelected = this.isSelected(rowIndex);
15615 if(e.button !== 0 && isSelected){
15616 view.focusRow(rowIndex);
15617 }else if(e.ctrlKey && isSelected){
15618 this.deselectRow(rowIndex);
15619 }else if(!isSelected){
15620 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15621 view.focusRow(rowIndex);
15624 this.fireEvent("afterselectionchange", this);
15627 handleDragableRowClick : function(grid, rowIndex, e)
15629 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15630 this.selectRow(rowIndex, false);
15631 grid.view.focusRow(rowIndex);
15632 this.fireEvent("afterselectionchange", this);
15637 * Selects multiple rows.
15638 * @param {Array} rows Array of the indexes of the row to select
15639 * @param {Boolean} keepExisting (optional) True to keep existing selections
15641 selectRows : function(rows, keepExisting){
15643 this.clearSelections();
15645 for(var i = 0, len = rows.length; i < len; i++){
15646 this.selectRow(rows[i], true);
15651 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15652 * @param {Number} startRow The index of the first row in the range
15653 * @param {Number} endRow The index of the last row in the range
15654 * @param {Boolean} keepExisting (optional) True to retain existing selections
15656 selectRange : function(startRow, endRow, keepExisting){
15657 if(this.locked) return;
15659 this.clearSelections();
15661 if(startRow <= endRow){
15662 for(var i = startRow; i <= endRow; i++){
15663 this.selectRow(i, true);
15666 for(var i = startRow; i >= endRow; i--){
15667 this.selectRow(i, true);
15673 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15674 * @param {Number} startRow The index of the first row in the range
15675 * @param {Number} endRow The index of the last row in the range
15677 deselectRange : function(startRow, endRow, preventViewNotify){
15678 if(this.locked) return;
15679 for(var i = startRow; i <= endRow; i++){
15680 this.deselectRow(i, preventViewNotify);
15686 * @param {Number} row The index of the row to select
15687 * @param {Boolean} keepExisting (optional) True to keep existing selections
15689 selectRow : function(index, keepExisting, preventViewNotify){
15690 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15691 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15692 if(!keepExisting || this.singleSelect){
15693 this.clearSelections();
15695 var r = this.grid.dataSource.getAt(index);
15696 this.selections.add(r);
15697 this.last = this.lastActive = index;
15698 if(!preventViewNotify){
15699 this.grid.getView().onRowSelect(index);
15701 this.fireEvent("rowselect", this, index, r);
15702 this.fireEvent("selectionchange", this);
15708 * @param {Number} row The index of the row to deselect
15710 deselectRow : function(index, preventViewNotify){
15711 if(this.locked) return;
15712 if(this.last == index){
15715 if(this.lastActive == index){
15716 this.lastActive = false;
15718 var r = this.grid.dataSource.getAt(index);
15719 this.selections.remove(r);
15720 if(!preventViewNotify){
15721 this.grid.getView().onRowDeselect(index);
15723 this.fireEvent("rowdeselect", this, index);
15724 this.fireEvent("selectionchange", this);
15728 restoreLast : function(){
15730 this.last = this._last;
15735 acceptsNav : function(row, col, cm){
15736 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15740 onEditorKey : function(field, e){
15741 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15746 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15748 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15750 }else if(k == e.ENTER && !e.ctrlKey){
15754 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15756 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15758 }else if(k == e.ESC){
15762 g.startEditing(newCell[0], newCell[1]);
15773 * @class Roo.bootstrap.MessageBar
15774 * @extends Roo.bootstrap.Component
15775 * Bootstrap MessageBar class
15776 * @cfg {String} html contents of the MessageBar
15777 * @cfg {String} weight (info | success | warning | danger) default info
15778 * @cfg {String} beforeClass insert the bar before the given class
15779 * @cfg {Boolean} closable (true | false) default false
15780 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15783 * Create a new Element
15784 * @param {Object} config The config object
15787 Roo.bootstrap.MessageBar = function(config){
15788 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15791 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15797 beforeClass: 'bootstrap-sticky-wrap',
15799 getAutoCreate : function(){
15803 cls: 'alert alert-dismissable alert-' + this.weight,
15808 html: this.html || ''
15814 cfg.cls += ' alert-messages-fixed';
15828 onRender : function(ct, position)
15830 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15833 var cfg = Roo.apply({}, this.getAutoCreate());
15837 cfg.cls += ' ' + this.cls;
15840 cfg.style = this.style;
15842 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15844 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15847 this.el.select('>button.close').on('click', this.hide, this);
15853 if (!this.rendered) {
15859 this.fireEvent('show', this);
15865 if (!this.rendered) {
15871 this.fireEvent('hide', this);
15874 update : function()
15876 // var e = this.el.dom.firstChild;
15878 // if(this.closable){
15879 // e = e.nextSibling;
15882 // e.data = this.html || '';
15884 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';