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!!!!');
2953 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2954 // this.maskEl.enableDisplayMode("block");
2955 // this.maskEl.show();
2957 this.store.on('load', this.onLoad, this);
2958 this.store.on('beforeload', this.onBeforeLoad, this);
2966 renderHeader : function()
2977 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2980 config = cm.config[i];
2982 if(typeof(config.sortable) != 'undefined' && config.sortable){
2989 sort: (typeof(config.dataIndex) != 'undefined') ? config.dataIndex : '',
2990 html: cm.getColumnHeader(i)
2997 renderBody : function()
3007 renderFooter : function()
3019 Roo.log('ds onload');
3023 var tbody = this.el.select('tbody', true).first();
3027 if(this.store.getCount() > 0){
3028 this.store.data.each(function(d){
3034 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3035 var renderer = cm.getRenderer(i);
3036 var config = cm.config[i];
3040 if(typeof(renderer) !== 'undefined'){
3041 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3044 if(typeof(value) === 'object'){
3054 html: (typeof(value) === 'object') ? '' : value
3057 if(typeof(config.width) != 'undefined'){
3058 td.width = config.width;
3065 tbody.createChild(row);
3073 Roo.each(renders, function(r){
3074 _this.renderColumn(r);
3078 // if(this.loadMask){
3079 // this.maskEl.hide();
3083 onBeforeLoad : function()
3085 Roo.log('ds onBeforeLoad');
3089 // if(this.loadMask){
3090 // this.maskEl.show();
3096 this.el.select('tbody', true).first().dom.innerHTML = '';
3099 getSelectionModel : function(){
3101 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3103 return this.selModel;
3106 renderColumn : function(r)
3109 r.cfg.render(Roo.get(r.id));
3112 Roo.each(r.cfg.cn, function(c){
3117 _this.renderColumn(child);
3134 * @class Roo.bootstrap.TableCell
3135 * @extends Roo.bootstrap.Component
3136 * Bootstrap TableCell class
3137 * @cfg {String} html cell contain text
3138 * @cfg {String} cls cell class
3139 * @cfg {String} tag cell tag (td|th) default td
3140 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3141 * @cfg {String} align Aligns the content in a cell
3142 * @cfg {String} axis Categorizes cells
3143 * @cfg {String} bgcolor Specifies the background color of a cell
3144 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3145 * @cfg {Number} colspan Specifies the number of columns a cell should span
3146 * @cfg {String} headers Specifies one or more header cells a cell is related to
3147 * @cfg {Number} height Sets the height of a cell
3148 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3149 * @cfg {Number} rowspan Sets the number of rows a cell should span
3150 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3151 * @cfg {String} valign Vertical aligns the content in a cell
3152 * @cfg {Number} width Specifies the width of a cell
3155 * Create a new TableCell
3156 * @param {Object} config The config object
3159 Roo.bootstrap.TableCell = function(config){
3160 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3163 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3183 getAutoCreate : function(){
3184 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3204 cfg.align=this.align
3210 cfg.bgcolor=this.bgcolor
3213 cfg.charoff=this.charoff
3216 cfg.colspan=this.colspan
3219 cfg.headers=this.headers
3222 cfg.height=this.height
3225 cfg.nowrap=this.nowrap
3228 cfg.rowspan=this.rowspan
3231 cfg.scope=this.scope
3234 cfg.valign=this.valign
3237 cfg.width=this.width
3256 * @class Roo.bootstrap.TableRow
3257 * @extends Roo.bootstrap.Component
3258 * Bootstrap TableRow class
3259 * @cfg {String} cls row class
3260 * @cfg {String} align Aligns the content in a table row
3261 * @cfg {String} bgcolor Specifies a background color for a table row
3262 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3263 * @cfg {String} valign Vertical aligns the content in a table row
3266 * Create a new TableRow
3267 * @param {Object} config The config object
3270 Roo.bootstrap.TableRow = function(config){
3271 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3274 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3282 getAutoCreate : function(){
3283 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3293 cfg.align = this.align;
3296 cfg.bgcolor = this.bgcolor;
3299 cfg.charoff = this.charoff;
3302 cfg.valign = this.valign;
3320 * @class Roo.bootstrap.TableBody
3321 * @extends Roo.bootstrap.Component
3322 * Bootstrap TableBody class
3323 * @cfg {String} cls element class
3324 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3325 * @cfg {String} align Aligns the content inside the element
3326 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3327 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3330 * Create a new TableBody
3331 * @param {Object} config The config object
3334 Roo.bootstrap.TableBody = function(config){
3335 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3338 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3346 getAutoCreate : function(){
3347 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3361 cfg.align = this.align;
3364 cfg.charoff = this.charoff;
3367 cfg.valign = this.valign;
3374 // initEvents : function()
3381 // this.store = Roo.factory(this.store, Roo.data);
3382 // this.store.on('load', this.onLoad, this);
3384 // this.store.load();
3388 // onLoad: function ()
3390 // this.fireEvent('load', this);
3400 * Ext JS Library 1.1.1
3401 * Copyright(c) 2006-2007, Ext JS, LLC.
3403 * Originally Released Under LGPL - original licence link has changed is not relivant.
3406 * <script type="text/javascript">
3409 // as we use this in bootstrap.
3410 Roo.namespace('Roo.form');
3412 * @class Roo.form.Action
3413 * Internal Class used to handle form actions
3415 * @param {Roo.form.BasicForm} el The form element or its id
3416 * @param {Object} config Configuration options
3421 // define the action interface
3422 Roo.form.Action = function(form, options){
3424 this.options = options || {};
3427 * Client Validation Failed
3430 Roo.form.Action.CLIENT_INVALID = 'client';
3432 * Server Validation Failed
3435 Roo.form.Action.SERVER_INVALID = 'server';
3437 * Connect to Server Failed
3440 Roo.form.Action.CONNECT_FAILURE = 'connect';
3442 * Reading Data from Server Failed
3445 Roo.form.Action.LOAD_FAILURE = 'load';
3447 Roo.form.Action.prototype = {
3449 failureType : undefined,
3450 response : undefined,
3454 run : function(options){
3459 success : function(response){
3464 handleResponse : function(response){
3468 // default connection failure
3469 failure : function(response){
3471 this.response = response;
3472 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3473 this.form.afterAction(this, false);
3476 processResponse : function(response){
3477 this.response = response;
3478 if(!response.responseText){
3481 this.result = this.handleResponse(response);
3485 // utility functions used internally
3486 getUrl : function(appendParams){
3487 var url = this.options.url || this.form.url || this.form.el.dom.action;
3489 var p = this.getParams();
3491 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3497 getMethod : function(){
3498 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3501 getParams : function(){
3502 var bp = this.form.baseParams;
3503 var p = this.options.params;
3505 if(typeof p == "object"){
3506 p = Roo.urlEncode(Roo.applyIf(p, bp));
3507 }else if(typeof p == 'string' && bp){
3508 p += '&' + Roo.urlEncode(bp);
3511 p = Roo.urlEncode(bp);
3516 createCallback : function(){
3518 success: this.success,
3519 failure: this.failure,
3521 timeout: (this.form.timeout*1000),
3522 upload: this.form.fileUpload ? this.success : undefined
3527 Roo.form.Action.Submit = function(form, options){
3528 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3531 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3534 haveProgress : false,
3535 uploadComplete : false,
3537 // uploadProgress indicator.
3538 uploadProgress : function()
3540 if (!this.form.progressUrl) {
3544 if (!this.haveProgress) {
3545 Roo.MessageBox.progress("Uploading", "Uploading");
3547 if (this.uploadComplete) {
3548 Roo.MessageBox.hide();
3552 this.haveProgress = true;
3554 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3556 var c = new Roo.data.Connection();
3558 url : this.form.progressUrl,
3563 success : function(req){
3564 //console.log(data);
3568 rdata = Roo.decode(req.responseText)
3570 Roo.log("Invalid data from server..");
3574 if (!rdata || !rdata.success) {
3576 Roo.MessageBox.alert(Roo.encode(rdata));
3579 var data = rdata.data;
3581 if (this.uploadComplete) {
3582 Roo.MessageBox.hide();
3587 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3588 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3591 this.uploadProgress.defer(2000,this);
3594 failure: function(data) {
3595 Roo.log('progress url failed ');
3606 // run get Values on the form, so it syncs any secondary forms.
3607 this.form.getValues();
3609 var o = this.options;
3610 var method = this.getMethod();
3611 var isPost = method == 'POST';
3612 if(o.clientValidation === false || this.form.isValid()){
3614 if (this.form.progressUrl) {
3615 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3616 (new Date() * 1) + '' + Math.random());
3621 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3622 form:this.form.el.dom,
3623 url:this.getUrl(!isPost),
3625 params:isPost ? this.getParams() : null,
3626 isUpload: this.form.fileUpload
3629 this.uploadProgress();
3631 }else if (o.clientValidation !== false){ // client validation failed
3632 this.failureType = Roo.form.Action.CLIENT_INVALID;
3633 this.form.afterAction(this, false);
3637 success : function(response)
3639 this.uploadComplete= true;
3640 if (this.haveProgress) {
3641 Roo.MessageBox.hide();
3645 var result = this.processResponse(response);
3646 if(result === true || result.success){
3647 this.form.afterAction(this, true);
3651 this.form.markInvalid(result.errors);
3652 this.failureType = Roo.form.Action.SERVER_INVALID;
3654 this.form.afterAction(this, false);
3656 failure : function(response)
3658 this.uploadComplete= true;
3659 if (this.haveProgress) {
3660 Roo.MessageBox.hide();
3663 this.response = response;
3664 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3665 this.form.afterAction(this, false);
3668 handleResponse : function(response){
3669 if(this.form.errorReader){
3670 var rs = this.form.errorReader.read(response);
3673 for(var i = 0, len = rs.records.length; i < len; i++) {
3674 var r = rs.records[i];
3678 if(errors.length < 1){
3682 success : rs.success,
3688 ret = Roo.decode(response.responseText);
3692 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3702 Roo.form.Action.Load = function(form, options){
3703 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3704 this.reader = this.form.reader;
3707 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3712 Roo.Ajax.request(Roo.apply(
3713 this.createCallback(), {
3714 method:this.getMethod(),
3715 url:this.getUrl(false),
3716 params:this.getParams()
3720 success : function(response){
3722 var result = this.processResponse(response);
3723 if(result === true || !result.success || !result.data){
3724 this.failureType = Roo.form.Action.LOAD_FAILURE;
3725 this.form.afterAction(this, false);
3728 this.form.clearInvalid();
3729 this.form.setValues(result.data);
3730 this.form.afterAction(this, true);
3733 handleResponse : function(response){
3734 if(this.form.reader){
3735 var rs = this.form.reader.read(response);
3736 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3738 success : rs.success,
3742 return Roo.decode(response.responseText);
3746 Roo.form.Action.ACTION_TYPES = {
3747 'load' : Roo.form.Action.Load,
3748 'submit' : Roo.form.Action.Submit
3757 * @class Roo.bootstrap.Form
3758 * @extends Roo.bootstrap.Component
3759 * Bootstrap Form class
3760 * @cfg {String} method GET | POST (default POST)
3761 * @cfg {String} labelAlign top | left (default top)
3762 * @cfg {String} align left | right - for navbars
3767 * @param {Object} config The config object
3771 Roo.bootstrap.Form = function(config){
3772 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3775 * @event clientvalidation
3776 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3777 * @param {Form} this
3778 * @param {Boolean} valid true if the form has passed client-side validation
3780 clientvalidation: true,
3782 * @event beforeaction
3783 * Fires before any action is performed. Return false to cancel the action.
3784 * @param {Form} this
3785 * @param {Action} action The action to be performed
3789 * @event actionfailed
3790 * Fires when an action fails.
3791 * @param {Form} this
3792 * @param {Action} action The action that failed
3794 actionfailed : true,
3796 * @event actioncomplete
3797 * Fires when an action is completed.
3798 * @param {Form} this
3799 * @param {Action} action The action that completed
3801 actioncomplete : true
3806 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3809 * @cfg {String} method
3810 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3815 * The URL to use for form actions if one isn't supplied in the action options.
3818 * @cfg {Boolean} fileUpload
3819 * Set to true if this form is a file upload.
3823 * @cfg {Object} baseParams
3824 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3828 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3832 * @cfg {Sting} align (left|right) for navbar forms
3837 activeAction : null,
3840 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3841 * element by passing it or its id or mask the form itself by passing in true.
3844 waitMsgTarget : false,
3849 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3850 * element by passing it or its id or mask the form itself by passing in true.
3854 getAutoCreate : function(){
3858 method : this.method || 'POST',
3859 id : this.id || Roo.id(),
3862 if (this.parent().xtype.match(/^Nav/)) {
3863 cfg.cls = 'navbar-form navbar-' + this.align;
3867 if (this.labelAlign == 'left' ) {
3868 cfg.cls += ' form-horizontal';
3874 initEvents : function()
3876 this.el.on('submit', this.onSubmit, this);
3881 onSubmit : function(e){
3886 * Returns true if client-side validation on the form is successful.
3889 isValid : function(){
3890 var items = this.getItems();
3892 items.each(function(f){
3901 * Returns true if any fields in this form have changed since their original load.
3904 isDirty : function(){
3906 var items = this.getItems();
3907 items.each(function(f){
3917 * Performs a predefined action (submit or load) or custom actions you define on this form.
3918 * @param {String} actionName The name of the action type
3919 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3920 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3921 * accept other config options):
3923 Property Type Description
3924 ---------------- --------------- ----------------------------------------------------------------------------------
3925 url String The url for the action (defaults to the form's url)
3926 method String The form method to use (defaults to the form's method, or POST if not defined)
3927 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3928 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3929 validate the form on the client (defaults to false)
3931 * @return {BasicForm} this
3933 doAction : function(action, options){
3934 if(typeof action == 'string'){
3935 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3937 if(this.fireEvent('beforeaction', this, action) !== false){
3938 this.beforeAction(action);
3939 action.run.defer(100, action);
3945 beforeAction : function(action){
3946 var o = action.options;
3948 // not really supported yet.. ??
3950 //if(this.waitMsgTarget === true){
3951 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3952 //}else if(this.waitMsgTarget){
3953 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3954 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3956 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3962 afterAction : function(action, success){
3963 this.activeAction = null;
3964 var o = action.options;
3966 //if(this.waitMsgTarget === true){
3968 //}else if(this.waitMsgTarget){
3969 // this.waitMsgTarget.unmask();
3971 // Roo.MessageBox.updateProgress(1);
3972 // Roo.MessageBox.hide();
3979 Roo.callback(o.success, o.scope, [this, action]);
3980 this.fireEvent('actioncomplete', this, action);
3984 // failure condition..
3985 // we have a scenario where updates need confirming.
3986 // eg. if a locking scenario exists..
3987 // we look for { errors : { needs_confirm : true }} in the response.
3989 (typeof(action.result) != 'undefined') &&
3990 (typeof(action.result.errors) != 'undefined') &&
3991 (typeof(action.result.errors.needs_confirm) != 'undefined')
3994 Roo.log("not supported yet");
3997 Roo.MessageBox.confirm(
3998 "Change requires confirmation",
3999 action.result.errorMsg,
4004 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4014 Roo.callback(o.failure, o.scope, [this, action]);
4015 // show an error message if no failed handler is set..
4016 if (!this.hasListener('actionfailed')) {
4017 Roo.log("need to add dialog support");
4019 Roo.MessageBox.alert("Error",
4020 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4021 action.result.errorMsg :
4022 "Saving Failed, please check your entries or try again"
4027 this.fireEvent('actionfailed', this, action);
4032 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4033 * @param {String} id The value to search for
4036 findField : function(id){
4037 var items = this.getItems();
4038 var field = items.get(id);
4040 items.each(function(f){
4041 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4048 return field || null;
4051 * Mark fields in this form invalid in bulk.
4052 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4053 * @return {BasicForm} this
4055 markInvalid : function(errors){
4056 if(errors instanceof Array){
4057 for(var i = 0, len = errors.length; i < len; i++){
4058 var fieldError = errors[i];
4059 var f = this.findField(fieldError.id);
4061 f.markInvalid(fieldError.msg);
4067 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4068 field.markInvalid(errors[id]);
4072 //Roo.each(this.childForms || [], function (f) {
4073 // f.markInvalid(errors);
4080 * Set values for fields in this form in bulk.
4081 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4082 * @return {BasicForm} this
4084 setValues : function(values){
4085 if(values instanceof Array){ // array of objects
4086 for(var i = 0, len = values.length; i < len; i++){
4088 var f = this.findField(v.id);
4090 f.setValue(v.value);
4091 if(this.trackResetOnLoad){
4092 f.originalValue = f.getValue();
4096 }else{ // object hash
4099 if(typeof values[id] != 'function' && (field = this.findField(id))){
4101 if (field.setFromData &&
4103 field.displayField &&
4104 // combos' with local stores can
4105 // be queried via setValue()
4106 // to set their value..
4107 (field.store && !field.store.isLocal)
4111 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4112 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4113 field.setFromData(sd);
4116 field.setValue(values[id]);
4120 if(this.trackResetOnLoad){
4121 field.originalValue = field.getValue();
4127 //Roo.each(this.childForms || [], function (f) {
4128 // f.setValues(values);
4135 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4136 * they are returned as an array.
4137 * @param {Boolean} asString
4140 getValues : function(asString){
4141 //if (this.childForms) {
4142 // copy values from the child forms
4143 // Roo.each(this.childForms, function (f) {
4144 // this.setValues(f.getValues());
4150 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4151 if(asString === true){
4154 return Roo.urlDecode(fs);
4158 * Returns the fields in this form as an object with key/value pairs.
4159 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4162 getFieldValues : function(with_hidden)
4164 var items = this.getItems();
4166 items.each(function(f){
4170 var v = f.getValue();
4171 if (f.inputType =='radio') {
4172 if (typeof(ret[f.getName()]) == 'undefined') {
4173 ret[f.getName()] = ''; // empty..
4176 if (!f.el.dom.checked) {
4184 // not sure if this supported any more..
4185 if ((typeof(v) == 'object') && f.getRawValue) {
4186 v = f.getRawValue() ; // dates..
4188 // combo boxes where name != hiddenName...
4189 if (f.name != f.getName()) {
4190 ret[f.name] = f.getRawValue();
4192 ret[f.getName()] = v;
4199 * Clears all invalid messages in this form.
4200 * @return {BasicForm} this
4202 clearInvalid : function(){
4203 var items = this.getItems();
4205 items.each(function(f){
4216 * @return {BasicForm} this
4219 var items = this.getItems();
4220 items.each(function(f){
4224 Roo.each(this.childForms || [], function (f) {
4231 getItems : function()
4233 var r=new Roo.util.MixedCollection(false, function(o){
4234 return o.id || (o.id = Roo.id());
4236 var iter = function(el) {
4243 Roo.each(el.items,function(e) {
4262 * Ext JS Library 1.1.1
4263 * Copyright(c) 2006-2007, Ext JS, LLC.
4265 * Originally Released Under LGPL - original licence link has changed is not relivant.
4268 * <script type="text/javascript">
4271 * @class Roo.form.VTypes
4272 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4275 Roo.form.VTypes = function(){
4276 // closure these in so they are only created once.
4277 var alpha = /^[a-zA-Z_]+$/;
4278 var alphanum = /^[a-zA-Z0-9_]+$/;
4279 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4280 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4282 // All these messages and functions are configurable
4285 * The function used to validate email addresses
4286 * @param {String} value The email address
4288 'email' : function(v){
4289 return email.test(v);
4292 * The error text to display when the email validation function returns false
4295 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4297 * The keystroke filter mask to be applied on email input
4300 'emailMask' : /[a-z0-9_\.\-@]/i,
4303 * The function used to validate URLs
4304 * @param {String} value The URL
4306 'url' : function(v){
4310 * The error text to display when the url validation function returns false
4313 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4316 * The function used to validate alpha values
4317 * @param {String} value The value
4319 'alpha' : function(v){
4320 return alpha.test(v);
4323 * The error text to display when the alpha validation function returns false
4326 'alphaText' : 'This field should only contain letters and _',
4328 * The keystroke filter mask to be applied on alpha input
4331 'alphaMask' : /[a-z_]/i,
4334 * The function used to validate alphanumeric values
4335 * @param {String} value The value
4337 'alphanum' : function(v){
4338 return alphanum.test(v);
4341 * The error text to display when the alphanumeric validation function returns false
4344 'alphanumText' : 'This field should only contain letters, numbers and _',
4346 * The keystroke filter mask to be applied on alphanumeric input
4349 'alphanumMask' : /[a-z0-9_]/i
4359 * @class Roo.bootstrap.Input
4360 * @extends Roo.bootstrap.Component
4361 * Bootstrap Input class
4362 * @cfg {Boolean} disabled is it disabled
4363 * @cfg {String} fieldLabel - the label associated
4364 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4365 * @cfg {String} name name of the input
4366 * @cfg {string} fieldLabel - the label associated
4367 * @cfg {string} inputType - input / file submit ...
4368 * @cfg {string} placeholder - placeholder to put in text.
4369 * @cfg {string} before - input group add on before
4370 * @cfg {string} after - input group add on after
4371 * @cfg {string} size - (lg|sm) or leave empty..
4372 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4373 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4374 * @cfg {Number} md colspan out of 12 for computer-sized screens
4375 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4376 * @cfg {string} value default value of the input
4377 * @cfg {Number} labelWidth set the width of label (0-12)
4378 * @cfg {String} labelAlign (top|left)
4379 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4383 * Create a new Input
4384 * @param {Object} config The config object
4387 Roo.bootstrap.Input = function(config){
4388 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4393 * Fires when this field receives input focus.
4394 * @param {Roo.form.Field} this
4399 * Fires when this field loses input focus.
4400 * @param {Roo.form.Field} this
4405 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4406 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4407 * @param {Roo.form.Field} this
4408 * @param {Roo.EventObject} e The event object
4413 * Fires just before the field blurs if the field value has changed.
4414 * @param {Roo.form.Field} this
4415 * @param {Mixed} newValue The new value
4416 * @param {Mixed} oldValue The original value
4421 * Fires after the field has been marked as invalid.
4422 * @param {Roo.form.Field} this
4423 * @param {String} msg The validation message
4428 * Fires after the field has been validated with no errors.
4429 * @param {Roo.form.Field} this
4434 * Fires after the key up
4435 * @param {Roo.form.Field} this
4436 * @param {Roo.EventObject} e The event Object
4442 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4444 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4445 automatic validation (defaults to "keyup").
4447 validationEvent : "keyup",
4449 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4451 validateOnBlur : true,
4453 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4455 validationDelay : 250,
4457 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4459 focusClass : "x-form-focus", // not needed???
4463 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4465 invalidClass : "has-error",
4468 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4470 selectOnFocus : false,
4473 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4477 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4482 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4484 disableKeyFilter : false,
4487 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4491 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4495 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4497 blankText : "This field is required",
4500 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4504 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4506 maxLength : Number.MAX_VALUE,
4508 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4510 minLengthText : "The minimum length for this field is {0}",
4512 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4514 maxLengthText : "The maximum length for this field is {0}",
4518 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4519 * If available, this function will be called only after the basic validators all return true, and will be passed the
4520 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4524 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4525 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4526 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4530 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4553 parentLabelAlign : function()
4556 while (parent.parent()) {
4557 parent = parent.parent();
4558 if (typeof(parent.labelAlign) !='undefined') {
4559 return parent.labelAlign;
4566 getAutoCreate : function(){
4568 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4574 if(this.inputType != 'hidden'){
4575 cfg.cls = 'form-group' //input-group
4581 type : this.inputType,
4583 cls : 'form-control',
4584 placeholder : this.placeholder || ''
4588 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4589 input.maxLength = this.maxLength;
4592 if (this.disabled) {
4593 input.disabled=true;
4596 if (this.readOnly) {
4597 input.readonly=true;
4601 input.name = this.name;
4604 input.cls += ' input-' + this.size;
4607 ['xs','sm','md','lg'].map(function(size){
4608 if (settings[size]) {
4609 cfg.cls += ' col-' + size + '-' + settings[size];
4613 var inputblock = input;
4615 if (this.before || this.after) {
4618 cls : 'input-group',
4622 inputblock.cn.push({
4624 cls : 'input-group-addon',
4628 inputblock.cn.push(input);
4630 inputblock.cn.push({
4632 cls : 'input-group-addon',
4639 if (align ==='left' && this.fieldLabel.length) {
4640 Roo.log("left and has label");
4646 cls : 'control-label col-sm-' + this.labelWidth,
4647 html : this.fieldLabel
4651 cls : "col-sm-" + (12 - this.labelWidth),
4658 } else if ( this.fieldLabel.length) {
4664 //cls : 'input-group-addon',
4665 html : this.fieldLabel
4675 Roo.log(" no label && no align");
4684 Roo.log('input-parentType: ' + this.parentType);
4686 if (this.parentType === 'Navbar' && this.parent().bar) {
4687 cfg.cls += ' navbar-form';
4695 * return the real input element.
4697 inputEl: function ()
4699 return this.el.select('input.form-control',true).first();
4701 setDisabled : function(v)
4703 var i = this.inputEl().dom;
4705 i.removeAttribute('disabled');
4709 i.setAttribute('disabled','true');
4711 initEvents : function()
4714 this.inputEl().on("keydown" , this.fireKey, this);
4715 this.inputEl().on("focus", this.onFocus, this);
4716 this.inputEl().on("blur", this.onBlur, this);
4718 this.inputEl().relayEvent('keyup', this);
4720 // reference to original value for reset
4721 this.originalValue = this.getValue();
4722 //Roo.form.TextField.superclass.initEvents.call(this);
4723 if(this.validationEvent == 'keyup'){
4724 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4725 this.inputEl().on('keyup', this.filterValidation, this);
4727 else if(this.validationEvent !== false){
4728 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4731 if(this.selectOnFocus){
4732 this.on("focus", this.preFocus, this);
4735 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4736 this.inputEl().on("keypress", this.filterKeys, this);
4739 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4740 this.el.on("click", this.autoSize, this);
4743 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4744 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4748 filterValidation : function(e){
4749 if(!e.isNavKeyPress()){
4750 this.validationTask.delay(this.validationDelay);
4754 * Validates the field value
4755 * @return {Boolean} True if the value is valid, else false
4757 validate : function(){
4758 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4759 if(this.disabled || this.validateValue(this.getRawValue())){
4760 this.clearInvalid();
4768 * Validates a value according to the field's validation rules and marks the field as invalid
4769 * if the validation fails
4770 * @param {Mixed} value The value to validate
4771 * @return {Boolean} True if the value is valid, else false
4773 validateValue : function(value){
4774 if(value.length < 1) { // if it's blank
4775 if(this.allowBlank){
4776 this.clearInvalid();
4779 this.markInvalid(this.blankText);
4783 if(value.length < this.minLength){
4784 this.markInvalid(String.format(this.minLengthText, this.minLength));
4787 if(value.length > this.maxLength){
4788 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4792 var vt = Roo.form.VTypes;
4793 if(!vt[this.vtype](value, this)){
4794 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4798 if(typeof this.validator == "function"){
4799 var msg = this.validator(value);
4801 this.markInvalid(msg);
4805 if(this.regex && !this.regex.test(value)){
4806 this.markInvalid(this.regexText);
4815 fireKey : function(e){
4816 //Roo.log('field ' + e.getKey());
4817 if(e.isNavKeyPress()){
4818 this.fireEvent("specialkey", this, e);
4821 focus : function (selectText){
4823 this.inputEl().focus();
4824 if(selectText === true){
4825 this.inputEl().dom.select();
4831 onFocus : function(){
4832 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4833 // this.el.addClass(this.focusClass);
4836 this.hasFocus = true;
4837 this.startValue = this.getValue();
4838 this.fireEvent("focus", this);
4842 beforeBlur : Roo.emptyFn,
4846 onBlur : function(){
4848 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4849 //this.el.removeClass(this.focusClass);
4851 this.hasFocus = false;
4852 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4855 var v = this.getValue();
4856 if(String(v) !== String(this.startValue)){
4857 this.fireEvent('change', this, v, this.startValue);
4859 this.fireEvent("blur", this);
4863 * Resets the current field value to the originally loaded value and clears any validation messages
4866 this.setValue(this.originalValue);
4867 this.clearInvalid();
4870 * Returns the name of the field
4871 * @return {Mixed} name The name field
4873 getName: function(){
4877 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4878 * @return {Mixed} value The field value
4880 getValue : function(){
4881 return this.inputEl().getValue();
4884 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4885 * @return {Mixed} value The field value
4887 getRawValue : function(){
4888 var v = this.inputEl().getValue();
4894 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4895 * @param {Mixed} value The value to set
4897 setRawValue : function(v){
4898 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4901 selectText : function(start, end){
4902 var v = this.getRawValue();
4904 start = start === undefined ? 0 : start;
4905 end = end === undefined ? v.length : end;
4906 var d = this.inputEl().dom;
4907 if(d.setSelectionRange){
4908 d.setSelectionRange(start, end);
4909 }else if(d.createTextRange){
4910 var range = d.createTextRange();
4911 range.moveStart("character", start);
4912 range.moveEnd("character", v.length-end);
4919 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4920 * @param {Mixed} value The value to set
4922 setValue : function(v){
4925 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4931 processValue : function(value){
4932 if(this.stripCharsRe){
4933 var newValue = value.replace(this.stripCharsRe, '');
4934 if(newValue !== value){
4935 this.setRawValue(newValue);
4942 preFocus : function(){
4944 if(this.selectOnFocus){
4945 this.inputEl().dom.select();
4948 filterKeys : function(e){
4950 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4953 var c = e.getCharCode(), cc = String.fromCharCode(c);
4954 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4957 if(!this.maskRe.test(cc)){
4962 * Clear any invalid styles/messages for this field
4964 clearInvalid : function(){
4966 if(!this.el || this.preventMark){ // not rendered
4969 this.el.removeClass(this.invalidClass);
4971 switch(this.msgTarget){
4973 this.el.dom.qtip = '';
4976 this.el.dom.title = '';
4980 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4985 this.errorIcon.dom.qtip = '';
4986 this.errorIcon.hide();
4987 this.un('resize', this.alignErrorIcon, this);
4991 var t = Roo.getDom(this.msgTarget);
4993 t.style.display = 'none';
4997 this.fireEvent('valid', this);
5000 * Mark this field as invalid
5001 * @param {String} msg The validation message
5003 markInvalid : function(msg){
5004 if(!this.el || this.preventMark){ // not rendered
5007 this.el.addClass(this.invalidClass);
5009 msg = msg || this.invalidText;
5010 switch(this.msgTarget){
5012 this.el.dom.qtip = msg;
5013 this.el.dom.qclass = 'x-form-invalid-tip';
5014 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5015 Roo.QuickTips.enable();
5019 this.el.dom.title = msg;
5023 var elp = this.el.findParent('.x-form-element', 5, true);
5024 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5025 this.errorEl.setWidth(elp.getWidth(true)-20);
5027 this.errorEl.update(msg);
5028 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5031 if(!this.errorIcon){
5032 var elp = this.el.findParent('.x-form-element', 5, true);
5033 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5035 this.alignErrorIcon();
5036 this.errorIcon.dom.qtip = msg;
5037 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5038 this.errorIcon.show();
5039 this.on('resize', this.alignErrorIcon, this);
5042 var t = Roo.getDom(this.msgTarget);
5044 t.style.display = this.msgDisplay;
5048 this.fireEvent('invalid', this, msg);
5051 SafariOnKeyDown : function(event)
5053 // this is a workaround for a password hang bug on chrome/ webkit.
5055 var isSelectAll = false;
5057 if(this.inputEl().dom.selectionEnd > 0){
5058 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5060 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5061 event.preventDefault();
5066 if(isSelectAll){ // backspace and delete key
5068 event.preventDefault();
5069 // this is very hacky as keydown always get's upper case.
5071 var cc = String.fromCharCode(event.getCharCode());
5072 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5076 adjustWidth : function(tag, w){
5077 tag = tag.toLowerCase();
5078 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5079 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5083 if(tag == 'textarea'){
5086 }else if(Roo.isOpera){
5090 if(tag == 'textarea'){
5109 * @class Roo.bootstrap.TextArea
5110 * @extends Roo.bootstrap.Input
5111 * Bootstrap TextArea class
5112 * @cfg {Number} cols Specifies the visible width of a text area
5113 * @cfg {Number} rows Specifies the visible number of lines in a text area
5114 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5115 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5116 * @cfg {string} html text
5119 * Create a new TextArea
5120 * @param {Object} config The config object
5123 Roo.bootstrap.TextArea = function(config){
5124 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5128 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5138 getAutoCreate : function(){
5140 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5151 value : this.value || '',
5152 html: this.html || '',
5153 cls : 'form-control',
5154 placeholder : this.placeholder || ''
5158 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5159 input.maxLength = this.maxLength;
5163 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5167 input.cols = this.cols;
5170 if (this.readOnly) {
5171 input.readonly = true;
5175 input.name = this.name;
5179 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5183 ['xs','sm','md','lg'].map(function(size){
5184 if (settings[size]) {
5185 cfg.cls += ' col-' + size + '-' + settings[size];
5189 var inputblock = input;
5191 if (this.before || this.after) {
5194 cls : 'input-group',
5198 inputblock.cn.push({
5200 cls : 'input-group-addon',
5204 inputblock.cn.push(input);
5206 inputblock.cn.push({
5208 cls : 'input-group-addon',
5215 if (align ==='left' && this.fieldLabel.length) {
5216 Roo.log("left and has label");
5222 cls : 'control-label col-sm-' + this.labelWidth,
5223 html : this.fieldLabel
5227 cls : "col-sm-" + (12 - this.labelWidth),
5234 } else if ( this.fieldLabel.length) {
5240 //cls : 'input-group-addon',
5241 html : this.fieldLabel
5251 Roo.log(" no label && no align");
5261 if (this.disabled) {
5262 input.disabled=true;
5269 * return the real textarea element.
5271 inputEl: function ()
5273 return this.el.select('textarea.form-control',true).first();
5281 * trigger field - base class for combo..
5286 * @class Roo.bootstrap.TriggerField
5287 * @extends Roo.bootstrap.Input
5288 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5289 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5290 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5291 * for which you can provide a custom implementation. For example:
5293 var trigger = new Roo.bootstrap.TriggerField();
5294 trigger.onTriggerClick = myTriggerFn;
5295 trigger.applyTo('my-field');
5298 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5299 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5300 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5301 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5303 * Create a new TriggerField.
5304 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5305 * to the base TextField)
5307 Roo.bootstrap.TriggerField = function(config){
5308 this.mimicing = false;
5309 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5312 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5314 * @cfg {String} triggerClass A CSS class to apply to the trigger
5317 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5321 /** @cfg {Boolean} grow @hide */
5322 /** @cfg {Number} growMin @hide */
5323 /** @cfg {Number} growMax @hide */
5329 autoSize: Roo.emptyFn,
5336 actionMode : 'wrap',
5340 getAutoCreate : function(){
5342 var parent = this.parent();
5344 var align = this.parentLabelAlign();
5349 cls: 'form-group' //input-group
5356 type : this.inputType,
5357 cls : 'form-control',
5358 autocomplete: 'off',
5359 placeholder : this.placeholder || ''
5363 input.name = this.name;
5366 input.cls += ' input-' + this.size;
5369 if (this.disabled) {
5370 input.disabled=true;
5373 var inputblock = input;
5375 if (this.before || this.after) {
5378 cls : 'input-group',
5382 inputblock.cn.push({
5384 cls : 'input-group-addon',
5388 inputblock.cn.push(input);
5390 inputblock.cn.push({
5392 cls : 'input-group-addon',
5405 cls: 'form-hidden-field'
5413 Roo.log('multiple');
5421 cls: 'form-hidden-field'
5425 cls: 'select2-choices',
5429 cls: 'select2-search-field',
5442 cls: 'select2-container input-group',
5447 cls: 'typeahead typeahead-long dropdown-menu',
5448 style: 'display:none'
5456 cls : 'input-group-addon btn dropdown-toggle',
5464 cls: 'combobox-clear',
5478 combobox.cls += ' select2-container-multi';
5481 if (align ==='left' && this.fieldLabel.length) {
5483 Roo.log("left and has label");
5489 cls : 'control-label col-sm-' + this.labelWidth,
5490 html : this.fieldLabel
5494 cls : "col-sm-" + (12 - this.labelWidth),
5501 } else if ( this.fieldLabel.length) {
5507 //cls : 'input-group-addon',
5508 html : this.fieldLabel
5518 Roo.log(" no label && no align");
5525 ['xs','sm','md','lg'].map(function(size){
5526 if (settings[size]) {
5527 cfg.cls += ' col-' + size + '-' + settings[size];
5538 onResize : function(w, h){
5539 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5540 // if(typeof w == 'number'){
5541 // var x = w - this.trigger.getWidth();
5542 // this.inputEl().setWidth(this.adjustWidth('input', x));
5543 // this.trigger.setStyle('left', x+'px');
5548 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5551 getResizeEl : function(){
5552 return this.inputEl();
5556 getPositionEl : function(){
5557 return this.inputEl();
5561 alignErrorIcon : function(){
5562 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5566 initEvents : function(){
5568 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5569 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5571 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5572 if(this.hideTrigger){
5573 this.trigger.setDisplayed(false);
5575 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5579 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5582 //this.trigger.addClassOnOver('x-form-trigger-over');
5583 //this.trigger.addClassOnClick('x-form-trigger-click');
5586 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5591 initTrigger : function(){
5596 onDestroy : function(){
5598 this.trigger.removeAllListeners();
5599 // this.trigger.remove();
5602 // this.wrap.remove();
5604 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5608 onFocus : function(){
5609 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5612 this.wrap.addClass('x-trigger-wrap-focus');
5613 this.mimicing = true;
5614 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5615 if(this.monitorTab){
5616 this.el.on("keydown", this.checkTab, this);
5623 checkTab : function(e){
5624 if(e.getKey() == e.TAB){
5630 onBlur : function(){
5635 mimicBlur : function(e, t){
5637 if(!this.wrap.contains(t) && this.validateBlur()){
5644 triggerBlur : function(){
5645 this.mimicing = false;
5646 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5647 if(this.monitorTab){
5648 this.el.un("keydown", this.checkTab, this);
5650 //this.wrap.removeClass('x-trigger-wrap-focus');
5651 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5655 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5656 validateBlur : function(e, t){
5661 onDisable : function(){
5662 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5664 // this.wrap.addClass('x-item-disabled');
5669 onEnable : function(){
5670 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5672 // this.el.removeClass('x-item-disabled');
5677 onShow : function(){
5678 var ae = this.getActionEl();
5681 ae.dom.style.display = '';
5682 ae.dom.style.visibility = 'visible';
5688 onHide : function(){
5689 var ae = this.getActionEl();
5690 ae.dom.style.display = 'none';
5694 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5695 * by an implementing function.
5697 * @param {EventObject} e
5699 onTriggerClick : Roo.emptyFn
5703 * Ext JS Library 1.1.1
5704 * Copyright(c) 2006-2007, Ext JS, LLC.
5706 * Originally Released Under LGPL - original licence link has changed is not relivant.
5709 * <script type="text/javascript">
5714 * @class Roo.data.SortTypes
5716 * Defines the default sorting (casting?) comparison functions used when sorting data.
5718 Roo.data.SortTypes = {
5720 * Default sort that does nothing
5721 * @param {Mixed} s The value being converted
5722 * @return {Mixed} The comparison value
5729 * The regular expression used to strip tags
5733 stripTagsRE : /<\/?[^>]+>/gi,
5736 * Strips all HTML tags to sort on text only
5737 * @param {Mixed} s The value being converted
5738 * @return {String} The comparison value
5740 asText : function(s){
5741 return String(s).replace(this.stripTagsRE, "");
5745 * Strips all HTML tags to sort on text only - Case insensitive
5746 * @param {Mixed} s The value being converted
5747 * @return {String} The comparison value
5749 asUCText : function(s){
5750 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5754 * Case insensitive string
5755 * @param {Mixed} s The value being converted
5756 * @return {String} The comparison value
5758 asUCString : function(s) {
5759 return String(s).toUpperCase();
5764 * @param {Mixed} s The value being converted
5765 * @return {Number} The comparison value
5767 asDate : function(s) {
5771 if(s instanceof Date){
5774 return Date.parse(String(s));
5779 * @param {Mixed} s The value being converted
5780 * @return {Float} The comparison value
5782 asFloat : function(s) {
5783 var val = parseFloat(String(s).replace(/,/g, ""));
5784 if(isNaN(val)) val = 0;
5790 * @param {Mixed} s The value being converted
5791 * @return {Number} The comparison value
5793 asInt : function(s) {
5794 var val = parseInt(String(s).replace(/,/g, ""));
5795 if(isNaN(val)) val = 0;
5800 * Ext JS Library 1.1.1
5801 * Copyright(c) 2006-2007, Ext JS, LLC.
5803 * Originally Released Under LGPL - original licence link has changed is not relivant.
5806 * <script type="text/javascript">
5810 * @class Roo.data.Record
5811 * Instances of this class encapsulate both record <em>definition</em> information, and record
5812 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5813 * to access Records cached in an {@link Roo.data.Store} object.<br>
5815 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5816 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5819 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5821 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5822 * {@link #create}. The parameters are the same.
5823 * @param {Array} data An associative Array of data values keyed by the field name.
5824 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5825 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5826 * not specified an integer id is generated.
5828 Roo.data.Record = function(data, id){
5829 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5834 * Generate a constructor for a specific record layout.
5835 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5836 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5837 * Each field definition object may contain the following properties: <ul>
5838 * <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,
5839 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5840 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5841 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5842 * is being used, then this is a string containing the javascript expression to reference the data relative to
5843 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5844 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5845 * this may be omitted.</p></li>
5846 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5847 * <ul><li>auto (Default, implies no conversion)</li>
5852 * <li>date</li></ul></p></li>
5853 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5854 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5855 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5856 * by the Reader into an object that will be stored in the Record. It is passed the
5857 * following parameters:<ul>
5858 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5860 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5862 * <br>usage:<br><pre><code>
5863 var TopicRecord = Roo.data.Record.create(
5864 {name: 'title', mapping: 'topic_title'},
5865 {name: 'author', mapping: 'username'},
5866 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5867 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5868 {name: 'lastPoster', mapping: 'user2'},
5869 {name: 'excerpt', mapping: 'post_text'}
5872 var myNewRecord = new TopicRecord({
5873 title: 'Do my job please',
5876 lastPost: new Date(),
5877 lastPoster: 'Animal',
5878 excerpt: 'No way dude!'
5880 myStore.add(myNewRecord);
5885 Roo.data.Record.create = function(o){
5887 f.superclass.constructor.apply(this, arguments);
5889 Roo.extend(f, Roo.data.Record);
5890 var p = f.prototype;
5891 p.fields = new Roo.util.MixedCollection(false, function(field){
5894 for(var i = 0, len = o.length; i < len; i++){
5895 p.fields.add(new Roo.data.Field(o[i]));
5897 f.getField = function(name){
5898 return p.fields.get(name);
5903 Roo.data.Record.AUTO_ID = 1000;
5904 Roo.data.Record.EDIT = 'edit';
5905 Roo.data.Record.REJECT = 'reject';
5906 Roo.data.Record.COMMIT = 'commit';
5908 Roo.data.Record.prototype = {
5910 * Readonly flag - true if this record has been modified.
5919 join : function(store){
5924 * Set the named field to the specified value.
5925 * @param {String} name The name of the field to set.
5926 * @param {Object} value The value to set the field to.
5928 set : function(name, value){
5929 if(this.data[name] == value){
5936 if(typeof this.modified[name] == 'undefined'){
5937 this.modified[name] = this.data[name];
5939 this.data[name] = value;
5940 if(!this.editing && this.store){
5941 this.store.afterEdit(this);
5946 * Get the value of the named field.
5947 * @param {String} name The name of the field to get the value of.
5948 * @return {Object} The value of the field.
5950 get : function(name){
5951 return this.data[name];
5955 beginEdit : function(){
5956 this.editing = true;
5961 cancelEdit : function(){
5962 this.editing = false;
5963 delete this.modified;
5967 endEdit : function(){
5968 this.editing = false;
5969 if(this.dirty && this.store){
5970 this.store.afterEdit(this);
5975 * Usually called by the {@link Roo.data.Store} which owns the Record.
5976 * Rejects all changes made to the Record since either creation, or the last commit operation.
5977 * Modified fields are reverted to their original values.
5979 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5980 * of reject operations.
5982 reject : function(){
5983 var m = this.modified;
5985 if(typeof m[n] != "function"){
5986 this.data[n] = m[n];
5990 delete this.modified;
5991 this.editing = false;
5993 this.store.afterReject(this);
5998 * Usually called by the {@link Roo.data.Store} which owns the Record.
5999 * Commits all changes made to the Record since either creation, or the last commit operation.
6001 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6002 * of commit operations.
6004 commit : function(){
6006 delete this.modified;
6007 this.editing = false;
6009 this.store.afterCommit(this);
6014 hasError : function(){
6015 return this.error != null;
6019 clearError : function(){
6024 * Creates a copy of this record.
6025 * @param {String} id (optional) A new record id if you don't want to use this record's id
6028 copy : function(newId) {
6029 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6033 * Ext JS Library 1.1.1
6034 * Copyright(c) 2006-2007, Ext JS, LLC.
6036 * Originally Released Under LGPL - original licence link has changed is not relivant.
6039 * <script type="text/javascript">
6045 * @class Roo.data.Store
6046 * @extends Roo.util.Observable
6047 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6048 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6050 * 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
6051 * has no knowledge of the format of the data returned by the Proxy.<br>
6053 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6054 * instances from the data object. These records are cached and made available through accessor functions.
6056 * Creates a new Store.
6057 * @param {Object} config A config object containing the objects needed for the Store to access data,
6058 * and read the data into Records.
6060 Roo.data.Store = function(config){
6061 this.data = new Roo.util.MixedCollection(false);
6062 this.data.getKey = function(o){
6065 this.baseParams = {};
6072 "multisort" : "_multisort"
6075 if(config && config.data){
6076 this.inlineData = config.data;
6080 Roo.apply(this, config);
6082 if(this.reader){ // reader passed
6083 this.reader = Roo.factory(this.reader, Roo.data);
6084 this.reader.xmodule = this.xmodule || false;
6085 if(!this.recordType){
6086 this.recordType = this.reader.recordType;
6088 if(this.reader.onMetaChange){
6089 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6093 if(this.recordType){
6094 this.fields = this.recordType.prototype.fields;
6100 * @event datachanged
6101 * Fires when the data cache has changed, and a widget which is using this Store
6102 * as a Record cache should refresh its view.
6103 * @param {Store} this
6108 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6109 * @param {Store} this
6110 * @param {Object} meta The JSON metadata
6115 * Fires when Records have been added to the Store
6116 * @param {Store} this
6117 * @param {Roo.data.Record[]} records The array of Records added
6118 * @param {Number} index The index at which the record(s) were added
6123 * Fires when a Record has been removed from the Store
6124 * @param {Store} this
6125 * @param {Roo.data.Record} record The Record that was removed
6126 * @param {Number} index The index at which the record was removed
6131 * Fires when a Record has been updated
6132 * @param {Store} this
6133 * @param {Roo.data.Record} record The Record that was updated
6134 * @param {String} operation The update operation being performed. Value may be one of:
6136 Roo.data.Record.EDIT
6137 Roo.data.Record.REJECT
6138 Roo.data.Record.COMMIT
6144 * Fires when the data cache has been cleared.
6145 * @param {Store} this
6150 * Fires before a request is made for a new data object. If the beforeload handler returns false
6151 * the load action will be canceled.
6152 * @param {Store} this
6153 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6157 * @event beforeloadadd
6158 * Fires after a new set of Records has been loaded.
6159 * @param {Store} this
6160 * @param {Roo.data.Record[]} records The Records that were loaded
6161 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6163 beforeloadadd : true,
6166 * Fires after a new set of Records has been loaded, before they are added to the store.
6167 * @param {Store} this
6168 * @param {Roo.data.Record[]} records The Records that were loaded
6169 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6170 * @params {Object} return from reader
6174 * @event loadexception
6175 * Fires if an exception occurs in the Proxy during loading.
6176 * Called with the signature of the Proxy's "loadexception" event.
6177 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6180 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6181 * @param {Object} load options
6182 * @param {Object} jsonData from your request (normally this contains the Exception)
6184 loadexception : true
6188 this.proxy = Roo.factory(this.proxy, Roo.data);
6189 this.proxy.xmodule = this.xmodule || false;
6190 this.relayEvents(this.proxy, ["loadexception"]);
6192 this.sortToggle = {};
6193 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6195 Roo.data.Store.superclass.constructor.call(this);
6197 if(this.inlineData){
6198 this.loadData(this.inlineData);
6199 delete this.inlineData;
6203 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6205 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6206 * without a remote query - used by combo/forms at present.
6210 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6213 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6216 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6217 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6220 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6221 * on any HTTP request
6224 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6227 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6231 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6232 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6237 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6238 * loaded or when a record is removed. (defaults to false).
6240 pruneModifiedRecords : false,
6246 * Add Records to the Store and fires the add event.
6247 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6249 add : function(records){
6250 records = [].concat(records);
6251 for(var i = 0, len = records.length; i < len; i++){
6252 records[i].join(this);
6254 var index = this.data.length;
6255 this.data.addAll(records);
6256 this.fireEvent("add", this, records, index);
6260 * Remove a Record from the Store and fires the remove event.
6261 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6263 remove : function(record){
6264 var index = this.data.indexOf(record);
6265 this.data.removeAt(index);
6266 if(this.pruneModifiedRecords){
6267 this.modified.remove(record);
6269 this.fireEvent("remove", this, record, index);
6273 * Remove all Records from the Store and fires the clear event.
6275 removeAll : function(){
6277 if(this.pruneModifiedRecords){
6280 this.fireEvent("clear", this);
6284 * Inserts Records to the Store at the given index and fires the add event.
6285 * @param {Number} index The start index at which to insert the passed Records.
6286 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6288 insert : function(index, records){
6289 records = [].concat(records);
6290 for(var i = 0, len = records.length; i < len; i++){
6291 this.data.insert(index, records[i]);
6292 records[i].join(this);
6294 this.fireEvent("add", this, records, index);
6298 * Get the index within the cache of the passed Record.
6299 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6300 * @return {Number} The index of the passed Record. Returns -1 if not found.
6302 indexOf : function(record){
6303 return this.data.indexOf(record);
6307 * Get the index within the cache of the Record with the passed id.
6308 * @param {String} id The id of the Record to find.
6309 * @return {Number} The index of the Record. Returns -1 if not found.
6311 indexOfId : function(id){
6312 return this.data.indexOfKey(id);
6316 * Get the Record with the specified id.
6317 * @param {String} id The id of the Record to find.
6318 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6320 getById : function(id){
6321 return this.data.key(id);
6325 * Get the Record at the specified index.
6326 * @param {Number} index The index of the Record to find.
6327 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6329 getAt : function(index){
6330 return this.data.itemAt(index);
6334 * Returns a range of Records between specified indices.
6335 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6336 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6337 * @return {Roo.data.Record[]} An array of Records
6339 getRange : function(start, end){
6340 return this.data.getRange(start, end);
6344 storeOptions : function(o){
6345 o = Roo.apply({}, o);
6348 this.lastOptions = o;
6352 * Loads the Record cache from the configured Proxy using the configured Reader.
6354 * If using remote paging, then the first load call must specify the <em>start</em>
6355 * and <em>limit</em> properties in the options.params property to establish the initial
6356 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6358 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6359 * and this call will return before the new data has been loaded. Perform any post-processing
6360 * in a callback function, or in a "load" event handler.</strong>
6362 * @param {Object} options An object containing properties which control loading options:<ul>
6363 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6364 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6365 * passed the following arguments:<ul>
6366 * <li>r : Roo.data.Record[]</li>
6367 * <li>options: Options object from the load call</li>
6368 * <li>success: Boolean success indicator</li></ul></li>
6369 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6370 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6373 load : function(options){
6374 options = options || {};
6375 if(this.fireEvent("beforeload", this, options) !== false){
6376 this.storeOptions(options);
6377 var p = Roo.apply(options.params || {}, this.baseParams);
6378 // if meta was not loaded from remote source.. try requesting it.
6379 if (!this.reader.metaFromRemote) {
6382 if(this.sortInfo && this.remoteSort){
6383 var pn = this.paramNames;
6384 p[pn["sort"]] = this.sortInfo.field;
6385 p[pn["dir"]] = this.sortInfo.direction;
6387 if (this.multiSort) {
6388 var pn = this.paramNames;
6389 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6392 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6397 * Reloads the Record cache from the configured Proxy using the configured Reader and
6398 * the options from the last load operation performed.
6399 * @param {Object} options (optional) An object containing properties which may override the options
6400 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6401 * the most recently used options are reused).
6403 reload : function(options){
6404 this.load(Roo.applyIf(options||{}, this.lastOptions));
6408 // Called as a callback by the Reader during a load operation.
6409 loadRecords : function(o, options, success){
6410 if(!o || success === false){
6411 if(success !== false){
6412 this.fireEvent("load", this, [], options, o);
6414 if(options.callback){
6415 options.callback.call(options.scope || this, [], options, false);
6419 // if data returned failure - throw an exception.
6420 if (o.success === false) {
6421 // show a message if no listener is registered.
6422 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6423 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6425 // loadmask wil be hooked into this..
6426 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6429 var r = o.records, t = o.totalRecords || r.length;
6431 this.fireEvent("beforeloadadd", this, r, options, o);
6433 if(!options || options.add !== true){
6434 if(this.pruneModifiedRecords){
6437 for(var i = 0, len = r.length; i < len; i++){
6441 this.data = this.snapshot;
6442 delete this.snapshot;
6445 this.data.addAll(r);
6446 this.totalLength = t;
6448 this.fireEvent("datachanged", this);
6450 this.totalLength = Math.max(t, this.data.length+r.length);
6453 this.fireEvent("load", this, r, options, o);
6454 if(options.callback){
6455 options.callback.call(options.scope || this, r, options, true);
6461 * Loads data from a passed data block. A Reader which understands the format of the data
6462 * must have been configured in the constructor.
6463 * @param {Object} data The data block from which to read the Records. The format of the data expected
6464 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6465 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6467 loadData : function(o, append){
6468 var r = this.reader.readRecords(o);
6469 this.loadRecords(r, {add: append}, true);
6473 * Gets the number of cached records.
6475 * <em>If using paging, this may not be the total size of the dataset. If the data object
6476 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6477 * the data set size</em>
6479 getCount : function(){
6480 return this.data.length || 0;
6484 * Gets the total number of records in the dataset as returned by the server.
6486 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6487 * the dataset size</em>
6489 getTotalCount : function(){
6490 return this.totalLength || 0;
6494 * Returns the sort state of the Store as an object with two properties:
6496 field {String} The name of the field by which the Records are sorted
6497 direction {String} The sort order, "ASC" or "DESC"
6500 getSortState : function(){
6501 return this.sortInfo;
6505 applySort : function(){
6506 if(this.sortInfo && !this.remoteSort){
6507 var s = this.sortInfo, f = s.field;
6508 var st = this.fields.get(f).sortType;
6509 var fn = function(r1, r2){
6510 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6511 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6513 this.data.sort(s.direction, fn);
6514 if(this.snapshot && this.snapshot != this.data){
6515 this.snapshot.sort(s.direction, fn);
6521 * Sets the default sort column and order to be used by the next load operation.
6522 * @param {String} fieldName The name of the field to sort by.
6523 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6525 setDefaultSort : function(field, dir){
6526 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6531 * If remote sorting is used, the sort is performed on the server, and the cache is
6532 * reloaded. If local sorting is used, the cache is sorted internally.
6533 * @param {String} fieldName The name of the field to sort by.
6534 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6536 sort : function(fieldName, dir){
6537 var f = this.fields.get(fieldName);
6539 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6541 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6542 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6547 this.sortToggle[f.name] = dir;
6548 this.sortInfo = {field: f.name, direction: dir};
6549 if(!this.remoteSort){
6551 this.fireEvent("datachanged", this);
6553 this.load(this.lastOptions);
6558 * Calls the specified function for each of the Records in the cache.
6559 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6560 * Returning <em>false</em> aborts and exits the iteration.
6561 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6563 each : function(fn, scope){
6564 this.data.each(fn, scope);
6568 * Gets all records modified since the last commit. Modified records are persisted across load operations
6569 * (e.g., during paging).
6570 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6572 getModifiedRecords : function(){
6573 return this.modified;
6577 createFilterFn : function(property, value, anyMatch){
6578 if(!value.exec){ // not a regex
6579 value = String(value);
6580 if(value.length == 0){
6583 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6586 return value.test(r.data[property]);
6591 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6592 * @param {String} property A field on your records
6593 * @param {Number} start The record index to start at (defaults to 0)
6594 * @param {Number} end The last record index to include (defaults to length - 1)
6595 * @return {Number} The sum
6597 sum : function(property, start, end){
6598 var rs = this.data.items, v = 0;
6600 end = (end || end === 0) ? end : rs.length-1;
6602 for(var i = start; i <= end; i++){
6603 v += (rs[i].data[property] || 0);
6609 * Filter the records by a specified property.
6610 * @param {String} field A field on your records
6611 * @param {String/RegExp} value Either a string that the field
6612 * should start with or a RegExp to test against the field
6613 * @param {Boolean} anyMatch True to match any part not just the beginning
6615 filter : function(property, value, anyMatch){
6616 var fn = this.createFilterFn(property, value, anyMatch);
6617 return fn ? this.filterBy(fn) : this.clearFilter();
6621 * Filter by a function. The specified function will be called with each
6622 * record in this data source. If the function returns true the record is included,
6623 * otherwise it is filtered.
6624 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6625 * @param {Object} scope (optional) The scope of the function (defaults to this)
6627 filterBy : function(fn, scope){
6628 this.snapshot = this.snapshot || this.data;
6629 this.data = this.queryBy(fn, scope||this);
6630 this.fireEvent("datachanged", this);
6634 * Query the records by a specified property.
6635 * @param {String} field A field on your records
6636 * @param {String/RegExp} value Either a string that the field
6637 * should start with or a RegExp to test against the field
6638 * @param {Boolean} anyMatch True to match any part not just the beginning
6639 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6641 query : function(property, value, anyMatch){
6642 var fn = this.createFilterFn(property, value, anyMatch);
6643 return fn ? this.queryBy(fn) : this.data.clone();
6647 * Query by a function. The specified function will be called with each
6648 * record in this data source. If the function returns true the record is included
6650 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6651 * @param {Object} scope (optional) The scope of the function (defaults to this)
6652 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6654 queryBy : function(fn, scope){
6655 var data = this.snapshot || this.data;
6656 return data.filterBy(fn, scope||this);
6660 * Collects unique values for a particular dataIndex from this store.
6661 * @param {String} dataIndex The property to collect
6662 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6663 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6664 * @return {Array} An array of the unique values
6666 collect : function(dataIndex, allowNull, bypassFilter){
6667 var d = (bypassFilter === true && this.snapshot) ?
6668 this.snapshot.items : this.data.items;
6669 var v, sv, r = [], l = {};
6670 for(var i = 0, len = d.length; i < len; i++){
6671 v = d[i].data[dataIndex];
6673 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6682 * Revert to a view of the Record cache with no filtering applied.
6683 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6685 clearFilter : function(suppressEvent){
6686 if(this.snapshot && this.snapshot != this.data){
6687 this.data = this.snapshot;
6688 delete this.snapshot;
6689 if(suppressEvent !== true){
6690 this.fireEvent("datachanged", this);
6696 afterEdit : function(record){
6697 if(this.modified.indexOf(record) == -1){
6698 this.modified.push(record);
6700 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6704 afterReject : function(record){
6705 this.modified.remove(record);
6706 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6710 afterCommit : function(record){
6711 this.modified.remove(record);
6712 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6716 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6717 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6719 commitChanges : function(){
6720 var m = this.modified.slice(0);
6722 for(var i = 0, len = m.length; i < len; i++){
6728 * Cancel outstanding changes on all changed records.
6730 rejectChanges : function(){
6731 var m = this.modified.slice(0);
6733 for(var i = 0, len = m.length; i < len; i++){
6738 onMetaChange : function(meta, rtype, o){
6739 this.recordType = rtype;
6740 this.fields = rtype.prototype.fields;
6741 delete this.snapshot;
6742 this.sortInfo = meta.sortInfo || this.sortInfo;
6744 this.fireEvent('metachange', this, this.reader.meta);
6747 moveIndex : function(data, type)
6749 var index = this.indexOf(data);
6751 var newIndex = index + type;
6755 this.insert(newIndex, data);
6760 * Ext JS Library 1.1.1
6761 * Copyright(c) 2006-2007, Ext JS, LLC.
6763 * Originally Released Under LGPL - original licence link has changed is not relivant.
6766 * <script type="text/javascript">
6770 * @class Roo.data.SimpleStore
6771 * @extends Roo.data.Store
6772 * Small helper class to make creating Stores from Array data easier.
6773 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6774 * @cfg {Array} fields An array of field definition objects, or field name strings.
6775 * @cfg {Array} data The multi-dimensional array of data
6777 * @param {Object} config
6779 Roo.data.SimpleStore = function(config){
6780 Roo.data.SimpleStore.superclass.constructor.call(this, {
6782 reader: new Roo.data.ArrayReader({
6785 Roo.data.Record.create(config.fields)
6787 proxy : new Roo.data.MemoryProxy(config.data)
6791 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6793 * Ext JS Library 1.1.1
6794 * Copyright(c) 2006-2007, Ext JS, LLC.
6796 * Originally Released Under LGPL - original licence link has changed is not relivant.
6799 * <script type="text/javascript">
6804 * @extends Roo.data.Store
6805 * @class Roo.data.JsonStore
6806 * Small helper class to make creating Stores for JSON data easier. <br/>
6808 var store = new Roo.data.JsonStore({
6809 url: 'get-images.php',
6811 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6814 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6815 * JsonReader and HttpProxy (unless inline data is provided).</b>
6816 * @cfg {Array} fields An array of field definition objects, or field name strings.
6818 * @param {Object} config
6820 Roo.data.JsonStore = function(c){
6821 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6822 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6823 reader: new Roo.data.JsonReader(c, c.fields)
6826 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6828 * Ext JS Library 1.1.1
6829 * Copyright(c) 2006-2007, Ext JS, LLC.
6831 * Originally Released Under LGPL - original licence link has changed is not relivant.
6834 * <script type="text/javascript">
6838 Roo.data.Field = function(config){
6839 if(typeof config == "string"){
6840 config = {name: config};
6842 Roo.apply(this, config);
6848 var st = Roo.data.SortTypes;
6849 // named sortTypes are supported, here we look them up
6850 if(typeof this.sortType == "string"){
6851 this.sortType = st[this.sortType];
6854 // set default sortType for strings and dates
6858 this.sortType = st.asUCString;
6861 this.sortType = st.asDate;
6864 this.sortType = st.none;
6869 var stripRe = /[\$,%]/g;
6871 // prebuilt conversion function for this field, instead of
6872 // switching every time we're reading a value
6874 var cv, dateFormat = this.dateFormat;
6879 cv = function(v){ return v; };
6882 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6886 return v !== undefined && v !== null && v !== '' ?
6887 parseInt(String(v).replace(stripRe, ""), 10) : '';
6892 return v !== undefined && v !== null && v !== '' ?
6893 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6898 cv = function(v){ return v === true || v === "true" || v == 1; };
6905 if(v instanceof Date){
6909 if(dateFormat == "timestamp"){
6910 return new Date(v*1000);
6912 return Date.parseDate(v, dateFormat);
6914 var parsed = Date.parse(v);
6915 return parsed ? new Date(parsed) : null;
6924 Roo.data.Field.prototype = {
6932 * Ext JS Library 1.1.1
6933 * Copyright(c) 2006-2007, Ext JS, LLC.
6935 * Originally Released Under LGPL - original licence link has changed is not relivant.
6938 * <script type="text/javascript">
6941 // Base class for reading structured data from a data source. This class is intended to be
6942 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6945 * @class Roo.data.DataReader
6946 * Base class for reading structured data from a data source. This class is intended to be
6947 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6950 Roo.data.DataReader = function(meta, recordType){
6954 this.recordType = recordType instanceof Array ?
6955 Roo.data.Record.create(recordType) : recordType;
6958 Roo.data.DataReader.prototype = {
6960 * Create an empty record
6961 * @param {Object} data (optional) - overlay some values
6962 * @return {Roo.data.Record} record created.
6964 newRow : function(d) {
6966 this.recordType.prototype.fields.each(function(c) {
6968 case 'int' : da[c.name] = 0; break;
6969 case 'date' : da[c.name] = new Date(); break;
6970 case 'float' : da[c.name] = 0.0; break;
6971 case 'boolean' : da[c.name] = false; break;
6972 default : da[c.name] = ""; break;
6976 return new this.recordType(Roo.apply(da, d));
6981 * Ext JS Library 1.1.1
6982 * Copyright(c) 2006-2007, Ext JS, LLC.
6984 * Originally Released Under LGPL - original licence link has changed is not relivant.
6987 * <script type="text/javascript">
6991 * @class Roo.data.DataProxy
6992 * @extends Roo.data.Observable
6993 * This class is an abstract base class for implementations which provide retrieval of
6994 * unformatted data objects.<br>
6996 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6997 * (of the appropriate type which knows how to parse the data object) to provide a block of
6998 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7000 * Custom implementations must implement the load method as described in
7001 * {@link Roo.data.HttpProxy#load}.
7003 Roo.data.DataProxy = function(){
7007 * Fires before a network request is made to retrieve a data object.
7008 * @param {Object} This DataProxy object.
7009 * @param {Object} params The params parameter to the load function.
7014 * Fires before the load method's callback is called.
7015 * @param {Object} This DataProxy object.
7016 * @param {Object} o The data object.
7017 * @param {Object} arg The callback argument object passed to the load function.
7021 * @event loadexception
7022 * Fires if an Exception occurs during data retrieval.
7023 * @param {Object} This DataProxy object.
7024 * @param {Object} o The data object.
7025 * @param {Object} arg The callback argument object passed to the load function.
7026 * @param {Object} e The Exception.
7028 loadexception : true
7030 Roo.data.DataProxy.superclass.constructor.call(this);
7033 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7036 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7040 * Ext JS Library 1.1.1
7041 * Copyright(c) 2006-2007, Ext JS, LLC.
7043 * Originally Released Under LGPL - original licence link has changed is not relivant.
7046 * <script type="text/javascript">
7049 * @class Roo.data.MemoryProxy
7050 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7051 * to the Reader when its load method is called.
7053 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7055 Roo.data.MemoryProxy = function(data){
7059 Roo.data.MemoryProxy.superclass.constructor.call(this);
7063 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7065 * Load data from the requested source (in this case an in-memory
7066 * data object passed to the constructor), read the data object into
7067 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7068 * process that block using the passed callback.
7069 * @param {Object} params This parameter is not used by the MemoryProxy class.
7070 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7071 * object into a block of Roo.data.Records.
7072 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7073 * The function must be passed <ul>
7074 * <li>The Record block object</li>
7075 * <li>The "arg" argument from the load function</li>
7076 * <li>A boolean success indicator</li>
7078 * @param {Object} scope The scope in which to call the callback
7079 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7081 load : function(params, reader, callback, scope, arg){
7082 params = params || {};
7085 result = reader.readRecords(this.data);
7087 this.fireEvent("loadexception", this, arg, null, e);
7088 callback.call(scope, null, arg, false);
7091 callback.call(scope, result, arg, true);
7095 update : function(params, records){
7100 * Ext JS Library 1.1.1
7101 * Copyright(c) 2006-2007, Ext JS, LLC.
7103 * Originally Released Under LGPL - original licence link has changed is not relivant.
7106 * <script type="text/javascript">
7109 * @class Roo.data.HttpProxy
7110 * @extends Roo.data.DataProxy
7111 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7112 * configured to reference a certain URL.<br><br>
7114 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7115 * from which the running page was served.<br><br>
7117 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7119 * Be aware that to enable the browser to parse an XML document, the server must set
7120 * the Content-Type header in the HTTP response to "text/xml".
7122 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7123 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7124 * will be used to make the request.
7126 Roo.data.HttpProxy = function(conn){
7127 Roo.data.HttpProxy.superclass.constructor.call(this);
7128 // is conn a conn config or a real conn?
7130 this.useAjax = !conn || !conn.events;
7134 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7135 // thse are take from connection...
7138 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7141 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7142 * extra parameters to each request made by this object. (defaults to undefined)
7145 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7146 * to each request made by this object. (defaults to undefined)
7149 * @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)
7152 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7155 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7161 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7165 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7166 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7167 * a finer-grained basis than the DataProxy events.
7169 getConnection : function(){
7170 return this.useAjax ? Roo.Ajax : this.conn;
7174 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7175 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7176 * process that block using the passed callback.
7177 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7178 * for the request to the remote server.
7179 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7180 * object into a block of Roo.data.Records.
7181 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7182 * The function must be passed <ul>
7183 * <li>The Record block object</li>
7184 * <li>The "arg" argument from the load function</li>
7185 * <li>A boolean success indicator</li>
7187 * @param {Object} scope The scope in which to call the callback
7188 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7190 load : function(params, reader, callback, scope, arg){
7191 if(this.fireEvent("beforeload", this, params) !== false){
7193 params : params || {},
7195 callback : callback,
7200 callback : this.loadResponse,
7204 Roo.applyIf(o, this.conn);
7205 if(this.activeRequest){
7206 Roo.Ajax.abort(this.activeRequest);
7208 this.activeRequest = Roo.Ajax.request(o);
7210 this.conn.request(o);
7213 callback.call(scope||this, null, arg, false);
7218 loadResponse : function(o, success, response){
7219 delete this.activeRequest;
7221 this.fireEvent("loadexception", this, o, response);
7222 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7227 result = o.reader.read(response);
7229 this.fireEvent("loadexception", this, o, response, e);
7230 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7234 this.fireEvent("load", this, o, o.request.arg);
7235 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7239 update : function(dataSet){
7244 updateResponse : function(dataSet){
7249 * Ext JS Library 1.1.1
7250 * Copyright(c) 2006-2007, Ext JS, LLC.
7252 * Originally Released Under LGPL - original licence link has changed is not relivant.
7255 * <script type="text/javascript">
7259 * @class Roo.data.ScriptTagProxy
7260 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7261 * other than the originating domain of the running page.<br><br>
7263 * <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
7264 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7266 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7267 * source code that is used as the source inside a <script> tag.<br><br>
7269 * In order for the browser to process the returned data, the server must wrap the data object
7270 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7271 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7272 * depending on whether the callback name was passed:
7275 boolean scriptTag = false;
7276 String cb = request.getParameter("callback");
7279 response.setContentType("text/javascript");
7281 response.setContentType("application/x-json");
7283 Writer out = response.getWriter();
7285 out.write(cb + "(");
7287 out.print(dataBlock.toJsonString());
7294 * @param {Object} config A configuration object.
7296 Roo.data.ScriptTagProxy = function(config){
7297 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7298 Roo.apply(this, config);
7299 this.head = document.getElementsByTagName("head")[0];
7302 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7304 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7306 * @cfg {String} url The URL from which to request the data object.
7309 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7313 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7314 * the server the name of the callback function set up by the load call to process the returned data object.
7315 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7316 * javascript output which calls this named function passing the data object as its only parameter.
7318 callbackParam : "callback",
7320 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7321 * name to the request.
7326 * Load data from the configured URL, read the data object into
7327 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7328 * process that block using the passed callback.
7329 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7330 * for the request to the remote server.
7331 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7332 * object into a block of Roo.data.Records.
7333 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7334 * The function must be passed <ul>
7335 * <li>The Record block object</li>
7336 * <li>The "arg" argument from the load function</li>
7337 * <li>A boolean success indicator</li>
7339 * @param {Object} scope The scope in which to call the callback
7340 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7342 load : function(params, reader, callback, scope, arg){
7343 if(this.fireEvent("beforeload", this, params) !== false){
7345 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7348 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7350 url += "&_dc=" + (new Date().getTime());
7352 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7355 cb : "stcCallback"+transId,
7356 scriptId : "stcScript"+transId,
7360 callback : callback,
7366 window[trans.cb] = function(o){
7367 conn.handleResponse(o, trans);
7370 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7372 if(this.autoAbort !== false){
7376 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7378 var script = document.createElement("script");
7379 script.setAttribute("src", url);
7380 script.setAttribute("type", "text/javascript");
7381 script.setAttribute("id", trans.scriptId);
7382 this.head.appendChild(script);
7386 callback.call(scope||this, null, arg, false);
7391 isLoading : function(){
7392 return this.trans ? true : false;
7396 * Abort the current server request.
7399 if(this.isLoading()){
7400 this.destroyTrans(this.trans);
7405 destroyTrans : function(trans, isLoaded){
7406 this.head.removeChild(document.getElementById(trans.scriptId));
7407 clearTimeout(trans.timeoutId);
7409 window[trans.cb] = undefined;
7411 delete window[trans.cb];
7414 // if hasn't been loaded, wait for load to remove it to prevent script error
7415 window[trans.cb] = function(){
7416 window[trans.cb] = undefined;
7418 delete window[trans.cb];
7425 handleResponse : function(o, trans){
7427 this.destroyTrans(trans, true);
7430 result = trans.reader.readRecords(o);
7432 this.fireEvent("loadexception", this, o, trans.arg, e);
7433 trans.callback.call(trans.scope||window, null, trans.arg, false);
7436 this.fireEvent("load", this, o, trans.arg);
7437 trans.callback.call(trans.scope||window, result, trans.arg, true);
7441 handleFailure : function(trans){
7443 this.destroyTrans(trans, false);
7444 this.fireEvent("loadexception", this, null, trans.arg);
7445 trans.callback.call(trans.scope||window, null, trans.arg, false);
7449 * Ext JS Library 1.1.1
7450 * Copyright(c) 2006-2007, Ext JS, LLC.
7452 * Originally Released Under LGPL - original licence link has changed is not relivant.
7455 * <script type="text/javascript">
7459 * @class Roo.data.JsonReader
7460 * @extends Roo.data.DataReader
7461 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7462 * based on mappings in a provided Roo.data.Record constructor.
7464 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7465 * in the reply previously.
7470 var RecordDef = Roo.data.Record.create([
7471 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7472 {name: 'occupation'} // This field will use "occupation" as the mapping.
7474 var myReader = new Roo.data.JsonReader({
7475 totalProperty: "results", // The property which contains the total dataset size (optional)
7476 root: "rows", // The property which contains an Array of row objects
7477 id: "id" // The property within each row object that provides an ID for the record (optional)
7481 * This would consume a JSON file like this:
7483 { 'results': 2, 'rows': [
7484 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7485 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7488 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7489 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7490 * paged from the remote server.
7491 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7492 * @cfg {String} root name of the property which contains the Array of row objects.
7493 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7495 * Create a new JsonReader
7496 * @param {Object} meta Metadata configuration options
7497 * @param {Object} recordType Either an Array of field definition objects,
7498 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7500 Roo.data.JsonReader = function(meta, recordType){
7503 // set some defaults:
7505 totalProperty: 'total',
7506 successProperty : 'success',
7511 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7513 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7516 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7517 * Used by Store query builder to append _requestMeta to params.
7520 metaFromRemote : false,
7522 * This method is only used by a DataProxy which has retrieved data from a remote server.
7523 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7524 * @return {Object} data A data block which is used by an Roo.data.Store object as
7525 * a cache of Roo.data.Records.
7527 read : function(response){
7528 var json = response.responseText;
7530 var o = /* eval:var:o */ eval("("+json+")");
7532 throw {message: "JsonReader.read: Json object not found"};
7538 this.metaFromRemote = true;
7539 this.meta = o.metaData;
7540 this.recordType = Roo.data.Record.create(o.metaData.fields);
7541 this.onMetaChange(this.meta, this.recordType, o);
7543 return this.readRecords(o);
7546 // private function a store will implement
7547 onMetaChange : function(meta, recordType, o){
7554 simpleAccess: function(obj, subsc) {
7561 getJsonAccessor: function(){
7563 return function(expr) {
7565 return(re.test(expr))
7566 ? new Function("obj", "return obj." + expr)
7576 * Create a data block containing Roo.data.Records from an XML document.
7577 * @param {Object} o An object which contains an Array of row objects in the property specified
7578 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7579 * which contains the total size of the dataset.
7580 * @return {Object} data A data block which is used by an Roo.data.Store object as
7581 * a cache of Roo.data.Records.
7583 readRecords : function(o){
7585 * After any data loads, the raw JSON data is available for further custom processing.
7589 var s = this.meta, Record = this.recordType,
7590 f = Record.prototype.fields, fi = f.items, fl = f.length;
7592 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7594 if(s.totalProperty) {
7595 this.getTotal = this.getJsonAccessor(s.totalProperty);
7597 if(s.successProperty) {
7598 this.getSuccess = this.getJsonAccessor(s.successProperty);
7600 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7602 var g = this.getJsonAccessor(s.id);
7603 this.getId = function(rec) {
7605 return (r === undefined || r === "") ? null : r;
7608 this.getId = function(){return null;};
7611 for(var jj = 0; jj < fl; jj++){
7613 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7614 this.ef[jj] = this.getJsonAccessor(map);
7618 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7619 if(s.totalProperty){
7620 var vt = parseInt(this.getTotal(o), 10);
7625 if(s.successProperty){
7626 var vs = this.getSuccess(o);
7627 if(vs === false || vs === 'false'){
7632 for(var i = 0; i < c; i++){
7635 var id = this.getId(n);
7636 for(var j = 0; j < fl; j++){
7638 var v = this.ef[j](n);
7640 Roo.log('missing convert for ' + f.name);
7644 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7646 var record = new Record(values, id);
7648 records[i] = record;
7654 totalRecords : totalRecords
7659 * Ext JS Library 1.1.1
7660 * Copyright(c) 2006-2007, Ext JS, LLC.
7662 * Originally Released Under LGPL - original licence link has changed is not relivant.
7665 * <script type="text/javascript">
7669 * @class Roo.data.ArrayReader
7670 * @extends Roo.data.DataReader
7671 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7672 * Each element of that Array represents a row of data fields. The
7673 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7674 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7678 var RecordDef = Roo.data.Record.create([
7679 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7680 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7682 var myReader = new Roo.data.ArrayReader({
7683 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7687 * This would consume an Array like this:
7689 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7691 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7693 * Create a new JsonReader
7694 * @param {Object} meta Metadata configuration options.
7695 * @param {Object} recordType Either an Array of field definition objects
7696 * as specified to {@link Roo.data.Record#create},
7697 * or an {@link Roo.data.Record} object
7698 * created using {@link Roo.data.Record#create}.
7700 Roo.data.ArrayReader = function(meta, recordType){
7701 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7704 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7706 * Create a data block containing Roo.data.Records from an XML document.
7707 * @param {Object} o An Array of row objects which represents the dataset.
7708 * @return {Object} data A data block which is used by an Roo.data.Store object as
7709 * a cache of Roo.data.Records.
7711 readRecords : function(o){
7712 var sid = this.meta ? this.meta.id : null;
7713 var recordType = this.recordType, fields = recordType.prototype.fields;
7716 for(var i = 0; i < root.length; i++){
7719 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7720 for(var j = 0, jlen = fields.length; j < jlen; j++){
7721 var f = fields.items[j];
7722 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7723 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7727 var record = new recordType(values, id);
7729 records[records.length] = record;
7733 totalRecords : records.length
7742 * @class Roo.bootstrap.ComboBox
7743 * @extends Roo.bootstrap.TriggerField
7744 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7745 * @cfg {Boolean} append (true|false) default false
7747 * Create a new ComboBox.
7748 * @param {Object} config Configuration options
7750 Roo.bootstrap.ComboBox = function(config){
7751 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7755 * Fires when the dropdown list is expanded
7756 * @param {Roo.bootstrap.ComboBox} combo This combo box
7761 * Fires when the dropdown list is collapsed
7762 * @param {Roo.bootstrap.ComboBox} combo This combo box
7766 * @event beforeselect
7767 * Fires before a list item is selected. Return false to cancel the selection.
7768 * @param {Roo.bootstrap.ComboBox} combo This combo box
7769 * @param {Roo.data.Record} record The data record returned from the underlying store
7770 * @param {Number} index The index of the selected item in the dropdown list
7772 'beforeselect' : true,
7775 * Fires when a list item is selected
7776 * @param {Roo.bootstrap.ComboBox} combo This combo box
7777 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7778 * @param {Number} index The index of the selected item in the dropdown list
7782 * @event beforequery
7783 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7784 * The event object passed has these properties:
7785 * @param {Roo.bootstrap.ComboBox} combo This combo box
7786 * @param {String} query The query
7787 * @param {Boolean} forceAll true to force "all" query
7788 * @param {Boolean} cancel true to cancel the query
7789 * @param {Object} e The query event object
7791 'beforequery': true,
7794 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7795 * @param {Roo.bootstrap.ComboBox} combo This combo box
7800 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7801 * @param {Roo.bootstrap.ComboBox} combo This combo box
7802 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7807 * Fires when the remove value from the combobox array
7808 * @param {Roo.bootstrap.ComboBox} combo This combo box
7815 this.selectedIndex = -1;
7816 if(this.mode == 'local'){
7817 if(config.queryDelay === undefined){
7818 this.queryDelay = 10;
7820 if(config.minChars === undefined){
7826 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7829 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7830 * rendering into an Roo.Editor, defaults to false)
7833 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7834 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7837 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7840 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7841 * the dropdown list (defaults to undefined, with no header element)
7845 * @cfg {String/Roo.Template} tpl The template to use to render the output
7849 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7851 listWidth: undefined,
7853 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7854 * mode = 'remote' or 'text' if mode = 'local')
7856 displayField: undefined,
7858 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7859 * mode = 'remote' or 'value' if mode = 'local').
7860 * Note: use of a valueField requires the user make a selection
7861 * in order for a value to be mapped.
7863 valueField: undefined,
7867 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7868 * field's data value (defaults to the underlying DOM element's name)
7870 hiddenName: undefined,
7872 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7876 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7878 selectedClass: 'active',
7881 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7885 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7886 * anchor positions (defaults to 'tl-bl')
7888 listAlign: 'tl-bl?',
7890 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7894 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7895 * query specified by the allQuery config option (defaults to 'query')
7897 triggerAction: 'query',
7899 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7900 * (defaults to 4, does not apply if editable = false)
7904 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7905 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7909 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7910 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7914 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7915 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7919 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7920 * when editable = true (defaults to false)
7922 selectOnFocus:false,
7924 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7926 queryParam: 'query',
7928 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7929 * when mode = 'remote' (defaults to 'Loading...')
7931 loadingText: 'Loading...',
7933 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7937 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7941 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7942 * traditional select (defaults to true)
7946 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7950 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7954 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7955 * listWidth has a higher value)
7959 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7960 * allow the user to set arbitrary text into the field (defaults to false)
7962 forceSelection:false,
7964 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7965 * if typeAhead = true (defaults to 250)
7967 typeAheadDelay : 250,
7969 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7970 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7972 valueNotFoundText : undefined,
7974 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7979 * @cfg {Boolean} disableClear Disable showing of clear button.
7981 disableClear : false,
7983 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
7985 alwaysQuery : false,
7988 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8002 // element that contains real text value.. (when hidden is used..)
8005 initEvents: function(){
8008 throw "can not find store for combo";
8010 this.store = Roo.factory(this.store, Roo.data);
8014 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8017 if(this.hiddenName){
8019 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8021 this.hiddenField.dom.value =
8022 this.hiddenValue !== undefined ? this.hiddenValue :
8023 this.value !== undefined ? this.value : '';
8025 // prevent input submission
8026 this.el.dom.removeAttribute('name');
8027 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8032 // this.el.dom.setAttribute('autocomplete', 'off');
8035 var cls = 'x-combo-list';
8036 this.list = this.el.select('ul.dropdown-menu',true).first();
8038 //this.list = new Roo.Layer({
8039 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8042 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8043 this.list.setWidth(lw);
8045 this.list.on('mouseover', this.onViewOver, this);
8046 this.list.on('mousemove', this.onViewMove, this);
8048 this.list.on('scroll', this.onViewScroll, this);
8051 this.list.swallowEvent('mousewheel');
8052 this.assetHeight = 0;
8055 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8056 this.assetHeight += this.header.getHeight();
8059 this.innerList = this.list.createChild({cls:cls+'-inner'});
8060 this.innerList.on('mouseover', this.onViewOver, this);
8061 this.innerList.on('mousemove', this.onViewMove, this);
8062 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8064 if(this.allowBlank && !this.pageSize && !this.disableClear){
8065 this.footer = this.list.createChild({cls:cls+'-ft'});
8066 this.pageTb = new Roo.Toolbar(this.footer);
8070 this.footer = this.list.createChild({cls:cls+'-ft'});
8071 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8072 {pageSize: this.pageSize});
8076 if (this.pageTb && this.allowBlank && !this.disableClear) {
8078 this.pageTb.add(new Roo.Toolbar.Fill(), {
8079 cls: 'x-btn-icon x-btn-clear',
8085 _this.onSelect(false, -1);
8090 this.assetHeight += this.footer.getHeight();
8095 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8098 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8099 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8101 //this.view.wrapEl.setDisplayed(false);
8102 this.view.on('click', this.onViewClick, this);
8106 this.store.on('beforeload', this.onBeforeLoad, this);
8107 this.store.on('load', this.onLoad, this);
8108 this.store.on('loadexception', this.onLoadException, this);
8111 this.resizer = new Roo.Resizable(this.list, {
8112 pinned:true, handles:'se'
8114 this.resizer.on('resize', function(r, w, h){
8115 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8117 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8118 this.restrictHeight();
8120 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8124 this.editable = true;
8125 this.setEditable(false);
8130 if (typeof(this.events.add.listeners) != 'undefined') {
8132 this.addicon = this.wrap.createChild(
8133 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8135 this.addicon.on('click', function(e) {
8136 this.fireEvent('add', this);
8139 if (typeof(this.events.edit.listeners) != 'undefined') {
8141 this.editicon = this.wrap.createChild(
8142 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8144 this.editicon.setStyle('margin-left', '40px');
8146 this.editicon.on('click', function(e) {
8148 // we fire even if inothing is selected..
8149 this.fireEvent('edit', this, this.lastData );
8155 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8157 this.inKeyMode = true;
8161 "down" : function(e){
8162 if(!this.isExpanded()){
8163 this.onTriggerClick();
8165 this.inKeyMode = true;
8170 "enter" : function(e){
8175 "esc" : function(e){
8179 "tab" : function(e){
8182 if(this.fireEvent("specialkey", this, e)){
8183 this.onViewClick(false);
8191 doRelay : function(foo, bar, hname){
8192 if(hname == 'down' || this.scope.isExpanded()){
8193 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8202 this.queryDelay = Math.max(this.queryDelay || 10,
8203 this.mode == 'local' ? 10 : 250);
8206 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8209 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8211 if(this.editable !== false){
8212 this.inputEl().on("keyup", this.onKeyUp, this);
8214 if(this.forceSelection){
8215 this.on('blur', this.doForce, this);
8219 this.choices = this.el.select('ul.select2-choices', true).first();
8220 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8224 onDestroy : function(){
8226 this.view.setStore(null);
8227 this.view.el.removeAllListeners();
8228 this.view.el.remove();
8229 this.view.purgeListeners();
8232 this.list.dom.innerHTML = '';
8235 this.store.un('beforeload', this.onBeforeLoad, this);
8236 this.store.un('load', this.onLoad, this);
8237 this.store.un('loadexception', this.onLoadException, this);
8239 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8243 fireKey : function(e){
8244 if(e.isNavKeyPress() && !this.list.isVisible()){
8245 this.fireEvent("specialkey", this, e);
8250 onResize: function(w, h){
8251 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8253 // if(typeof w != 'number'){
8254 // // we do not handle it!?!?
8257 // var tw = this.trigger.getWidth();
8258 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8259 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8261 // this.inputEl().setWidth( this.adjustWidth('input', x));
8263 // //this.trigger.setStyle('left', x+'px');
8265 // if(this.list && this.listWidth === undefined){
8266 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8267 // this.list.setWidth(lw);
8268 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8276 * Allow or prevent the user from directly editing the field text. If false is passed,
8277 * the user will only be able to select from the items defined in the dropdown list. This method
8278 * is the runtime equivalent of setting the 'editable' config option at config time.
8279 * @param {Boolean} value True to allow the user to directly edit the field text
8281 setEditable : function(value){
8282 if(value == this.editable){
8285 this.editable = value;
8287 this.inputEl().dom.setAttribute('readOnly', true);
8288 this.inputEl().on('mousedown', this.onTriggerClick, this);
8289 this.inputEl().addClass('x-combo-noedit');
8291 this.inputEl().dom.setAttribute('readOnly', false);
8292 this.inputEl().un('mousedown', this.onTriggerClick, this);
8293 this.inputEl().removeClass('x-combo-noedit');
8299 onBeforeLoad : function(combo,opts){
8304 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8306 this.restrictHeight();
8307 this.selectedIndex = -1;
8311 onLoad : function(){
8313 this.hasQuery = false;
8319 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8320 this.loading.hide();
8323 if(this.store.getCount() > 0){
8325 this.restrictHeight();
8326 if(this.lastQuery == this.allQuery){
8328 this.inputEl().dom.select();
8330 if(!this.selectByValue(this.value, true)){
8331 this.select(0, true);
8335 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8336 this.taTask.delay(this.typeAheadDelay);
8340 this.onEmptyResults();
8346 onLoadException : function()
8348 this.hasQuery = false;
8350 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8351 this.loading.hide();
8355 Roo.log(this.store.reader.jsonData);
8356 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8358 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8364 onTypeAhead : function(){
8365 if(this.store.getCount() > 0){
8366 var r = this.store.getAt(0);
8367 var newValue = r.data[this.displayField];
8368 var len = newValue.length;
8369 var selStart = this.getRawValue().length;
8371 if(selStart != len){
8372 this.setRawValue(newValue);
8373 this.selectText(selStart, newValue.length);
8379 onSelect : function(record, index){
8381 if(this.fireEvent('beforeselect', this, record, index) !== false){
8383 this.setFromData(index > -1 ? record.data : false);
8386 this.fireEvent('select', this, record, index);
8391 * Returns the currently selected field value or empty string if no value is set.
8392 * @return {String} value The selected value
8394 getValue : function(){
8397 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8400 if(this.valueField){
8401 return typeof this.value != 'undefined' ? this.value : '';
8403 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8408 * Clears any text/value currently set in the field
8410 clearValue : function(){
8411 if(this.hiddenField){
8412 this.hiddenField.dom.value = '';
8415 this.setRawValue('');
8416 this.lastSelectionText = '';
8421 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8422 * will be displayed in the field. If the value does not match the data value of an existing item,
8423 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8424 * Otherwise the field will be blank (although the value will still be set).
8425 * @param {String} value The value to match
8427 setValue : function(v){
8434 if(this.valueField){
8435 var r = this.findRecord(this.valueField, v);
8437 text = r.data[this.displayField];
8438 }else if(this.valueNotFoundText !== undefined){
8439 text = this.valueNotFoundText;
8442 this.lastSelectionText = text;
8443 if(this.hiddenField){
8444 this.hiddenField.dom.value = v;
8446 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8450 * @property {Object} the last set data for the element
8455 * Sets the value of the field based on a object which is related to the record format for the store.
8456 * @param {Object} value the value to set as. or false on reset?
8458 setFromData : function(o){
8465 var dv = ''; // display value
8466 var vv = ''; // value value..
8468 if (this.displayField) {
8469 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8471 // this is an error condition!!!
8472 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8475 if(this.valueField){
8476 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8479 if(this.hiddenField){
8480 this.hiddenField.dom.value = vv;
8482 this.lastSelectionText = dv;
8483 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8487 // no hidden field.. - we store the value in 'value', but still display
8488 // display field!!!!
8489 this.lastSelectionText = dv;
8490 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8497 // overridden so that last data is reset..
8498 this.setValue(this.originalValue);
8499 this.clearInvalid();
8500 this.lastData = false;
8502 this.view.clearSelections();
8506 findRecord : function(prop, value){
8508 if(this.store.getCount() > 0){
8509 this.store.each(function(r){
8510 if(r.data[prop] == value){
8522 // returns hidden if it's set..
8523 if (!this.rendered) {return ''};
8524 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8528 onViewMove : function(e, t){
8529 this.inKeyMode = false;
8533 onViewOver : function(e, t){
8534 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8537 var item = this.view.findItemFromChild(t);
8539 var index = this.view.indexOf(item);
8540 this.select(index, false);
8545 onViewClick : function(doFocus)
8547 var index = this.view.getSelectedIndexes()[0];
8548 var r = this.store.getAt(index);
8550 this.onSelect(r, index);
8552 if(doFocus !== false && !this.blockFocus){
8553 this.inputEl().focus();
8558 restrictHeight : function(){
8559 //this.innerList.dom.style.height = '';
8560 //var inner = this.innerList.dom;
8561 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8562 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8563 //this.list.beginUpdate();
8564 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8565 this.list.alignTo(this.inputEl(), this.listAlign);
8566 //this.list.endUpdate();
8570 onEmptyResults : function(){
8575 * Returns true if the dropdown list is expanded, else false.
8577 isExpanded : function(){
8578 return this.list.isVisible();
8582 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8583 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8584 * @param {String} value The data value of the item to select
8585 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8586 * selected item if it is not currently in view (defaults to true)
8587 * @return {Boolean} True if the value matched an item in the list, else false
8589 selectByValue : function(v, scrollIntoView){
8590 if(v !== undefined && v !== null){
8591 var r = this.findRecord(this.valueField || this.displayField, v);
8593 this.select(this.store.indexOf(r), scrollIntoView);
8601 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8602 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8603 * @param {Number} index The zero-based index of the list item to select
8604 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8605 * selected item if it is not currently in view (defaults to true)
8607 select : function(index, scrollIntoView){
8608 this.selectedIndex = index;
8609 this.view.select(index);
8610 if(scrollIntoView !== false){
8611 var el = this.view.getNode(index);
8613 //this.innerList.scrollChildIntoView(el, false);
8620 selectNext : function(){
8621 var ct = this.store.getCount();
8623 if(this.selectedIndex == -1){
8625 }else if(this.selectedIndex < ct-1){
8626 this.select(this.selectedIndex+1);
8632 selectPrev : function(){
8633 var ct = this.store.getCount();
8635 if(this.selectedIndex == -1){
8637 }else if(this.selectedIndex != 0){
8638 this.select(this.selectedIndex-1);
8644 onKeyUp : function(e){
8645 if(this.editable !== false && !e.isSpecialKey()){
8646 this.lastKey = e.getKey();
8647 this.dqTask.delay(this.queryDelay);
8652 validateBlur : function(){
8653 return !this.list || !this.list.isVisible();
8657 initQuery : function(){
8658 this.doQuery(this.getRawValue());
8662 doForce : function(){
8663 if(this.el.dom.value.length > 0){
8665 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8671 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8672 * query allowing the query action to be canceled if needed.
8673 * @param {String} query The SQL query to execute
8674 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8675 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8676 * saved in the current store (defaults to false)
8678 doQuery : function(q, forceAll){
8680 if(q === undefined || q === null){
8689 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8694 forceAll = qe.forceAll;
8695 if(forceAll === true || (q.length >= this.minChars)){
8697 this.hasQuery = true;
8699 if(this.lastQuery != q || this.alwaysQuery){
8701 if(this.mode == 'local'){
8702 this.selectedIndex = -1;
8704 this.store.clearFilter();
8706 this.store.filter(this.displayField, q);
8710 this.store.baseParams[this.queryParam] = q;
8712 var options = {params : this.getParams(q)};
8716 options.params.start = this.page * this.pageSize;
8719 this.store.load(options);
8723 this.selectedIndex = -1;
8728 this.loadNext = false;
8732 getParams : function(q){
8734 //p[this.queryParam] = q;
8738 p.limit = this.pageSize;
8744 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8746 collapse : function(){
8747 if(!this.isExpanded()){
8752 Roo.get(document).un('mousedown', this.collapseIf, this);
8753 Roo.get(document).un('mousewheel', this.collapseIf, this);
8754 if (!this.editable) {
8755 Roo.get(document).un('keydown', this.listKeyPress, this);
8757 this.fireEvent('collapse', this);
8761 collapseIf : function(e){
8762 var in_combo = e.within(this.el);
8763 var in_list = e.within(this.list);
8765 if (in_combo || in_list) {
8766 //e.stopPropagation();
8775 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8777 expand : function(){
8779 if(this.isExpanded() || !this.hasFocus){
8783 this.list.alignTo(this.inputEl(), this.listAlign);
8785 Roo.get(document).on('mousedown', this.collapseIf, this);
8786 Roo.get(document).on('mousewheel', this.collapseIf, this);
8787 if (!this.editable) {
8788 Roo.get(document).on('keydown', this.listKeyPress, this);
8791 this.fireEvent('expand', this);
8795 // Implements the default empty TriggerField.onTriggerClick function
8796 onTriggerClick : function()
8798 Roo.log('trigger click');
8805 this.loadNext = false;
8807 if(this.isExpanded()){
8809 if (!this.blockFocus) {
8810 this.inputEl().focus();
8814 this.hasFocus = true;
8815 if(this.triggerAction == 'all') {
8816 this.doQuery(this.allQuery, true);
8818 this.doQuery(this.getRawValue());
8820 if (!this.blockFocus) {
8821 this.inputEl().focus();
8825 listKeyPress : function(e)
8827 //Roo.log('listkeypress');
8828 // scroll to first matching element based on key pres..
8829 if (e.isSpecialKey()) {
8832 var k = String.fromCharCode(e.getKey()).toUpperCase();
8835 var csel = this.view.getSelectedNodes();
8836 var cselitem = false;
8838 var ix = this.view.indexOf(csel[0]);
8839 cselitem = this.store.getAt(ix);
8840 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8846 this.store.each(function(v) {
8848 // start at existing selection.
8849 if (cselitem.id == v.id) {
8855 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8856 match = this.store.indexOf(v);
8862 if (match === false) {
8863 return true; // no more action?
8866 this.view.select(match);
8867 var sn = Roo.get(this.view.getSelectedNodes()[0])
8868 //sn.scrollIntoView(sn.dom.parentNode, false);
8871 onViewScroll : function(e, t){
8873 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8877 this.hasQuery = true;
8879 this.loading = this.list.select('.loading', true).first();
8881 if(this.loading === null){
8882 this.list.createChild({
8884 cls: 'loading select2-more-results select2-active',
8885 html: 'Loading more results...'
8888 this.loading = this.list.select('.loading', true).first();
8890 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8892 this.loading.hide();
8895 this.loading.show();
8900 this.loadNext = true;
8902 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8907 addItem : function(o)
8909 var dv = ''; // display value
8911 if (this.displayField) {
8912 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8914 // this is an error condition!!!
8915 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8922 var choice = this.choices.createChild({
8924 cls: 'select2-search-choice',
8933 cls: 'select2-search-choice-close',
8938 }, this.searchField);
8940 var close = choice.select('a.select2-search-choice-close', true).first()
8942 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8949 this.inputEl().dom.value = '';
8953 onRemoveItem : function(e, _self, o)
8955 Roo.log('remove item');
8956 var index = this.item.indexOf(o.data) * 1;
8959 Roo.log('not this item?!');
8963 this.item.splice(index, 1);
8968 this.fireEvent('remove', this);
8972 syncValue : function()
8974 if(!this.item.length){
8981 Roo.each(this.item, function(i){
8982 if(_this.valueField){
8983 value.push(i[_this.valueField]);
8990 this.value = value.join(',');
8992 if(this.hiddenField){
8993 this.hiddenField.dom.value = this.value;
8997 clearItem : function()
9005 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9015 * @cfg {Boolean} grow
9019 * @cfg {Number} growMin
9023 * @cfg {Number} growMax
9033 * Ext JS Library 1.1.1
9034 * Copyright(c) 2006-2007, Ext JS, LLC.
9036 * Originally Released Under LGPL - original licence link has changed is not relivant.
9039 * <script type="text/javascript">
9044 * @extends Roo.util.Observable
9045 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9046 * This class also supports single and multi selection modes. <br>
9047 * Create a data model bound view:
9049 var store = new Roo.data.Store(...);
9051 var view = new Roo.View({
9053 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9056 selectedClass: "ydataview-selected",
9060 // listen for node click?
9061 view.on("click", function(vw, index, node, e){
9062 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9066 dataModel.load("foobar.xml");
9068 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9070 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9071 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9073 * Note: old style constructor is still suported (container, template, config)
9077 * @param {Object} config The config object
9080 Roo.View = function(config, depreciated_tpl, depreciated_config){
9082 if (typeof(depreciated_tpl) == 'undefined') {
9083 // new way.. - universal constructor.
9084 Roo.apply(this, config);
9085 this.el = Roo.get(this.el);
9088 this.el = Roo.get(config);
9089 this.tpl = depreciated_tpl;
9090 Roo.apply(this, depreciated_config);
9092 this.wrapEl = this.el.wrap().wrap();
9093 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9096 if(typeof(this.tpl) == "string"){
9097 this.tpl = new Roo.Template(this.tpl);
9099 // support xtype ctors..
9100 this.tpl = new Roo.factory(this.tpl, Roo);
9112 * @event beforeclick
9113 * Fires before a click is processed. Returns false to cancel the default action.
9114 * @param {Roo.View} this
9115 * @param {Number} index The index of the target node
9116 * @param {HTMLElement} node The target node
9117 * @param {Roo.EventObject} e The raw event object
9119 "beforeclick" : true,
9122 * Fires when a template node is clicked.
9123 * @param {Roo.View} this
9124 * @param {Number} index The index of the target node
9125 * @param {HTMLElement} node The target node
9126 * @param {Roo.EventObject} e The raw event object
9131 * Fires when a template node is double clicked.
9132 * @param {Roo.View} this
9133 * @param {Number} index The index of the target node
9134 * @param {HTMLElement} node The target node
9135 * @param {Roo.EventObject} e The raw event object
9139 * @event contextmenu
9140 * Fires when a template node is right clicked.
9141 * @param {Roo.View} this
9142 * @param {Number} index The index of the target node
9143 * @param {HTMLElement} node The target node
9144 * @param {Roo.EventObject} e The raw event object
9146 "contextmenu" : true,
9148 * @event selectionchange
9149 * Fires when the selected nodes change.
9150 * @param {Roo.View} this
9151 * @param {Array} selections Array of the selected nodes
9153 "selectionchange" : true,
9156 * @event beforeselect
9157 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9158 * @param {Roo.View} this
9159 * @param {HTMLElement} node The node to be selected
9160 * @param {Array} selections Array of currently selected nodes
9162 "beforeselect" : true,
9164 * @event preparedata
9165 * Fires on every row to render, to allow you to change the data.
9166 * @param {Roo.View} this
9167 * @param {Object} data to be rendered (change this)
9169 "preparedata" : true
9177 "click": this.onClick,
9178 "dblclick": this.onDblClick,
9179 "contextmenu": this.onContextMenu,
9183 this.selections = [];
9185 this.cmp = new Roo.CompositeElementLite([]);
9187 this.store = Roo.factory(this.store, Roo.data);
9188 this.setStore(this.store, true);
9191 if ( this.footer && this.footer.xtype) {
9193 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9195 this.footer.dataSource = this.store
9196 this.footer.container = fctr;
9197 this.footer = Roo.factory(this.footer, Roo);
9198 fctr.insertFirst(this.el);
9200 // this is a bit insane - as the paging toolbar seems to detach the el..
9201 // dom.parentNode.parentNode.parentNode
9202 // they get detached?
9206 Roo.View.superclass.constructor.call(this);
9211 Roo.extend(Roo.View, Roo.util.Observable, {
9214 * @cfg {Roo.data.Store} store Data store to load data from.
9219 * @cfg {String|Roo.Element} el The container element.
9224 * @cfg {String|Roo.Template} tpl The template used by this View
9228 * @cfg {String} dataName the named area of the template to use as the data area
9229 * Works with domtemplates roo-name="name"
9233 * @cfg {String} selectedClass The css class to add to selected nodes
9235 selectedClass : "x-view-selected",
9237 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9242 * @cfg {String} text to display on mask (default Loading)
9246 * @cfg {Boolean} multiSelect Allow multiple selection
9248 multiSelect : false,
9250 * @cfg {Boolean} singleSelect Allow single selection
9252 singleSelect: false,
9255 * @cfg {Boolean} toggleSelect - selecting
9257 toggleSelect : false,
9260 * Returns the element this view is bound to.
9261 * @return {Roo.Element}
9270 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9272 refresh : function(){
9276 // if we are using something like 'domtemplate', then
9277 // the what gets used is:
9278 // t.applySubtemplate(NAME, data, wrapping data..)
9279 // the outer template then get' applied with
9280 // the store 'extra data'
9281 // and the body get's added to the
9282 // roo-name="data" node?
9283 // <span class='roo-tpl-{name}'></span> ?????
9287 this.clearSelections();
9290 var records = this.store.getRange();
9291 if(records.length < 1) {
9293 // is this valid?? = should it render a template??
9295 this.el.update(this.emptyText);
9299 if (this.dataName) {
9300 this.el.update(t.apply(this.store.meta)); //????
9301 el = this.el.child('.roo-tpl-' + this.dataName);
9304 for(var i = 0, len = records.length; i < len; i++){
9305 var data = this.prepareData(records[i].data, i, records[i]);
9306 this.fireEvent("preparedata", this, data, i, records[i]);
9307 html[html.length] = Roo.util.Format.trim(
9309 t.applySubtemplate(this.dataName, data, this.store.meta) :
9316 el.update(html.join(""));
9317 this.nodes = el.dom.childNodes;
9318 this.updateIndexes(0);
9323 * Function to override to reformat the data that is sent to
9324 * the template for each node.
9325 * DEPRICATED - use the preparedata event handler.
9326 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9327 * a JSON object for an UpdateManager bound view).
9329 prepareData : function(data, index, record)
9331 this.fireEvent("preparedata", this, data, index, record);
9335 onUpdate : function(ds, record){
9336 Roo.log('on update');
9337 this.clearSelections();
9338 var index = this.store.indexOf(record);
9339 var n = this.nodes[index];
9340 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9341 n.parentNode.removeChild(n);
9342 this.updateIndexes(index, index);
9348 onAdd : function(ds, records, index)
9350 Roo.log(['on Add', ds, records, index] );
9351 this.clearSelections();
9352 if(this.nodes.length == 0){
9356 var n = this.nodes[index];
9357 for(var i = 0, len = records.length; i < len; i++){
9358 var d = this.prepareData(records[i].data, i, records[i]);
9360 this.tpl.insertBefore(n, d);
9363 this.tpl.append(this.el, d);
9366 this.updateIndexes(index);
9369 onRemove : function(ds, record, index){
9370 Roo.log('onRemove');
9371 this.clearSelections();
9372 var el = this.dataName ?
9373 this.el.child('.roo-tpl-' + this.dataName) :
9376 el.dom.removeChild(this.nodes[index]);
9377 this.updateIndexes(index);
9381 * Refresh an individual node.
9382 * @param {Number} index
9384 refreshNode : function(index){
9385 this.onUpdate(this.store, this.store.getAt(index));
9388 updateIndexes : function(startIndex, endIndex){
9389 var ns = this.nodes;
9390 startIndex = startIndex || 0;
9391 endIndex = endIndex || ns.length - 1;
9392 for(var i = startIndex; i <= endIndex; i++){
9393 ns[i].nodeIndex = i;
9398 * Changes the data store this view uses and refresh the view.
9399 * @param {Store} store
9401 setStore : function(store, initial){
9402 if(!initial && this.store){
9403 this.store.un("datachanged", this.refresh);
9404 this.store.un("add", this.onAdd);
9405 this.store.un("remove", this.onRemove);
9406 this.store.un("update", this.onUpdate);
9407 this.store.un("clear", this.refresh);
9408 this.store.un("beforeload", this.onBeforeLoad);
9409 this.store.un("load", this.onLoad);
9410 this.store.un("loadexception", this.onLoad);
9414 store.on("datachanged", this.refresh, this);
9415 store.on("add", this.onAdd, this);
9416 store.on("remove", this.onRemove, this);
9417 store.on("update", this.onUpdate, this);
9418 store.on("clear", this.refresh, this);
9419 store.on("beforeload", this.onBeforeLoad, this);
9420 store.on("load", this.onLoad, this);
9421 store.on("loadexception", this.onLoad, this);
9429 * onbeforeLoad - masks the loading area.
9432 onBeforeLoad : function(store,opts)
9434 Roo.log('onBeforeLoad');
9438 this.el.mask(this.mask ? this.mask : "Loading" );
9440 onLoad : function ()
9447 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9448 * @param {HTMLElement} node
9449 * @return {HTMLElement} The template node
9451 findItemFromChild : function(node){
9452 var el = this.dataName ?
9453 this.el.child('.roo-tpl-' + this.dataName,true) :
9456 if(!node || node.parentNode == el){
9459 var p = node.parentNode;
9460 while(p && p != el){
9461 if(p.parentNode == el){
9470 onClick : function(e){
9471 var item = this.findItemFromChild(e.getTarget());
9473 var index = this.indexOf(item);
9474 if(this.onItemClick(item, index, e) !== false){
9475 this.fireEvent("click", this, index, item, e);
9478 this.clearSelections();
9483 onContextMenu : function(e){
9484 var item = this.findItemFromChild(e.getTarget());
9486 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9491 onDblClick : function(e){
9492 var item = this.findItemFromChild(e.getTarget());
9494 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9498 onItemClick : function(item, index, e)
9500 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9503 if (this.toggleSelect) {
9504 var m = this.isSelected(item) ? 'unselect' : 'select';
9507 _t[m](item, true, false);
9510 if(this.multiSelect || this.singleSelect){
9511 if(this.multiSelect && e.shiftKey && this.lastSelection){
9512 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9514 this.select(item, this.multiSelect && e.ctrlKey);
9515 this.lastSelection = item;
9523 * Get the number of selected nodes.
9526 getSelectionCount : function(){
9527 return this.selections.length;
9531 * Get the currently selected nodes.
9532 * @return {Array} An array of HTMLElements
9534 getSelectedNodes : function(){
9535 return this.selections;
9539 * Get the indexes of the selected nodes.
9542 getSelectedIndexes : function(){
9543 var indexes = [], s = this.selections;
9544 for(var i = 0, len = s.length; i < len; i++){
9545 indexes.push(s[i].nodeIndex);
9551 * Clear all selections
9552 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9554 clearSelections : function(suppressEvent){
9555 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9556 this.cmp.elements = this.selections;
9557 this.cmp.removeClass(this.selectedClass);
9558 this.selections = [];
9560 this.fireEvent("selectionchange", this, this.selections);
9566 * Returns true if the passed node is selected
9567 * @param {HTMLElement/Number} node The node or node index
9570 isSelected : function(node){
9571 var s = this.selections;
9575 node = this.getNode(node);
9576 return s.indexOf(node) !== -1;
9581 * @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
9582 * @param {Boolean} keepExisting (optional) true to keep existing selections
9583 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9585 select : function(nodeInfo, keepExisting, suppressEvent){
9586 if(nodeInfo instanceof Array){
9588 this.clearSelections(true);
9590 for(var i = 0, len = nodeInfo.length; i < len; i++){
9591 this.select(nodeInfo[i], true, true);
9595 var node = this.getNode(nodeInfo);
9596 if(!node || this.isSelected(node)){
9597 return; // already selected.
9600 this.clearSelections(true);
9602 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9603 Roo.fly(node).addClass(this.selectedClass);
9604 this.selections.push(node);
9606 this.fireEvent("selectionchange", this, this.selections);
9614 * @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
9615 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9616 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9618 unselect : function(nodeInfo, keepExisting, suppressEvent)
9620 if(nodeInfo instanceof Array){
9621 Roo.each(this.selections, function(s) {
9622 this.unselect(s, nodeInfo);
9626 var node = this.getNode(nodeInfo);
9627 if(!node || !this.isSelected(node)){
9628 Roo.log("not selected");
9629 return; // not selected.
9633 Roo.each(this.selections, function(s) {
9635 Roo.fly(node).removeClass(this.selectedClass);
9642 this.selections= ns;
9643 this.fireEvent("selectionchange", this, this.selections);
9647 * Gets a template node.
9648 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9649 * @return {HTMLElement} The node or null if it wasn't found
9651 getNode : function(nodeInfo){
9652 if(typeof nodeInfo == "string"){
9653 return document.getElementById(nodeInfo);
9654 }else if(typeof nodeInfo == "number"){
9655 return this.nodes[nodeInfo];
9661 * Gets a range template nodes.
9662 * @param {Number} startIndex
9663 * @param {Number} endIndex
9664 * @return {Array} An array of nodes
9666 getNodes : function(start, end){
9667 var ns = this.nodes;
9669 end = typeof end == "undefined" ? ns.length - 1 : end;
9672 for(var i = start; i <= end; i++){
9676 for(var i = start; i >= end; i--){
9684 * Finds the index of the passed node
9685 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9686 * @return {Number} The index of the node or -1
9688 indexOf : function(node){
9689 node = this.getNode(node);
9690 if(typeof node.nodeIndex == "number"){
9691 return node.nodeIndex;
9693 var ns = this.nodes;
9694 for(var i = 0, len = ns.length; i < len; i++){
9705 * based on jquery fullcalendar
9709 Roo.bootstrap = Roo.bootstrap || {};
9711 * @class Roo.bootstrap.Calendar
9712 * @extends Roo.bootstrap.Component
9713 * Bootstrap Calendar class
9714 * @cfg {Boolean} loadMask (true|false) default false
9717 * Create a new Container
9718 * @param {Object} config The config object
9723 Roo.bootstrap.Calendar = function(config){
9724 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9728 * Fires when a date is selected
9729 * @param {DatePicker} this
9730 * @param {Date} date The selected date
9734 * @event monthchange
9735 * Fires when the displayed month changes
9736 * @param {DatePicker} this
9737 * @param {Date} date The selected month
9739 'monthchange': true,
9742 * Fires when mouse over an event
9743 * @param {Calendar} this
9744 * @param {event} Event
9749 * Fires when the mouse leaves an
9750 * @param {Calendar} this
9756 * Fires when the mouse click an
9757 * @param {Calendar} this
9766 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9769 * @cfg {Number} startDay
9770 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9776 getAutoCreate : function(){
9779 var fc_button = function(name, corner, style, content ) {
9780 return Roo.apply({},{
9782 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9784 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9787 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9795 style : 'width:100%',
9802 cls : 'fc-header-left',
9804 fc_button('prev', 'left', 'arrow', '‹' ),
9805 fc_button('next', 'right', 'arrow', '›' ),
9806 { tag: 'span', cls: 'fc-header-space' },
9807 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9815 cls : 'fc-header-center',
9819 cls: 'fc-header-title',
9822 html : 'month / year'
9830 cls : 'fc-header-right',
9832 /* fc_button('month', 'left', '', 'month' ),
9833 fc_button('week', '', '', 'week' ),
9834 fc_button('day', 'right', '', 'day' )
9846 var cal_heads = function() {
9848 // fixme - handle this.
9850 for (var i =0; i < Date.dayNames.length; i++) {
9851 var d = Date.dayNames[i];
9854 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9855 html : d.substring(0,3)
9859 ret[0].cls += ' fc-first';
9860 ret[6].cls += ' fc-last';
9863 var cal_cell = function(n) {
9866 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9871 cls: 'fc-day-number',
9875 cls: 'fc-day-content',
9879 style: 'position: relative;' // height: 17px;
9891 var cal_rows = function() {
9894 for (var r = 0; r < 6; r++) {
9901 for (var i =0; i < Date.dayNames.length; i++) {
9902 var d = Date.dayNames[i];
9903 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9906 row.cn[0].cls+=' fc-first';
9907 row.cn[0].cn[0].style = 'min-height:90px';
9908 row.cn[6].cls+=' fc-last';
9912 ret[0].cls += ' fc-first';
9913 ret[4].cls += ' fc-prev-last';
9914 ret[5].cls += ' fc-last';
9921 cls: 'fc-border-separate',
9922 style : 'width:100%',
9930 cls : 'fc-first fc-last',
9949 style : "position: relative;",
9952 cls : 'fc-view fc-view-month fc-grid',
9953 style : 'position: relative',
9954 unselectable : 'on',
9957 cls : 'fc-event-container',
9958 style : 'position:absolute;z-index:8;top:0;left:0;'
9976 initEvents : function()
9979 throw "can not find store for calendar";
9985 style: "text-align:center",
9989 style: "background-color:white;width:50%;margin:250 auto",
9993 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10004 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10006 var size = this.el.select('.fc-content', true).first().getSize();
10007 this.maskEl.setSize(size.width, size.height);
10008 this.maskEl.enableDisplayMode("block");
10009 if(!this.loadMask){
10010 this.maskEl.hide();
10013 this.store = Roo.factory(this.store, Roo.data);
10014 this.store.on('load', this.onLoad, this);
10015 this.store.on('beforeload', this.onBeforeLoad, this);
10019 this.cells = this.el.select('.fc-day',true);
10020 //Roo.log(this.cells);
10021 this.textNodes = this.el.query('.fc-day-number');
10022 this.cells.addClassOnOver('fc-state-hover');
10024 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10025 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10026 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10027 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10029 this.on('monthchange', this.onMonthChange, this);
10031 this.update(new Date().clearTime());
10034 resize : function() {
10035 var sz = this.el.getSize();
10037 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10038 this.el.select('.fc-day-content div',true).setHeight(34);
10043 showPrevMonth : function(e){
10044 this.update(this.activeDate.add("mo", -1));
10046 showToday : function(e){
10047 this.update(new Date().clearTime());
10050 showNextMonth : function(e){
10051 this.update(this.activeDate.add("mo", 1));
10055 showPrevYear : function(){
10056 this.update(this.activeDate.add("y", -1));
10060 showNextYear : function(){
10061 this.update(this.activeDate.add("y", 1));
10066 update : function(date)
10068 var vd = this.activeDate;
10069 this.activeDate = date;
10070 // if(vd && this.el){
10071 // var t = date.getTime();
10072 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10073 // Roo.log('using add remove');
10075 // this.fireEvent('monthchange', this, date);
10077 // this.cells.removeClass("fc-state-highlight");
10078 // this.cells.each(function(c){
10079 // if(c.dateValue == t){
10080 // c.addClass("fc-state-highlight");
10081 // setTimeout(function(){
10082 // try{c.dom.firstChild.focus();}catch(e){}
10092 var days = date.getDaysInMonth();
10094 var firstOfMonth = date.getFirstDateOfMonth();
10095 var startingPos = firstOfMonth.getDay()-this.startDay;
10097 if(startingPos < this.startDay){
10101 var pm = date.add(Date.MONTH, -1);
10102 var prevStart = pm.getDaysInMonth()-startingPos;
10104 this.cells = this.el.select('.fc-day',true);
10105 this.textNodes = this.el.query('.fc-day-number');
10106 this.cells.addClassOnOver('fc-state-hover');
10108 var cells = this.cells.elements;
10109 var textEls = this.textNodes;
10111 Roo.each(cells, function(cell){
10112 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10115 days += startingPos;
10117 // convert everything to numbers so it's fast
10118 var day = 86400000;
10119 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10122 //Roo.log(prevStart);
10124 var today = new Date().clearTime().getTime();
10125 var sel = date.clearTime().getTime();
10126 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10127 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10128 var ddMatch = this.disabledDatesRE;
10129 var ddText = this.disabledDatesText;
10130 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10131 var ddaysText = this.disabledDaysText;
10132 var format = this.format;
10134 var setCellClass = function(cal, cell){
10136 //Roo.log('set Cell Class');
10138 var t = d.getTime();
10142 cell.dateValue = t;
10144 cell.className += " fc-today";
10145 cell.className += " fc-state-highlight";
10146 cell.title = cal.todayText;
10149 // disable highlight in other month..
10150 //cell.className += " fc-state-highlight";
10155 cell.className = " fc-state-disabled";
10156 cell.title = cal.minText;
10160 cell.className = " fc-state-disabled";
10161 cell.title = cal.maxText;
10165 if(ddays.indexOf(d.getDay()) != -1){
10166 cell.title = ddaysText;
10167 cell.className = " fc-state-disabled";
10170 if(ddMatch && format){
10171 var fvalue = d.dateFormat(format);
10172 if(ddMatch.test(fvalue)){
10173 cell.title = ddText.replace("%0", fvalue);
10174 cell.className = " fc-state-disabled";
10178 if (!cell.initialClassName) {
10179 cell.initialClassName = cell.dom.className;
10182 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10187 for(; i < startingPos; i++) {
10188 textEls[i].innerHTML = (++prevStart);
10189 d.setDate(d.getDate()+1);
10191 cells[i].className = "fc-past fc-other-month";
10192 setCellClass(this, cells[i]);
10197 for(; i < days; i++){
10198 intDay = i - startingPos + 1;
10199 textEls[i].innerHTML = (intDay);
10200 d.setDate(d.getDate()+1);
10202 cells[i].className = ''; // "x-date-active";
10203 setCellClass(this, cells[i]);
10207 for(; i < 42; i++) {
10208 textEls[i].innerHTML = (++extraDays);
10209 d.setDate(d.getDate()+1);
10211 cells[i].className = "fc-future fc-other-month";
10212 setCellClass(this, cells[i]);
10215 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10217 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10219 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10220 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10222 if(totalRows != 6){
10223 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10224 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10227 this.fireEvent('monthchange', this, date);
10231 if(!this.internalRender){
10232 var main = this.el.dom.firstChild;
10233 var w = main.offsetWidth;
10234 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10235 Roo.fly(main).setWidth(w);
10236 this.internalRender = true;
10237 // opera does not respect the auto grow header center column
10238 // then, after it gets a width opera refuses to recalculate
10239 // without a second pass
10240 if(Roo.isOpera && !this.secondPass){
10241 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10242 this.secondPass = true;
10243 this.update.defer(10, this, [date]);
10250 findCell : function(dt) {
10251 dt = dt.clearTime().getTime();
10253 this.cells.each(function(c){
10254 //Roo.log("check " +c.dateValue + '?=' + dt);
10255 if(c.dateValue == dt){
10265 findCells : function(ev) {
10266 var s = ev.start.clone().clearTime().getTime();
10268 var e= ev.end.clone().clearTime().getTime();
10271 this.cells.each(function(c){
10272 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10274 if(c.dateValue > e){
10277 if(c.dateValue < s){
10286 findBestRow: function(cells)
10290 for (var i =0 ; i < cells.length;i++) {
10291 ret = Math.max(cells[i].rows || 0,ret);
10298 addItem : function(ev)
10300 // look for vertical location slot in
10301 var cells = this.findCells(ev);
10303 ev.row = this.findBestRow(cells);
10305 // work out the location.
10309 for(var i =0; i < cells.length; i++) {
10317 if (crow.start.getY() == cells[i].getY()) {
10319 crow.end = cells[i];
10335 for (var i = 0; i < cells.length;i++) {
10336 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10340 this.calevents.push(ev);
10343 clearEvents: function() {
10345 if(!this.calevents){
10349 Roo.each(this.cells.elements, function(c){
10353 Roo.each(this.calevents, function(e) {
10354 Roo.each(e.els, function(el) {
10355 el.un('mouseenter' ,this.onEventEnter, this);
10356 el.un('mouseleave' ,this.onEventLeave, this);
10363 renderEvents: function()
10365 // first make sure there is enough space..
10367 this.cells.each(function(c) {
10369 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10372 for (var e = 0; e < this.calevents.length; e++) {
10373 var ev = this.calevents[e];
10374 var cells = ev.cells;
10375 var rows = ev.rows;
10377 for(var i =0; i < rows.length; i++) {
10380 // how many rows should it span..
10383 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10384 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10386 unselectable : "on",
10389 cls: 'fc-event-inner',
10393 // cls: 'fc-event-time',
10394 // html : cells.length > 1 ? '' : ev.time
10398 cls: 'fc-event-title',
10399 html : String.format('{0}', ev.title)
10406 cls: 'ui-resizable-handle ui-resizable-e',
10407 html : '  '
10413 cfg.cls += ' fc-event-start';
10415 if ((i+1) == rows.length) {
10416 cfg.cls += ' fc-event-end';
10419 var ctr = this.el.select('.fc-event-container',true).first();
10420 var cg = ctr.createChild(cfg);
10422 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10423 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10424 cg.on('click', this.onEventClick, this, ev);
10428 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10429 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10431 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10432 cg.setWidth(ebox.right - sbox.x -2);
10440 onEventEnter: function (e, el,event,d) {
10441 this.fireEvent('evententer', this, el, event);
10444 onEventLeave: function (e, el,event,d) {
10445 this.fireEvent('eventleave', this, el, event);
10448 onEventClick: function (e, el,event,d) {
10449 this.fireEvent('eventclick', this, el, event);
10452 onMonthChange: function () {
10456 onLoad: function ()
10458 this.calevents = [];
10461 if(this.store.getCount() > 0){
10462 this.store.data.each(function(d){
10465 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10466 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10467 time : d.data.start_time,
10468 title : d.data.title,
10469 description : d.data.description,
10470 venue : d.data.venue
10475 this.renderEvents();
10478 this.maskEl.hide();
10482 onBeforeLoad: function()
10484 this.clearEvents();
10487 this.maskEl.show();
10501 * @class Roo.bootstrap.Popover
10502 * @extends Roo.bootstrap.Component
10503 * Bootstrap Popover class
10504 * @cfg {String} html contents of the popover (or false to use children..)
10505 * @cfg {String} title of popover (or false to hide)
10506 * @cfg {String} placement how it is placed
10507 * @cfg {String} trigger click || hover (or false to trigger manually)
10508 * @cfg {String} over what (parent or false to trigger manually.)
10511 * Create a new Popover
10512 * @param {Object} config The config object
10515 Roo.bootstrap.Popover = function(config){
10516 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10519 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10521 title: 'Fill in a title',
10524 placement : 'right',
10525 trigger : 'hover', // hover
10529 can_build_overlaid : false,
10531 getChildContainer : function()
10533 return this.el.select('.popover-content',true).first();
10536 getAutoCreate : function(){
10537 Roo.log('make popover?');
10539 cls : 'popover roo-dynamic',
10540 style: 'display:block',
10546 cls : 'popover-inner',
10550 cls: 'popover-title',
10554 cls : 'popover-content',
10565 setTitle: function(str)
10567 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10569 setContent: function(str)
10571 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10573 // as it get's added to the bottom of the page.
10574 onRender : function(ct, position)
10576 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10578 var cfg = Roo.apply({}, this.getAutoCreate());
10582 cfg.cls += ' ' + this.cls;
10585 cfg.style = this.style;
10587 Roo.log("adding to ")
10588 this.el = Roo.get(document.body).createChild(cfg, position);
10594 initEvents : function()
10596 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10597 this.el.enableDisplayMode('block');
10599 if (this.over === false) {
10602 if (this.triggers === false) {
10605 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10606 var triggers = this.trigger ? this.trigger.split(' ') : [];
10607 Roo.each(triggers, function(trigger) {
10609 if (trigger == 'click') {
10610 on_el.on('click', this.toggle, this);
10611 } else if (trigger != 'manual') {
10612 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10613 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10615 on_el.on(eventIn ,this.enter, this);
10616 on_el.on(eventOut, this.leave, this);
10627 toggle : function () {
10628 this.hoverState == 'in' ? this.leave() : this.enter();
10631 enter : function () {
10634 clearTimeout(this.timeout);
10636 this.hoverState = 'in'
10638 if (!this.delay || !this.delay.show) {
10643 this.timeout = setTimeout(function () {
10644 if (_t.hoverState == 'in') {
10647 }, this.delay.show)
10649 leave : function() {
10650 clearTimeout(this.timeout);
10652 this.hoverState = 'out'
10654 if (!this.delay || !this.delay.hide) {
10659 this.timeout = setTimeout(function () {
10660 if (_t.hoverState == 'out') {
10663 }, this.delay.hide)
10666 show : function (on_el)
10669 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10672 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10673 if (this.html !== false) {
10674 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10676 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10677 if (!this.title.length) {
10678 this.el.select('.popover-title',true).hide();
10681 var placement = typeof this.placement == 'function' ?
10682 this.placement.call(this, this.el, on_el) :
10685 var autoToken = /\s?auto?\s?/i;
10686 var autoPlace = autoToken.test(placement);
10688 placement = placement.replace(autoToken, '') || 'top';
10692 //this.el.setXY([0,0]);
10694 this.el.dom.style.display='block';
10695 this.el.addClass(placement);
10697 //this.el.appendTo(on_el);
10699 var p = this.getPosition();
10700 var box = this.el.getBox();
10705 var align = Roo.bootstrap.Popover.alignment[placement]
10706 this.el.alignTo(on_el, align[0],align[1]);
10707 //var arrow = this.el.select('.arrow',true).first();
10708 //arrow.set(align[2],
10710 this.el.addClass('in');
10711 this.hoverState = null;
10713 if (this.el.hasClass('fade')) {
10720 this.el.setXY([0,0]);
10721 this.el.removeClass('in');
10728 Roo.bootstrap.Popover.alignment = {
10729 'left' : ['r-l', [-10,0], 'right'],
10730 'right' : ['l-r', [10,0], 'left'],
10731 'bottom' : ['t-b', [0,10], 'top'],
10732 'top' : [ 'b-t', [0,-10], 'bottom']
10743 * @class Roo.bootstrap.Progress
10744 * @extends Roo.bootstrap.Component
10745 * Bootstrap Progress class
10746 * @cfg {Boolean} striped striped of the progress bar
10747 * @cfg {Boolean} active animated of the progress bar
10751 * Create a new Progress
10752 * @param {Object} config The config object
10755 Roo.bootstrap.Progress = function(config){
10756 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10759 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10764 getAutoCreate : function(){
10772 cfg.cls += ' progress-striped';
10776 cfg.cls += ' active';
10795 * @class Roo.bootstrap.ProgressBar
10796 * @extends Roo.bootstrap.Component
10797 * Bootstrap ProgressBar class
10798 * @cfg {Number} aria_valuenow aria-value now
10799 * @cfg {Number} aria_valuemin aria-value min
10800 * @cfg {Number} aria_valuemax aria-value max
10801 * @cfg {String} label label for the progress bar
10802 * @cfg {String} panel (success | info | warning | danger )
10803 * @cfg {String} role role of the progress bar
10804 * @cfg {String} sr_only text
10808 * Create a new ProgressBar
10809 * @param {Object} config The config object
10812 Roo.bootstrap.ProgressBar = function(config){
10813 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10816 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10820 aria_valuemax : 100,
10826 getAutoCreate : function()
10831 cls: 'progress-bar',
10832 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10844 cfg.role = this.role;
10847 if(this.aria_valuenow){
10848 cfg['aria-valuenow'] = this.aria_valuenow;
10851 if(this.aria_valuemin){
10852 cfg['aria-valuemin'] = this.aria_valuemin;
10855 if(this.aria_valuemax){
10856 cfg['aria-valuemax'] = this.aria_valuemax;
10859 if(this.label && !this.sr_only){
10860 cfg.html = this.label;
10864 cfg.cls += ' progress-bar-' + this.panel;
10870 update : function(aria_valuenow)
10872 this.aria_valuenow = aria_valuenow;
10874 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10889 * @class Roo.bootstrap.TabPanel
10890 * @extends Roo.bootstrap.Component
10891 * Bootstrap TabPanel class
10892 * @cfg {Boolean} active panel active
10893 * @cfg {String} html panel content
10894 * @cfg {String} tabId tab relate id
10898 * Create a new TabPanel
10899 * @param {Object} config The config object
10902 Roo.bootstrap.TabPanel = function(config){
10903 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10906 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10912 getAutoCreate : function(){
10916 html: this.html || ''
10920 cfg.cls += ' active';
10924 cfg.tabId = this.tabId;
10942 * @class Roo.bootstrap.DateField
10943 * @extends Roo.bootstrap.Input
10944 * Bootstrap DateField class
10945 * @cfg {Number} weekStart default 0
10946 * @cfg {Number} weekStart default 0
10947 * @cfg {Number} viewMode default empty, (months|years)
10948 * @cfg {Number} minViewMode default empty, (months|years)
10949 * @cfg {Number} startDate default -Infinity
10950 * @cfg {Number} endDate default Infinity
10951 * @cfg {Boolean} todayHighlight default false
10952 * @cfg {Boolean} todayBtn default false
10953 * @cfg {Boolean} calendarWeeks default false
10954 * @cfg {Object} daysOfWeekDisabled default empty
10956 * @cfg {Boolean} keyboardNavigation default true
10957 * @cfg {String} language default en
10960 * Create a new DateField
10961 * @param {Object} config The config object
10964 Roo.bootstrap.DateField = function(config){
10965 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10969 * Fires when this field show.
10970 * @param {Roo.bootstrap.DateField} this
10971 * @param {Mixed} date The date value
10976 * Fires when this field hide.
10977 * @param {Roo.bootstrap.DateField} this
10978 * @param {Mixed} date The date value
10983 * Fires when select a date.
10984 * @param {Roo.bootstrap.DateField} this
10985 * @param {Mixed} date The date value
10991 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
10994 * @cfg {String} format
10995 * The default date format string which can be overriden for localization support. The format must be
10996 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11000 * @cfg {String} altFormats
11001 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11002 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11004 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11012 todayHighlight : false,
11018 keyboardNavigation: true,
11020 calendarWeeks: false,
11022 startDate: -Infinity,
11026 daysOfWeekDisabled: [],
11030 UTCDate: function()
11032 return new Date(Date.UTC.apply(Date, arguments));
11035 UTCToday: function()
11037 var today = new Date();
11038 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11041 getDate: function() {
11042 var d = this.getUTCDate();
11043 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11046 getUTCDate: function() {
11050 setDate: function(d) {
11051 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11054 setUTCDate: function(d) {
11056 this.setValue(this.formatDate(this.date));
11059 onRender: function(ct, position)
11062 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11064 this.language = this.language || 'en';
11065 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11066 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11068 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11069 this.format = this.format || 'm/d/y';
11070 this.isInline = false;
11071 this.isInput = true;
11072 this.component = this.el.select('.add-on', true).first() || false;
11073 this.component = (this.component && this.component.length === 0) ? false : this.component;
11074 this.hasInput = this.component && this.inputEL().length;
11076 if (typeof(this.minViewMode === 'string')) {
11077 switch (this.minViewMode) {
11079 this.minViewMode = 1;
11082 this.minViewMode = 2;
11085 this.minViewMode = 0;
11090 if (typeof(this.viewMode === 'string')) {
11091 switch (this.viewMode) {
11104 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11106 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11108 this.picker().on('mousedown', this.onMousedown, this);
11109 this.picker().on('click', this.onClick, this);
11111 this.picker().addClass('datepicker-dropdown');
11113 this.startViewMode = this.viewMode;
11116 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11117 if(!this.calendarWeeks){
11122 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11123 v.attr('colspan', function(i, val){
11124 return parseInt(val) + 1;
11129 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11131 this.setStartDate(this.startDate);
11132 this.setEndDate(this.endDate);
11134 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11141 if(this.isInline) {
11146 picker : function()
11148 return this.el.select('.datepicker', true).first();
11151 fillDow: function()
11153 var dowCnt = this.weekStart;
11162 if(this.calendarWeeks){
11170 while (dowCnt < this.weekStart + 7) {
11174 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11178 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11181 fillMonths: function()
11184 var months = this.picker().select('>.datepicker-months td', true).first();
11186 months.dom.innerHTML = '';
11192 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11195 months.createChild(month);
11200 update: function(){
11202 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11204 if (this.date < this.startDate) {
11205 this.viewDate = new Date(this.startDate);
11206 } else if (this.date > this.endDate) {
11207 this.viewDate = new Date(this.endDate);
11209 this.viewDate = new Date(this.date);
11216 var d = new Date(this.viewDate),
11217 year = d.getUTCFullYear(),
11218 month = d.getUTCMonth(),
11219 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11220 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11221 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11222 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11223 currentDate = this.date && this.date.valueOf(),
11224 today = this.UTCToday();
11226 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11228 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11230 // this.picker.select('>tfoot th.today').
11231 // .text(dates[this.language].today)
11232 // .toggle(this.todayBtn !== false);
11234 this.updateNavArrows();
11237 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11239 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11241 prevMonth.setUTCDate(day);
11243 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11245 var nextMonth = new Date(prevMonth);
11247 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11249 nextMonth = nextMonth.valueOf();
11251 var fillMonths = false;
11253 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11255 while(prevMonth.valueOf() < nextMonth) {
11258 if (prevMonth.getUTCDay() === this.weekStart) {
11260 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11268 if(this.calendarWeeks){
11269 // ISO 8601: First week contains first thursday.
11270 // ISO also states week starts on Monday, but we can be more abstract here.
11272 // Start of current week: based on weekstart/current date
11273 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11274 // Thursday of this week
11275 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11276 // First Thursday of year, year from thursday
11277 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11278 // Calendar week: ms between thursdays, div ms per day, div 7 days
11279 calWeek = (th - yth) / 864e5 / 7 + 1;
11281 fillMonths.cn.push({
11289 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11291 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11294 if (this.todayHighlight &&
11295 prevMonth.getUTCFullYear() == today.getFullYear() &&
11296 prevMonth.getUTCMonth() == today.getMonth() &&
11297 prevMonth.getUTCDate() == today.getDate()) {
11298 clsName += ' today';
11301 if (currentDate && prevMonth.valueOf() === currentDate) {
11302 clsName += ' active';
11305 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11306 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11307 clsName += ' disabled';
11310 fillMonths.cn.push({
11312 cls: 'day ' + clsName,
11313 html: prevMonth.getDate()
11316 prevMonth.setDate(prevMonth.getDate()+1);
11319 var currentYear = this.date && this.date.getUTCFullYear();
11320 var currentMonth = this.date && this.date.getUTCMonth();
11322 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11324 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11325 v.removeClass('active');
11327 if(currentYear === year && k === currentMonth){
11328 v.addClass('active');
11331 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11332 v.addClass('disabled');
11338 year = parseInt(year/10, 10) * 10;
11340 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11342 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11345 for (var i = -1; i < 11; i++) {
11346 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11348 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11356 showMode: function(dir) {
11358 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11360 Roo.each(this.picker().select('>div',true).elements, function(v){
11361 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11364 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11369 if(this.isInline) return;
11371 this.picker().removeClass(['bottom', 'top']);
11373 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11375 * place to the top of element!
11379 this.picker().addClass('top');
11380 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11385 this.picker().addClass('bottom');
11387 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11390 parseDate : function(value){
11391 if(!value || value instanceof Date){
11394 var v = Date.parseDate(value, this.format);
11395 if (!v && this.useIso) {
11396 v = Date.parseDate(value, 'Y-m-d');
11398 if(!v && this.altFormats){
11399 if(!this.altFormatsArray){
11400 this.altFormatsArray = this.altFormats.split("|");
11402 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11403 v = Date.parseDate(value, this.altFormatsArray[i]);
11409 formatDate : function(date, fmt){
11410 return (!date || !(date instanceof Date)) ?
11411 date : date.dateFormat(fmt || this.format);
11414 onFocus : function()
11416 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11420 onBlur : function()
11422 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11428 this.picker().show();
11432 this.fireEvent('show', this, this.date);
11437 if(this.isInline) return;
11438 this.picker().hide();
11439 this.viewMode = this.startViewMode;
11442 this.fireEvent('hide', this, this.date);
11446 onMousedown: function(e){
11447 e.stopPropagation();
11448 e.preventDefault();
11451 keyup: function(e){
11452 Roo.bootstrap.DateField.superclass.keyup.call(this);
11457 setValue: function(v){
11458 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11460 this.fireEvent('select', this, this.date);
11464 fireKey: function(e){
11465 if (!this.picker().isVisible()){
11466 if (e.keyCode == 27) // allow escape to hide and re-show picker
11470 var dateChanged = false,
11472 newDate, newViewDate;
11476 e.preventDefault();
11480 if (!this.keyboardNavigation) break;
11481 dir = e.keyCode == 37 ? -1 : 1;
11484 newDate = this.moveYear(this.date, dir);
11485 newViewDate = this.moveYear(this.viewDate, dir);
11486 } else if (e.shiftKey){
11487 newDate = this.moveMonth(this.date, dir);
11488 newViewDate = this.moveMonth(this.viewDate, dir);
11490 newDate = new Date(this.date);
11491 newDate.setUTCDate(this.date.getUTCDate() + dir);
11492 newViewDate = new Date(this.viewDate);
11493 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11495 if (this.dateWithinRange(newDate)){
11496 this.date = newDate;
11497 this.viewDate = newViewDate;
11498 this.setValue(this.formatDate(this.date));
11500 e.preventDefault();
11501 dateChanged = true;
11506 if (!this.keyboardNavigation) break;
11507 dir = e.keyCode == 38 ? -1 : 1;
11509 newDate = this.moveYear(this.date, dir);
11510 newViewDate = this.moveYear(this.viewDate, dir);
11511 } else if (e.shiftKey){
11512 newDate = this.moveMonth(this.date, dir);
11513 newViewDate = this.moveMonth(this.viewDate, dir);
11515 newDate = new Date(this.date);
11516 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11517 newViewDate = new Date(this.viewDate);
11518 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11520 if (this.dateWithinRange(newDate)){
11521 this.date = newDate;
11522 this.viewDate = newViewDate;
11523 this.setValue(this.formatDate(this.date));
11525 e.preventDefault();
11526 dateChanged = true;
11530 this.setValue(this.formatDate(this.date));
11532 e.preventDefault();
11535 this.setValue(this.formatDate(this.date));
11542 onClick: function(e) {
11543 e.stopPropagation();
11544 e.preventDefault();
11546 var target = e.getTarget();
11548 if(target.nodeName.toLowerCase() === 'i'){
11549 target = Roo.get(target).dom.parentNode;
11552 var nodeName = target.nodeName;
11553 var className = target.className;
11554 var html = target.innerHTML;
11556 switch(nodeName.toLowerCase()) {
11558 switch(className) {
11564 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11565 switch(this.viewMode){
11567 this.viewDate = this.moveMonth(this.viewDate, dir);
11571 this.viewDate = this.moveYear(this.viewDate, dir);
11577 var date = new Date();
11578 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11580 this.setValue(this.formatDate(this.date));
11586 if (className.indexOf('disabled') === -1) {
11587 this.viewDate.setUTCDate(1);
11588 if (className.indexOf('month') !== -1) {
11589 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11591 var year = parseInt(html, 10) || 0;
11592 this.viewDate.setUTCFullYear(year);
11601 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11602 var day = parseInt(html, 10) || 1;
11603 var year = this.viewDate.getUTCFullYear(),
11604 month = this.viewDate.getUTCMonth();
11606 if (className.indexOf('old') !== -1) {
11613 } else if (className.indexOf('new') !== -1) {
11621 this.date = this.UTCDate(year, month, day,0,0,0,0);
11622 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11624 this.setValue(this.formatDate(this.date));
11631 setStartDate: function(startDate){
11632 this.startDate = startDate || -Infinity;
11633 if (this.startDate !== -Infinity) {
11634 this.startDate = this.parseDate(this.startDate);
11637 this.updateNavArrows();
11640 setEndDate: function(endDate){
11641 this.endDate = endDate || Infinity;
11642 if (this.endDate !== Infinity) {
11643 this.endDate = this.parseDate(this.endDate);
11646 this.updateNavArrows();
11649 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11650 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11651 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11652 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11654 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11655 return parseInt(d, 10);
11658 this.updateNavArrows();
11661 updateNavArrows: function() {
11662 var d = new Date(this.viewDate),
11663 year = d.getUTCFullYear(),
11664 month = d.getUTCMonth();
11666 Roo.each(this.picker().select('.prev', true).elements, function(v){
11668 switch (this.viewMode) {
11671 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11677 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11684 Roo.each(this.picker().select('.next', true).elements, function(v){
11686 switch (this.viewMode) {
11689 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11695 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11703 moveMonth: function(date, dir){
11704 if (!dir) return date;
11705 var new_date = new Date(date.valueOf()),
11706 day = new_date.getUTCDate(),
11707 month = new_date.getUTCMonth(),
11708 mag = Math.abs(dir),
11710 dir = dir > 0 ? 1 : -1;
11713 // If going back one month, make sure month is not current month
11714 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11716 return new_date.getUTCMonth() == month;
11718 // If going forward one month, make sure month is as expected
11719 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11721 return new_date.getUTCMonth() != new_month;
11723 new_month = month + dir;
11724 new_date.setUTCMonth(new_month);
11725 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11726 if (new_month < 0 || new_month > 11)
11727 new_month = (new_month + 12) % 12;
11729 // For magnitudes >1, move one month at a time...
11730 for (var i=0; i<mag; i++)
11731 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11732 new_date = this.moveMonth(new_date, dir);
11733 // ...then reset the day, keeping it in the new month
11734 new_month = new_date.getUTCMonth();
11735 new_date.setUTCDate(day);
11737 return new_month != new_date.getUTCMonth();
11740 // Common date-resetting loop -- if date is beyond end of month, make it
11743 new_date.setUTCDate(--day);
11744 new_date.setUTCMonth(new_month);
11749 moveYear: function(date, dir){
11750 return this.moveMonth(date, dir*12);
11753 dateWithinRange: function(date){
11754 return date >= this.startDate && date <= this.endDate;
11758 remove: function() {
11759 this.picker().remove();
11764 Roo.apply(Roo.bootstrap.DateField, {
11775 html: '<i class="icon-arrow-left"/>'
11785 html: '<i class="icon-arrow-right"/>'
11827 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11828 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11829 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11830 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11831 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11844 navFnc: 'FullYear',
11849 navFnc: 'FullYear',
11854 Roo.apply(Roo.bootstrap.DateField, {
11858 cls: 'datepicker dropdown-menu',
11862 cls: 'datepicker-days',
11866 cls: 'table-condensed',
11868 Roo.bootstrap.DateField.head,
11872 Roo.bootstrap.DateField.footer
11879 cls: 'datepicker-months',
11883 cls: 'table-condensed',
11885 Roo.bootstrap.DateField.head,
11886 Roo.bootstrap.DateField.content,
11887 Roo.bootstrap.DateField.footer
11894 cls: 'datepicker-years',
11898 cls: 'table-condensed',
11900 Roo.bootstrap.DateField.head,
11901 Roo.bootstrap.DateField.content,
11902 Roo.bootstrap.DateField.footer
11921 * @class Roo.bootstrap.TimeField
11922 * @extends Roo.bootstrap.Input
11923 * Bootstrap DateField class
11927 * Create a new TimeField
11928 * @param {Object} config The config object
11931 Roo.bootstrap.TimeField = function(config){
11932 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11936 * Fires when this field show.
11937 * @param {Roo.bootstrap.DateField} this
11938 * @param {Mixed} date The date value
11943 * Fires when this field hide.
11944 * @param {Roo.bootstrap.DateField} this
11945 * @param {Mixed} date The date value
11950 * Fires when select a date.
11951 * @param {Roo.bootstrap.DateField} this
11952 * @param {Mixed} date The date value
11958 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
11961 * @cfg {String} format
11962 * The default time format string which can be overriden for localization support. The format must be
11963 * valid according to {@link Date#parseDate} (defaults to 'H:i').
11967 onRender: function(ct, position)
11970 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11972 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11974 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11976 this.pop = this.picker().select('>.datepicker-time',true).first();
11977 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
11979 this.picker().on('mousedown', this.onMousedown, this);
11980 this.picker().on('click', this.onClick, this);
11982 this.picker().addClass('datepicker-dropdown');
11987 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11988 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11989 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11990 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11991 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11992 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11996 fireKey: function(e){
11997 if (!this.picker().isVisible()){
11998 if (e.keyCode == 27) // allow escape to hide and re-show picker
12003 e.preventDefault();
12011 this.onTogglePeriod();
12014 this.onIncrementMinutes();
12017 this.onDecrementMinutes();
12026 onClick: function(e) {
12027 e.stopPropagation();
12028 e.preventDefault();
12031 picker : function()
12033 return this.el.select('.datepicker', true).first();
12036 fillTime: function()
12038 var time = this.pop.select('tbody', true).first();
12040 time.dom.innerHTML = '';
12055 cls: 'hours-up glyphicon glyphicon-chevron-up'
12075 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12096 cls: 'timepicker-hour',
12111 cls: 'timepicker-minute',
12126 cls: 'btn btn-primary period',
12148 cls: 'hours-down glyphicon glyphicon-chevron-down'
12168 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12186 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12193 var hours = this.time.getHours();
12194 var minutes = this.time.getMinutes();
12207 hours = hours - 12;
12211 hours = '0' + hours;
12215 minutes = '0' + minutes;
12218 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12219 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12220 this.pop.select('button', true).first().dom.innerHTML = period;
12226 this.picker().removeClass(['bottom', 'top']);
12228 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12230 * place to the top of element!
12234 this.picker().addClass('top');
12235 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12240 this.picker().addClass('bottom');
12242 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12245 onFocus : function()
12247 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12251 onBlur : function()
12253 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12259 this.picker().show();
12264 this.fireEvent('show', this, this.date);
12269 this.picker().hide();
12272 this.fireEvent('hide', this, this.date);
12275 setTime : function()
12278 this.setValue(this.time.format(this.format));
12280 this.fireEvent('select', this, this.date);
12285 onMousedown: function(e){
12286 e.stopPropagation();
12287 e.preventDefault();
12290 onIncrementHours: function()
12292 Roo.log('onIncrementHours');
12293 this.time = this.time.add(Date.HOUR, 1);
12298 onDecrementHours: function()
12300 Roo.log('onDecrementHours');
12301 this.time = this.time.add(Date.HOUR, -1);
12305 onIncrementMinutes: function()
12307 Roo.log('onIncrementMinutes');
12308 this.time = this.time.add(Date.MINUTE, 1);
12312 onDecrementMinutes: function()
12314 Roo.log('onDecrementMinutes');
12315 this.time = this.time.add(Date.MINUTE, -1);
12319 onTogglePeriod: function()
12321 Roo.log('onTogglePeriod');
12322 this.time = this.time.add(Date.HOUR, 12);
12329 Roo.apply(Roo.bootstrap.TimeField, {
12359 cls: 'btn btn-info ok',
12371 Roo.apply(Roo.bootstrap.TimeField, {
12375 cls: 'datepicker dropdown-menu',
12379 cls: 'datepicker-time',
12383 cls: 'table-condensed',
12385 Roo.bootstrap.TimeField.content,
12386 Roo.bootstrap.TimeField.footer
12405 * @class Roo.bootstrap.CheckBox
12406 * @extends Roo.bootstrap.Input
12407 * Bootstrap CheckBox class
12409 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12410 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12411 * @cfg {String} boxLabel The text that appears beside the checkbox
12412 * @cfg {Boolean} checked initnal the element
12415 * Create a new CheckBox
12416 * @param {Object} config The config object
12419 Roo.bootstrap.CheckBox = function(config){
12420 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12425 * Fires when the element is checked or unchecked.
12426 * @param {Roo.bootstrap.CheckBox} this This input
12427 * @param {Boolean} checked The new checked value
12433 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12435 inputType: 'checkbox',
12441 getAutoCreate : function()
12443 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12449 cfg.cls = 'form-group' //input-group
12454 type : this.inputType,
12455 value : (!this.checked) ? this.valueOff : this.inputValue,
12457 placeholder : this.placeholder || ''
12461 if (this.disabled) {
12462 input.disabled=true;
12466 input.checked = this.checked;
12470 input.name = this.name;
12474 input.cls += ' input-' + this.size;
12478 ['xs','sm','md','lg'].map(function(size){
12479 if (settings[size]) {
12480 cfg.cls += ' col-' + size + '-' + settings[size];
12484 var inputblock = input;
12486 if (this.before || this.after) {
12489 cls : 'input-group',
12493 inputblock.cn.push({
12495 cls : 'input-group-addon',
12499 inputblock.cn.push(input);
12501 inputblock.cn.push({
12503 cls : 'input-group-addon',
12510 if (align ==='left' && this.fieldLabel.length) {
12511 Roo.log("left and has label");
12517 cls : 'control-label col-md-' + this.labelWidth,
12518 html : this.fieldLabel
12522 cls : "col-md-" + (12 - this.labelWidth),
12529 } else if ( this.fieldLabel.length) {
12534 tag: this.boxLabel ? 'span' : 'label',
12536 cls: 'control-label box-input-label',
12537 //cls : 'input-group-addon',
12538 html : this.fieldLabel
12548 Roo.log(" no label && no align");
12563 html: this.boxLabel
12572 * return the real input element.
12574 inputEl: function ()
12576 return this.el.select('input.form-box',true).first();
12579 initEvents : function()
12581 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12583 this.inputEl().on('click', this.onClick, this);
12587 onClick : function()
12589 this.setChecked(!this.checked);
12592 setChecked : function(state,suppressEvent)
12594 this.checked = state;
12596 this.inputEl().dom.checked = state;
12598 if(suppressEvent !== true){
12599 this.fireEvent('check', this, state);
12602 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12606 setValue : function(v,suppressEvent)
12608 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12622 * @class Roo.bootstrap.Radio
12623 * @extends Roo.bootstrap.CheckBox
12624 * Bootstrap Radio class
12627 * Create a new Radio
12628 * @param {Object} config The config object
12631 Roo.bootstrap.Radio = function(config){
12632 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12636 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12638 inputType: 'radio',
12642 getAutoCreate : function()
12644 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12650 cfg.cls = 'form-group' //input-group
12655 type : this.inputType,
12656 value : (!this.checked) ? this.valueOff : this.inputValue,
12658 placeholder : this.placeholder || ''
12662 if (this.disabled) {
12663 input.disabled=true;
12667 input.checked = this.checked;
12671 input.name = this.name;
12675 input.cls += ' input-' + this.size;
12679 ['xs','sm','md','lg'].map(function(size){
12680 if (settings[size]) {
12681 cfg.cls += ' col-' + size + '-' + settings[size];
12685 var inputblock = input;
12687 if (this.before || this.after) {
12690 cls : 'input-group',
12694 inputblock.cn.push({
12696 cls : 'input-group-addon',
12700 inputblock.cn.push(input);
12702 inputblock.cn.push({
12704 cls : 'input-group-addon',
12711 if (align ==='left' && this.fieldLabel.length) {
12712 Roo.log("left and has label");
12718 cls : 'control-label col-md-' + this.labelWidth,
12719 html : this.fieldLabel
12723 cls : "col-md-" + (12 - this.labelWidth),
12730 } else if ( this.fieldLabel.length) {
12737 cls: 'control-label box-input-label',
12738 //cls : 'input-group-addon',
12739 html : this.fieldLabel
12749 Roo.log(" no label && no align");
12764 html: this.boxLabel
12772 onClick : function()
12774 this.setChecked(true);
12777 setChecked : function(state,suppressEvent)
12780 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12781 v.dom.checked = false;
12785 this.checked = state;
12786 this.inputEl().dom.checked = state;
12788 if(suppressEvent !== true){
12789 this.fireEvent('check', this, state);
12792 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12796 getGroupValue : function()
12799 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12800 if(v.dom.checked == true){
12801 value = v.dom.value;
12809 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12810 * @return {Mixed} value The field value
12812 getValue : function(){
12813 return this.getGroupValue();
12819 //<script type="text/javascript">
12822 * Based Ext JS Library 1.1.1
12823 * Copyright(c) 2006-2007, Ext JS, LLC.
12829 * @class Roo.HtmlEditorCore
12830 * @extends Roo.Component
12831 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12833 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12836 Roo.HtmlEditorCore = function(config){
12839 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12842 * @event initialize
12843 * Fires when the editor is fully initialized (including the iframe)
12844 * @param {Roo.HtmlEditorCore} this
12849 * Fires when the editor is first receives the focus. Any insertion must wait
12850 * until after this event.
12851 * @param {Roo.HtmlEditorCore} this
12855 * @event beforesync
12856 * Fires before the textarea is updated with content from the editor iframe. Return false
12857 * to cancel the sync.
12858 * @param {Roo.HtmlEditorCore} this
12859 * @param {String} html
12863 * @event beforepush
12864 * Fires before the iframe editor is updated with content from the textarea. Return false
12865 * to cancel the push.
12866 * @param {Roo.HtmlEditorCore} this
12867 * @param {String} html
12872 * Fires when the textarea is updated with content from the editor iframe.
12873 * @param {Roo.HtmlEditorCore} this
12874 * @param {String} html
12879 * Fires when the iframe editor is updated with content from the textarea.
12880 * @param {Roo.HtmlEditorCore} this
12881 * @param {String} html
12886 * @event editorevent
12887 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12888 * @param {Roo.HtmlEditorCore} this
12896 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12900 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12906 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12911 * @cfg {Number} height (in pixels)
12915 * @cfg {Number} width (in pixels)
12920 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12923 stylesheets: false,
12928 // private properties
12929 validationEvent : false,
12931 initialized : false,
12933 sourceEditMode : false,
12934 onFocus : Roo.emptyFn,
12936 hideMode:'offsets',
12944 * Protected method that will not generally be called directly. It
12945 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12946 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12948 getDocMarkup : function(){
12951 Roo.log(this.stylesheets);
12953 // inherit styels from page...??
12954 if (this.stylesheets === false) {
12956 Roo.get(document.head).select('style').each(function(node) {
12957 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12960 Roo.get(document.head).select('link').each(function(node) {
12961 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12964 } else if (!this.stylesheets.length) {
12966 st = '<style type="text/css">' +
12967 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12970 Roo.each(this.stylesheets, function(s) {
12971 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12976 st += '<style type="text/css">' +
12977 'IMG { cursor: pointer } ' +
12981 return '<html><head>' + st +
12982 //<style type="text/css">' +
12983 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12985 ' </head><body class="roo-htmleditor-body"></body></html>';
12989 onRender : function(ct, position)
12992 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12993 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
12996 this.el.dom.style.border = '0 none';
12997 this.el.dom.setAttribute('tabIndex', -1);
12998 this.el.addClass('x-hidden hide');
13002 if(Roo.isIE){ // fix IE 1px bogus margin
13003 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13007 this.frameId = Roo.id();
13011 var iframe = this.owner.wrap.createChild({
13013 cls: 'form-control', // bootstrap..
13015 name: this.frameId,
13016 frameBorder : 'no',
13017 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13022 this.iframe = iframe.dom;
13024 this.assignDocWin();
13026 this.doc.designMode = 'on';
13029 this.doc.write(this.getDocMarkup());
13033 var task = { // must defer to wait for browser to be ready
13035 //console.log("run task?" + this.doc.readyState);
13036 this.assignDocWin();
13037 if(this.doc.body || this.doc.readyState == 'complete'){
13039 this.doc.designMode="on";
13043 Roo.TaskMgr.stop(task);
13044 this.initEditor.defer(10, this);
13051 Roo.TaskMgr.start(task);
13058 onResize : function(w, h)
13060 Roo.log('resize: ' +w + ',' + h );
13061 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13065 if(typeof w == 'number'){
13067 this.iframe.style.width = w + 'px';
13069 if(typeof h == 'number'){
13071 this.iframe.style.height = h + 'px';
13073 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13080 * Toggles the editor between standard and source edit mode.
13081 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13083 toggleSourceEdit : function(sourceEditMode){
13085 this.sourceEditMode = sourceEditMode === true;
13087 if(this.sourceEditMode){
13089 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13092 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13093 //this.iframe.className = '';
13096 //this.setSize(this.owner.wrap.getSize());
13097 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13104 * Protected method that will not generally be called directly. If you need/want
13105 * custom HTML cleanup, this is the method you should override.
13106 * @param {String} html The HTML to be cleaned
13107 * return {String} The cleaned HTML
13109 cleanHtml : function(html){
13110 html = String(html);
13111 if(html.length > 5){
13112 if(Roo.isSafari){ // strip safari nonsense
13113 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13116 if(html == ' '){
13123 * HTML Editor -> Textarea
13124 * Protected method that will not generally be called directly. Syncs the contents
13125 * of the editor iframe with the textarea.
13127 syncValue : function(){
13128 if(this.initialized){
13129 var bd = (this.doc.body || this.doc.documentElement);
13130 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13131 var html = bd.innerHTML;
13133 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13134 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13136 html = '<div style="'+m[0]+'">' + html + '</div>';
13139 html = this.cleanHtml(html);
13140 // fix up the special chars.. normaly like back quotes in word...
13141 // however we do not want to do this with chinese..
13142 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13143 var cc = b.charCodeAt();
13145 (cc >= 0x4E00 && cc < 0xA000 ) ||
13146 (cc >= 0x3400 && cc < 0x4E00 ) ||
13147 (cc >= 0xf900 && cc < 0xfb00 )
13153 if(this.owner.fireEvent('beforesync', this, html) !== false){
13154 this.el.dom.value = html;
13155 this.owner.fireEvent('sync', this, html);
13161 * Protected method that will not generally be called directly. Pushes the value of the textarea
13162 * into the iframe editor.
13164 pushValue : function(){
13165 if(this.initialized){
13166 var v = this.el.dom.value.trim();
13168 // if(v.length < 1){
13172 if(this.owner.fireEvent('beforepush', this, v) !== false){
13173 var d = (this.doc.body || this.doc.documentElement);
13175 this.cleanUpPaste();
13176 this.el.dom.value = d.innerHTML;
13177 this.owner.fireEvent('push', this, v);
13183 deferFocus : function(){
13184 this.focus.defer(10, this);
13188 focus : function(){
13189 if(this.win && !this.sourceEditMode){
13196 assignDocWin: function()
13198 var iframe = this.iframe;
13201 this.doc = iframe.contentWindow.document;
13202 this.win = iframe.contentWindow;
13204 if (!Roo.get(this.frameId)) {
13207 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13208 this.win = Roo.get(this.frameId).dom.contentWindow;
13213 initEditor : function(){
13214 //console.log("INIT EDITOR");
13215 this.assignDocWin();
13219 this.doc.designMode="on";
13221 this.doc.write(this.getDocMarkup());
13224 var dbody = (this.doc.body || this.doc.documentElement);
13225 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13226 // this copies styles from the containing element into thsi one..
13227 // not sure why we need all of this..
13228 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13229 ss['background-attachment'] = 'fixed'; // w3c
13230 dbody.bgProperties = 'fixed'; // ie
13231 Roo.DomHelper.applyStyles(dbody, ss);
13232 Roo.EventManager.on(this.doc, {
13233 //'mousedown': this.onEditorEvent,
13234 'mouseup': this.onEditorEvent,
13235 'dblclick': this.onEditorEvent,
13236 'click': this.onEditorEvent,
13237 'keyup': this.onEditorEvent,
13242 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13244 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13245 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13247 this.initialized = true;
13249 this.owner.fireEvent('initialize', this);
13254 onDestroy : function(){
13260 //for (var i =0; i < this.toolbars.length;i++) {
13261 // // fixme - ask toolbars for heights?
13262 // this.toolbars[i].onDestroy();
13265 //this.wrap.dom.innerHTML = '';
13266 //this.wrap.remove();
13271 onFirstFocus : function(){
13273 this.assignDocWin();
13276 this.activated = true;
13279 if(Roo.isGecko){ // prevent silly gecko errors
13281 var s = this.win.getSelection();
13282 if(!s.focusNode || s.focusNode.nodeType != 3){
13283 var r = s.getRangeAt(0);
13284 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13289 this.execCmd('useCSS', true);
13290 this.execCmd('styleWithCSS', false);
13293 this.owner.fireEvent('activate', this);
13297 adjustFont: function(btn){
13298 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13299 //if(Roo.isSafari){ // safari
13302 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13303 if(Roo.isSafari){ // safari
13304 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13305 v = (v < 10) ? 10 : v;
13306 v = (v > 48) ? 48 : v;
13307 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13312 v = Math.max(1, v+adjust);
13314 this.execCmd('FontSize', v );
13317 onEditorEvent : function(e){
13318 this.owner.fireEvent('editorevent', this, e);
13319 // this.updateToolbar();
13320 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13323 insertTag : function(tg)
13325 // could be a bit smarter... -> wrap the current selected tRoo..
13326 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13328 range = this.createRange(this.getSelection());
13329 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13330 wrappingNode.appendChild(range.extractContents());
13331 range.insertNode(wrappingNode);
13338 this.execCmd("formatblock", tg);
13342 insertText : function(txt)
13346 var range = this.createRange();
13347 range.deleteContents();
13348 //alert(Sender.getAttribute('label'));
13350 range.insertNode(this.doc.createTextNode(txt));
13356 * Executes a Midas editor command on the editor document and performs necessary focus and
13357 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13358 * @param {String} cmd The Midas command
13359 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13361 relayCmd : function(cmd, value){
13363 this.execCmd(cmd, value);
13364 this.owner.fireEvent('editorevent', this);
13365 //this.updateToolbar();
13366 this.owner.deferFocus();
13370 * Executes a Midas editor command directly on the editor document.
13371 * For visual commands, you should use {@link #relayCmd} instead.
13372 * <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 execCmd : function(cmd, value){
13377 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13384 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13386 * @param {String} text | dom node..
13388 insertAtCursor : function(text)
13393 if(!this.activated){
13399 var r = this.doc.selection.createRange();
13410 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13414 // from jquery ui (MIT licenced)
13416 var win = this.win;
13418 if (win.getSelection && win.getSelection().getRangeAt) {
13419 range = win.getSelection().getRangeAt(0);
13420 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13421 range.insertNode(node);
13422 } else if (win.document.selection && win.document.selection.createRange) {
13423 // no firefox support
13424 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13425 win.document.selection.createRange().pasteHTML(txt);
13427 // no firefox support
13428 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13429 this.execCmd('InsertHTML', txt);
13438 mozKeyPress : function(e){
13440 var c = e.getCharCode(), cmd;
13443 c = String.fromCharCode(c).toLowerCase();
13457 this.cleanUpPaste.defer(100, this);
13465 e.preventDefault();
13473 fixKeys : function(){ // load time branching for fastest keydown performance
13475 return function(e){
13476 var k = e.getKey(), r;
13479 r = this.doc.selection.createRange();
13482 r.pasteHTML('    ');
13489 r = this.doc.selection.createRange();
13491 var target = r.parentElement();
13492 if(!target || target.tagName.toLowerCase() != 'li'){
13494 r.pasteHTML('<br />');
13500 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13501 this.cleanUpPaste.defer(100, this);
13507 }else if(Roo.isOpera){
13508 return function(e){
13509 var k = e.getKey();
13513 this.execCmd('InsertHTML','    ');
13516 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13517 this.cleanUpPaste.defer(100, this);
13522 }else if(Roo.isSafari){
13523 return function(e){
13524 var k = e.getKey();
13528 this.execCmd('InsertText','\t');
13532 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13533 this.cleanUpPaste.defer(100, this);
13541 getAllAncestors: function()
13543 var p = this.getSelectedNode();
13546 a.push(p); // push blank onto stack..
13547 p = this.getParentElement();
13551 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13555 a.push(this.doc.body);
13559 lastSelNode : false,
13562 getSelection : function()
13564 this.assignDocWin();
13565 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13568 getSelectedNode: function()
13570 // this may only work on Gecko!!!
13572 // should we cache this!!!!
13577 var range = this.createRange(this.getSelection()).cloneRange();
13580 var parent = range.parentElement();
13582 var testRange = range.duplicate();
13583 testRange.moveToElementText(parent);
13584 if (testRange.inRange(range)) {
13587 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13590 parent = parent.parentElement;
13595 // is ancestor a text element.
13596 var ac = range.commonAncestorContainer;
13597 if (ac.nodeType == 3) {
13598 ac = ac.parentNode;
13601 var ar = ac.childNodes;
13604 var other_nodes = [];
13605 var has_other_nodes = false;
13606 for (var i=0;i<ar.length;i++) {
13607 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13610 // fullly contained node.
13612 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13617 // probably selected..
13618 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13619 other_nodes.push(ar[i]);
13623 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13628 has_other_nodes = true;
13630 if (!nodes.length && other_nodes.length) {
13631 nodes= other_nodes;
13633 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13639 createRange: function(sel)
13641 // this has strange effects when using with
13642 // top toolbar - not sure if it's a great idea.
13643 //this.editor.contentWindow.focus();
13644 if (typeof sel != "undefined") {
13646 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13648 return this.doc.createRange();
13651 return this.doc.createRange();
13654 getParentElement: function()
13657 this.assignDocWin();
13658 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13660 var range = this.createRange(sel);
13663 var p = range.commonAncestorContainer;
13664 while (p.nodeType == 3) { // text node
13675 * Range intersection.. the hard stuff...
13679 * [ -- selected range --- ]
13683 * if end is before start or hits it. fail.
13684 * if start is after end or hits it fail.
13686 * if either hits (but other is outside. - then it's not
13692 // @see http://www.thismuchiknow.co.uk/?p=64.
13693 rangeIntersectsNode : function(range, node)
13695 var nodeRange = node.ownerDocument.createRange();
13697 nodeRange.selectNode(node);
13699 nodeRange.selectNodeContents(node);
13702 var rangeStartRange = range.cloneRange();
13703 rangeStartRange.collapse(true);
13705 var rangeEndRange = range.cloneRange();
13706 rangeEndRange.collapse(false);
13708 var nodeStartRange = nodeRange.cloneRange();
13709 nodeStartRange.collapse(true);
13711 var nodeEndRange = nodeRange.cloneRange();
13712 nodeEndRange.collapse(false);
13714 return rangeStartRange.compareBoundaryPoints(
13715 Range.START_TO_START, nodeEndRange) == -1 &&
13716 rangeEndRange.compareBoundaryPoints(
13717 Range.START_TO_START, nodeStartRange) == 1;
13721 rangeCompareNode : function(range, node)
13723 var nodeRange = node.ownerDocument.createRange();
13725 nodeRange.selectNode(node);
13727 nodeRange.selectNodeContents(node);
13731 range.collapse(true);
13733 nodeRange.collapse(true);
13735 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13736 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13738 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13740 var nodeIsBefore = ss == 1;
13741 var nodeIsAfter = ee == -1;
13743 if (nodeIsBefore && nodeIsAfter)
13745 if (!nodeIsBefore && nodeIsAfter)
13746 return 1; //right trailed.
13748 if (nodeIsBefore && !nodeIsAfter)
13749 return 2; // left trailed.
13754 // private? - in a new class?
13755 cleanUpPaste : function()
13757 // cleans up the whole document..
13758 Roo.log('cleanuppaste');
13760 this.cleanUpChildren(this.doc.body);
13761 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13762 if (clean != this.doc.body.innerHTML) {
13763 this.doc.body.innerHTML = clean;
13768 cleanWordChars : function(input) {// change the chars to hex code
13769 var he = Roo.HtmlEditorCore;
13771 var output = input;
13772 Roo.each(he.swapCodes, function(sw) {
13773 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13775 output = output.replace(swapper, sw[1]);
13782 cleanUpChildren : function (n)
13784 if (!n.childNodes.length) {
13787 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13788 this.cleanUpChild(n.childNodes[i]);
13795 cleanUpChild : function (node)
13798 //console.log(node);
13799 if (node.nodeName == "#text") {
13800 // clean up silly Windows -- stuff?
13803 if (node.nodeName == "#comment") {
13804 node.parentNode.removeChild(node);
13805 // clean up silly Windows -- stuff?
13809 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13811 node.parentNode.removeChild(node);
13816 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13818 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13819 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13821 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13822 // remove_keep_children = true;
13825 if (remove_keep_children) {
13826 this.cleanUpChildren(node);
13827 // inserts everything just before this node...
13828 while (node.childNodes.length) {
13829 var cn = node.childNodes[0];
13830 node.removeChild(cn);
13831 node.parentNode.insertBefore(cn, node);
13833 node.parentNode.removeChild(node);
13837 if (!node.attributes || !node.attributes.length) {
13838 this.cleanUpChildren(node);
13842 function cleanAttr(n,v)
13845 if (v.match(/^\./) || v.match(/^\//)) {
13848 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13851 if (v.match(/^#/)) {
13854 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13855 node.removeAttribute(n);
13859 function cleanStyle(n,v)
13861 if (v.match(/expression/)) { //XSS?? should we even bother..
13862 node.removeAttribute(n);
13865 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13866 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13869 var parts = v.split(/;/);
13872 Roo.each(parts, function(p) {
13873 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13877 var l = p.split(':').shift().replace(/\s+/g,'');
13878 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13880 if ( cblack.indexOf(l) > -1) {
13881 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13882 //node.removeAttribute(n);
13886 // only allow 'c whitelisted system attributes'
13887 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13888 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13889 //node.removeAttribute(n);
13899 if (clean.length) {
13900 node.setAttribute(n, clean.join(';'));
13902 node.removeAttribute(n);
13908 for (var i = node.attributes.length-1; i > -1 ; i--) {
13909 var a = node.attributes[i];
13912 if (a.name.toLowerCase().substr(0,2)=='on') {
13913 node.removeAttribute(a.name);
13916 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13917 node.removeAttribute(a.name);
13920 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13921 cleanAttr(a.name,a.value); // fixme..
13924 if (a.name == 'style') {
13925 cleanStyle(a.name,a.value);
13928 /// clean up MS crap..
13929 // tecnically this should be a list of valid class'es..
13932 if (a.name == 'class') {
13933 if (a.value.match(/^Mso/)) {
13934 node.className = '';
13937 if (a.value.match(/body/)) {
13938 node.className = '';
13949 this.cleanUpChildren(node);
13955 // hide stuff that is not compatible
13969 * @event specialkey
13973 * @cfg {String} fieldClass @hide
13976 * @cfg {String} focusClass @hide
13979 * @cfg {String} autoCreate @hide
13982 * @cfg {String} inputType @hide
13985 * @cfg {String} invalidClass @hide
13988 * @cfg {String} invalidText @hide
13991 * @cfg {String} msgFx @hide
13994 * @cfg {String} validateOnBlur @hide
13998 Roo.HtmlEditorCore.white = [
13999 'area', 'br', 'img', 'input', 'hr', 'wbr',
14001 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14002 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14003 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14004 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14005 'table', 'ul', 'xmp',
14007 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14010 'dir', 'menu', 'ol', 'ul', 'dl',
14016 Roo.HtmlEditorCore.black = [
14017 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14019 'base', 'basefont', 'bgsound', 'blink', 'body',
14020 'frame', 'frameset', 'head', 'html', 'ilayer',
14021 'iframe', 'layer', 'link', 'meta', 'object',
14022 'script', 'style' ,'title', 'xml' // clean later..
14024 Roo.HtmlEditorCore.clean = [
14025 'script', 'style', 'title', 'xml'
14027 Roo.HtmlEditorCore.remove = [
14032 Roo.HtmlEditorCore.ablack = [
14036 Roo.HtmlEditorCore.aclean = [
14037 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14041 Roo.HtmlEditorCore.pwhite= [
14042 'http', 'https', 'mailto'
14045 // white listed style attributes.
14046 Roo.HtmlEditorCore.cwhite= [
14047 // 'text-align', /// default is to allow most things..
14053 // black listed style attributes.
14054 Roo.HtmlEditorCore.cblack= [
14055 // 'font-size' -- this can be set by the project
14059 Roo.HtmlEditorCore.swapCodes =[
14078 * @class Roo.bootstrap.HtmlEditor
14079 * @extends Roo.bootstrap.TextArea
14080 * Bootstrap HtmlEditor class
14083 * Create a new HtmlEditor
14084 * @param {Object} config The config object
14087 Roo.bootstrap.HtmlEditor = function(config){
14088 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14089 if (!this.toolbars) {
14090 this.toolbars = [];
14092 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14095 * @event initialize
14096 * Fires when the editor is fully initialized (including the iframe)
14097 * @param {HtmlEditor} this
14102 * Fires when the editor is first receives the focus. Any insertion must wait
14103 * until after this event.
14104 * @param {HtmlEditor} this
14108 * @event beforesync
14109 * Fires before the textarea is updated with content from the editor iframe. Return false
14110 * to cancel the sync.
14111 * @param {HtmlEditor} this
14112 * @param {String} html
14116 * @event beforepush
14117 * Fires before the iframe editor is updated with content from the textarea. Return false
14118 * to cancel the push.
14119 * @param {HtmlEditor} this
14120 * @param {String} html
14125 * Fires when the textarea is updated with content from the editor iframe.
14126 * @param {HtmlEditor} this
14127 * @param {String} html
14132 * Fires when the iframe editor is updated with content from the textarea.
14133 * @param {HtmlEditor} this
14134 * @param {String} html
14138 * @event editmodechange
14139 * Fires when the editor switches edit modes
14140 * @param {HtmlEditor} this
14141 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14143 editmodechange: true,
14145 * @event editorevent
14146 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14147 * @param {HtmlEditor} this
14151 * @event firstfocus
14152 * Fires when on first focus - needed by toolbars..
14153 * @param {HtmlEditor} this
14158 * Auto save the htmlEditor value as a file into Events
14159 * @param {HtmlEditor} this
14163 * @event savedpreview
14164 * preview the saved version of htmlEditor
14165 * @param {HtmlEditor} this
14172 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14176 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14181 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14186 * @cfg {Number} height (in pixels)
14190 * @cfg {Number} width (in pixels)
14195 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14198 stylesheets: false,
14203 // private properties
14204 validationEvent : false,
14206 initialized : false,
14209 onFocus : Roo.emptyFn,
14211 hideMode:'offsets',
14214 tbContainer : false,
14216 toolbarContainer :function() {
14217 return this.wrap.select('.x-html-editor-tb',true).first();
14221 * Protected method that will not generally be called directly. It
14222 * is called when the editor creates its toolbar. Override this method if you need to
14223 * add custom toolbar buttons.
14224 * @param {HtmlEditor} editor
14226 createToolbar : function(){
14228 Roo.log("create toolbars");
14230 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14231 this.toolbars[0].render(this.toolbarContainer());
14235 // if (!editor.toolbars || !editor.toolbars.length) {
14236 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14239 // for (var i =0 ; i < editor.toolbars.length;i++) {
14240 // editor.toolbars[i] = Roo.factory(
14241 // typeof(editor.toolbars[i]) == 'string' ?
14242 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14243 // Roo.bootstrap.HtmlEditor);
14244 // editor.toolbars[i].init(editor);
14250 onRender : function(ct, position)
14252 // Roo.log("Call onRender: " + this.xtype);
14254 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14256 this.wrap = this.inputEl().wrap({
14257 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14260 this.editorcore.onRender(ct, position);
14262 if (this.resizable) {
14263 this.resizeEl = new Roo.Resizable(this.wrap, {
14267 minHeight : this.height,
14268 height: this.height,
14269 handles : this.resizable,
14272 resize : function(r, w, h) {
14273 _t.onResize(w,h); // -something
14279 this.createToolbar(this);
14282 if(!this.width && this.resizable){
14283 this.setSize(this.wrap.getSize());
14285 if (this.resizeEl) {
14286 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14287 // should trigger onReize..
14293 onResize : function(w, h)
14295 Roo.log('resize: ' +w + ',' + h );
14296 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14300 if(this.inputEl() ){
14301 if(typeof w == 'number'){
14302 var aw = w - this.wrap.getFrameWidth('lr');
14303 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14306 if(typeof h == 'number'){
14307 var tbh = -11; // fixme it needs to tool bar size!
14308 for (var i =0; i < this.toolbars.length;i++) {
14309 // fixme - ask toolbars for heights?
14310 tbh += this.toolbars[i].el.getHeight();
14311 //if (this.toolbars[i].footer) {
14312 // tbh += this.toolbars[i].footer.el.getHeight();
14320 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14321 ah -= 5; // knock a few pixes off for look..
14322 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14326 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14327 this.editorcore.onResize(ew,eh);
14332 * Toggles the editor between standard and source edit mode.
14333 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14335 toggleSourceEdit : function(sourceEditMode)
14337 this.editorcore.toggleSourceEdit(sourceEditMode);
14339 if(this.editorcore.sourceEditMode){
14340 Roo.log('editor - showing textarea');
14343 // Roo.log(this.syncValue());
14345 this.inputEl().removeClass('hide');
14346 this.inputEl().dom.removeAttribute('tabIndex');
14347 this.inputEl().focus();
14349 Roo.log('editor - hiding textarea');
14351 // Roo.log(this.pushValue());
14354 this.inputEl().addClass('hide');
14355 this.inputEl().dom.setAttribute('tabIndex', -1);
14356 //this.deferFocus();
14359 if(this.resizable){
14360 this.setSize(this.wrap.getSize());
14363 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14366 // private (for BoxComponent)
14367 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14369 // private (for BoxComponent)
14370 getResizeEl : function(){
14374 // private (for BoxComponent)
14375 getPositionEl : function(){
14380 initEvents : function(){
14381 this.originalValue = this.getValue();
14385 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14388 // markInvalid : Roo.emptyFn,
14390 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14393 // clearInvalid : Roo.emptyFn,
14395 setValue : function(v){
14396 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14397 this.editorcore.pushValue();
14402 deferFocus : function(){
14403 this.focus.defer(10, this);
14407 focus : function(){
14408 this.editorcore.focus();
14414 onDestroy : function(){
14420 for (var i =0; i < this.toolbars.length;i++) {
14421 // fixme - ask toolbars for heights?
14422 this.toolbars[i].onDestroy();
14425 this.wrap.dom.innerHTML = '';
14426 this.wrap.remove();
14431 onFirstFocus : function(){
14432 //Roo.log("onFirstFocus");
14433 this.editorcore.onFirstFocus();
14434 for (var i =0; i < this.toolbars.length;i++) {
14435 this.toolbars[i].onFirstFocus();
14441 syncValue : function()
14443 this.editorcore.syncValue();
14446 pushValue : function()
14448 this.editorcore.pushValue();
14452 // hide stuff that is not compatible
14466 * @event specialkey
14470 * @cfg {String} fieldClass @hide
14473 * @cfg {String} focusClass @hide
14476 * @cfg {String} autoCreate @hide
14479 * @cfg {String} inputType @hide
14482 * @cfg {String} invalidClass @hide
14485 * @cfg {String} invalidText @hide
14488 * @cfg {String} msgFx @hide
14491 * @cfg {String} validateOnBlur @hide
14502 * @class Roo.bootstrap.HtmlEditorToolbar1
14507 new Roo.bootstrap.HtmlEditor({
14510 new Roo.bootstrap.HtmlEditorToolbar1({
14511 disable : { fonts: 1 , format: 1, ..., ... , ...],
14517 * @cfg {Object} disable List of elements to disable..
14518 * @cfg {Array} btns List of additional buttons.
14522 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14525 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14528 Roo.apply(this, config);
14530 // default disabled, based on 'good practice'..
14531 this.disable = this.disable || {};
14532 Roo.applyIf(this.disable, {
14535 specialElements : true
14537 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14539 this.editor = config.editor;
14540 this.editorcore = config.editor.editorcore;
14542 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14544 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14545 // dont call parent... till later.
14547 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14553 editorcore : false,
14558 "h1","h2","h3","h4","h5","h6",
14560 "abbr", "acronym", "address", "cite", "samp", "var",
14564 onRender : function(ct, position)
14566 // Roo.log("Call onRender: " + this.xtype);
14568 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14570 this.el.dom.style.marginBottom = '0';
14572 var editorcore = this.editorcore;
14573 var editor= this.editor;
14576 var btn = function(id,cmd , toggle, handler){
14578 var event = toggle ? 'toggle' : 'click';
14583 xns: Roo.bootstrap,
14586 enableToggle:toggle !== false,
14588 pressed : toggle ? false : null,
14591 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14592 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14601 xns: Roo.bootstrap,
14602 glyphicon : 'font',
14606 xns: Roo.bootstrap,
14610 Roo.each(this.formats, function(f) {
14611 style.menu.items.push({
14613 xns: Roo.bootstrap,
14614 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14619 editorcore.insertTag(this.tagname);
14626 children.push(style);
14629 btn('bold',false,true);
14630 btn('italic',false,true);
14631 btn('align-left', 'justifyleft',true);
14632 btn('align-center', 'justifycenter',true);
14633 btn('align-right' , 'justifyright',true);
14634 btn('link', false, false, function(btn) {
14635 //Roo.log("create link?");
14636 var url = prompt(this.createLinkText, this.defaultLinkValue);
14637 if(url && url != 'http:/'+'/'){
14638 this.editorcore.relayCmd('createlink', url);
14641 btn('list','insertunorderedlist',true);
14642 btn('pencil', false,true, function(btn){
14645 this.toggleSourceEdit(btn.pressed);
14651 xns: Roo.bootstrap,
14656 xns: Roo.bootstrap,
14661 cog.menu.items.push({
14663 xns: Roo.bootstrap,
14664 html : Clean styles,
14669 editorcore.insertTag(this.tagname);
14678 this.xtype = 'Navbar';
14680 for(var i=0;i< children.length;i++) {
14682 this.buttons.add(this.addxtypeChild(children[i]));
14686 editor.on('editorevent', this.updateToolbar, this);
14688 onBtnClick : function(id)
14690 this.editorcore.relayCmd(id);
14691 this.editorcore.focus();
14695 * Protected method that will not generally be called directly. It triggers
14696 * a toolbar update by reading the markup state of the current selection in the editor.
14698 updateToolbar: function(){
14700 if(!this.editorcore.activated){
14701 this.editor.onFirstFocus(); // is this neeed?
14705 var btns = this.buttons;
14706 var doc = this.editorcore.doc;
14707 btns.get('bold').setActive(doc.queryCommandState('bold'));
14708 btns.get('italic').setActive(doc.queryCommandState('italic'));
14709 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14711 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14712 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14713 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14715 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14716 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14719 var ans = this.editorcore.getAllAncestors();
14720 if (this.formatCombo) {
14723 var store = this.formatCombo.store;
14724 this.formatCombo.setValue("");
14725 for (var i =0; i < ans.length;i++) {
14726 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14728 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14736 // hides menus... - so this cant be on a menu...
14737 Roo.bootstrap.MenuMgr.hideAll();
14739 Roo.bootstrap.MenuMgr.hideAll();
14740 //this.editorsyncValue();
14742 onFirstFocus: function() {
14743 this.buttons.each(function(item){
14747 toggleSourceEdit : function(sourceEditMode){
14750 if(sourceEditMode){
14751 Roo.log("disabling buttons");
14752 this.buttons.each( function(item){
14753 if(item.cmd != 'pencil'){
14759 Roo.log("enabling buttons");
14760 if(this.editorcore.initialized){
14761 this.buttons.each( function(item){
14767 Roo.log("calling toggole on editor");
14768 // tell the editor that it's been pressed..
14769 this.editor.toggleSourceEdit(sourceEditMode);
14779 * @class Roo.bootstrap.Table.AbstractSelectionModel
14780 * @extends Roo.util.Observable
14781 * Abstract base class for grid SelectionModels. It provides the interface that should be
14782 * implemented by descendant classes. This class should not be directly instantiated.
14785 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14786 this.locked = false;
14787 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14791 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14792 /** @ignore Called by the grid automatically. Do not call directly. */
14793 init : function(grid){
14799 * Locks the selections.
14802 this.locked = true;
14806 * Unlocks the selections.
14808 unlock : function(){
14809 this.locked = false;
14813 * Returns true if the selections are locked.
14814 * @return {Boolean}
14816 isLocked : function(){
14817 return this.locked;
14821 * @class Roo.bootstrap.Table.ColumnModel
14822 * @extends Roo.util.Observable
14823 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14824 * the columns in the table.
14827 * @param {Object} config An Array of column config objects. See this class's
14828 * config objects for details.
14830 Roo.bootstrap.Table.ColumnModel = function(config){
14832 * The config passed into the constructor
14834 this.config = config;
14837 // if no id, create one
14838 // if the column does not have a dataIndex mapping,
14839 // map it to the order it is in the config
14840 for(var i = 0, len = config.length; i < len; i++){
14842 if(typeof c.dataIndex == "undefined"){
14845 if(typeof c.renderer == "string"){
14846 c.renderer = Roo.util.Format[c.renderer];
14848 if(typeof c.id == "undefined"){
14851 // if(c.editor && c.editor.xtype){
14852 // c.editor = Roo.factory(c.editor, Roo.grid);
14854 // if(c.editor && c.editor.isFormField){
14855 // c.editor = new Roo.grid.GridEditor(c.editor);
14858 this.lookup[c.id] = c;
14862 * The width of columns which have no width specified (defaults to 100)
14865 this.defaultWidth = 100;
14868 * Default sortable of columns which have no sortable specified (defaults to false)
14871 this.defaultSortable = false;
14875 * @event widthchange
14876 * Fires when the width of a column changes.
14877 * @param {ColumnModel} this
14878 * @param {Number} columnIndex The column index
14879 * @param {Number} newWidth The new width
14881 "widthchange": true,
14883 * @event headerchange
14884 * Fires when the text of a header changes.
14885 * @param {ColumnModel} this
14886 * @param {Number} columnIndex The column index
14887 * @param {Number} newText The new header text
14889 "headerchange": true,
14891 * @event hiddenchange
14892 * Fires when a column is hidden or "unhidden".
14893 * @param {ColumnModel} this
14894 * @param {Number} columnIndex The column index
14895 * @param {Boolean} hidden true if hidden, false otherwise
14897 "hiddenchange": true,
14899 * @event columnmoved
14900 * Fires when a column is moved.
14901 * @param {ColumnModel} this
14902 * @param {Number} oldIndex
14903 * @param {Number} newIndex
14905 "columnmoved" : true,
14907 * @event columlockchange
14908 * Fires when a column's locked state is changed
14909 * @param {ColumnModel} this
14910 * @param {Number} colIndex
14911 * @param {Boolean} locked true if locked
14913 "columnlockchange" : true
14915 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14917 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14919 * @cfg {String} header The header text to display in the Grid view.
14922 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14923 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14924 * specified, the column's index is used as an index into the Record's data Array.
14927 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14928 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14931 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14932 * Defaults to the value of the {@link #defaultSortable} property.
14933 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14936 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14939 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14942 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14945 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14948 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14949 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14950 * default renderer uses the raw data value.
14953 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14957 * Returns the id of the column at the specified index.
14958 * @param {Number} index The column index
14959 * @return {String} the id
14961 getColumnId : function(index){
14962 return this.config[index].id;
14966 * Returns the column for a specified id.
14967 * @param {String} id The column id
14968 * @return {Object} the column
14970 getColumnById : function(id){
14971 return this.lookup[id];
14976 * Returns the column for a specified dataIndex.
14977 * @param {String} dataIndex The column dataIndex
14978 * @return {Object|Boolean} the column or false if not found
14980 getColumnByDataIndex: function(dataIndex){
14981 var index = this.findColumnIndex(dataIndex);
14982 return index > -1 ? this.config[index] : false;
14986 * Returns the index for a specified column id.
14987 * @param {String} id The column id
14988 * @return {Number} the index, or -1 if not found
14990 getIndexById : function(id){
14991 for(var i = 0, len = this.config.length; i < len; i++){
14992 if(this.config[i].id == id){
15000 * Returns the index for a specified column dataIndex.
15001 * @param {String} dataIndex The column dataIndex
15002 * @return {Number} the index, or -1 if not found
15005 findColumnIndex : function(dataIndex){
15006 for(var i = 0, len = this.config.length; i < len; i++){
15007 if(this.config[i].dataIndex == dataIndex){
15015 moveColumn : function(oldIndex, newIndex){
15016 var c = this.config[oldIndex];
15017 this.config.splice(oldIndex, 1);
15018 this.config.splice(newIndex, 0, c);
15019 this.dataMap = null;
15020 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15023 isLocked : function(colIndex){
15024 return this.config[colIndex].locked === true;
15027 setLocked : function(colIndex, value, suppressEvent){
15028 if(this.isLocked(colIndex) == value){
15031 this.config[colIndex].locked = value;
15032 if(!suppressEvent){
15033 this.fireEvent("columnlockchange", this, colIndex, value);
15037 getTotalLockedWidth : function(){
15038 var totalWidth = 0;
15039 for(var i = 0; i < this.config.length; i++){
15040 if(this.isLocked(i) && !this.isHidden(i)){
15041 this.totalWidth += this.getColumnWidth(i);
15047 getLockedCount : function(){
15048 for(var i = 0, len = this.config.length; i < len; i++){
15049 if(!this.isLocked(i)){
15056 * Returns the number of columns.
15059 getColumnCount : function(visibleOnly){
15060 if(visibleOnly === true){
15062 for(var i = 0, len = this.config.length; i < len; i++){
15063 if(!this.isHidden(i)){
15069 return this.config.length;
15073 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15074 * @param {Function} fn
15075 * @param {Object} scope (optional)
15076 * @return {Array} result
15078 getColumnsBy : function(fn, scope){
15080 for(var i = 0, len = this.config.length; i < len; i++){
15081 var c = this.config[i];
15082 if(fn.call(scope||this, c, i) === true){
15090 * Returns true if the specified column is sortable.
15091 * @param {Number} col The column index
15092 * @return {Boolean}
15094 isSortable : function(col){
15095 if(typeof this.config[col].sortable == "undefined"){
15096 return this.defaultSortable;
15098 return this.config[col].sortable;
15102 * Returns the rendering (formatting) function defined for the column.
15103 * @param {Number} col The column index.
15104 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15106 getRenderer : function(col){
15107 if(!this.config[col].renderer){
15108 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15110 return this.config[col].renderer;
15114 * Sets the rendering (formatting) function for a column.
15115 * @param {Number} col The column index
15116 * @param {Function} fn The function to use to process the cell's raw data
15117 * to return HTML markup for the grid view. The render function is called with
15118 * the following parameters:<ul>
15119 * <li>Data value.</li>
15120 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15121 * <li>css A CSS style string to apply to the table cell.</li>
15122 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15123 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15124 * <li>Row index</li>
15125 * <li>Column index</li>
15126 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15128 setRenderer : function(col, fn){
15129 this.config[col].renderer = fn;
15133 * Returns the width for the specified column.
15134 * @param {Number} col The column index
15137 getColumnWidth : function(col){
15138 return this.config[col].width * 1 || this.defaultWidth;
15142 * Sets the width for a column.
15143 * @param {Number} col The column index
15144 * @param {Number} width The new width
15146 setColumnWidth : function(col, width, suppressEvent){
15147 this.config[col].width = width;
15148 this.totalWidth = null;
15149 if(!suppressEvent){
15150 this.fireEvent("widthchange", this, col, width);
15155 * Returns the total width of all columns.
15156 * @param {Boolean} includeHidden True to include hidden column widths
15159 getTotalWidth : function(includeHidden){
15160 if(!this.totalWidth){
15161 this.totalWidth = 0;
15162 for(var i = 0, len = this.config.length; i < len; i++){
15163 if(includeHidden || !this.isHidden(i)){
15164 this.totalWidth += this.getColumnWidth(i);
15168 return this.totalWidth;
15172 * Returns the header for the specified column.
15173 * @param {Number} col The column index
15176 getColumnHeader : function(col){
15177 return this.config[col].header;
15181 * Sets the header for a column.
15182 * @param {Number} col The column index
15183 * @param {String} header The new header
15185 setColumnHeader : function(col, header){
15186 this.config[col].header = header;
15187 this.fireEvent("headerchange", this, col, header);
15191 * Returns the tooltip for the specified column.
15192 * @param {Number} col The column index
15195 getColumnTooltip : function(col){
15196 return this.config[col].tooltip;
15199 * Sets the tooltip for a column.
15200 * @param {Number} col The column index
15201 * @param {String} tooltip The new tooltip
15203 setColumnTooltip : function(col, tooltip){
15204 this.config[col].tooltip = tooltip;
15208 * Returns the dataIndex for the specified column.
15209 * @param {Number} col The column index
15212 getDataIndex : function(col){
15213 return this.config[col].dataIndex;
15217 * Sets the dataIndex for a column.
15218 * @param {Number} col The column index
15219 * @param {Number} dataIndex The new dataIndex
15221 setDataIndex : function(col, dataIndex){
15222 this.config[col].dataIndex = dataIndex;
15228 * Returns true if the cell is editable.
15229 * @param {Number} colIndex The column index
15230 * @param {Number} rowIndex The row index
15231 * @return {Boolean}
15233 isCellEditable : function(colIndex, rowIndex){
15234 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15238 * Returns the editor defined for the cell/column.
15239 * return false or null to disable editing.
15240 * @param {Number} colIndex The column index
15241 * @param {Number} rowIndex The row index
15244 getCellEditor : function(colIndex, rowIndex){
15245 return this.config[colIndex].editor;
15249 * Sets if a column is editable.
15250 * @param {Number} col The column index
15251 * @param {Boolean} editable True if the column is editable
15253 setEditable : function(col, editable){
15254 this.config[col].editable = editable;
15259 * Returns true if the column is hidden.
15260 * @param {Number} colIndex The column index
15261 * @return {Boolean}
15263 isHidden : function(colIndex){
15264 return this.config[colIndex].hidden;
15269 * Returns true if the column width cannot be changed
15271 isFixed : function(colIndex){
15272 return this.config[colIndex].fixed;
15276 * Returns true if the column can be resized
15277 * @return {Boolean}
15279 isResizable : function(colIndex){
15280 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15283 * Sets if a column is hidden.
15284 * @param {Number} colIndex The column index
15285 * @param {Boolean} hidden True if the column is hidden
15287 setHidden : function(colIndex, hidden){
15288 this.config[colIndex].hidden = hidden;
15289 this.totalWidth = null;
15290 this.fireEvent("hiddenchange", this, colIndex, hidden);
15294 * Sets the editor for a column.
15295 * @param {Number} col The column index
15296 * @param {Object} editor The editor object
15298 setEditor : function(col, editor){
15299 this.config[col].editor = editor;
15303 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15304 if(typeof value == "string" && value.length < 1){
15310 // Alias for backwards compatibility
15311 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15314 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15315 * @class Roo.bootstrap.Table.RowSelectionModel
15316 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15317 * It supports multiple selections and keyboard selection/navigation.
15319 * @param {Object} config
15322 Roo.bootstrap.Table.RowSelectionModel = function(config){
15323 Roo.apply(this, config);
15324 this.selections = new Roo.util.MixedCollection(false, function(o){
15329 this.lastActive = false;
15333 * @event selectionchange
15334 * Fires when the selection changes
15335 * @param {SelectionModel} this
15337 "selectionchange" : true,
15339 * @event afterselectionchange
15340 * Fires after the selection changes (eg. by key press or clicking)
15341 * @param {SelectionModel} this
15343 "afterselectionchange" : true,
15345 * @event beforerowselect
15346 * Fires when a row is selected being selected, return false to cancel.
15347 * @param {SelectionModel} this
15348 * @param {Number} rowIndex The selected index
15349 * @param {Boolean} keepExisting False if other selections will be cleared
15351 "beforerowselect" : true,
15354 * Fires when a row is selected.
15355 * @param {SelectionModel} this
15356 * @param {Number} rowIndex The selected index
15357 * @param {Roo.data.Record} r The record
15359 "rowselect" : true,
15361 * @event rowdeselect
15362 * Fires when a row is deselected.
15363 * @param {SelectionModel} this
15364 * @param {Number} rowIndex The selected index
15366 "rowdeselect" : true
15368 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15369 this.locked = false;
15372 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15374 * @cfg {Boolean} singleSelect
15375 * True to allow selection of only one row at a time (defaults to false)
15377 singleSelect : false,
15380 initEvents : function(){
15382 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15383 this.grid.on("mousedown", this.handleMouseDown, this);
15384 }else{ // allow click to work like normal
15385 this.grid.on("rowclick", this.handleDragableRowClick, this);
15388 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15389 "up" : function(e){
15391 this.selectPrevious(e.shiftKey);
15392 }else if(this.last !== false && this.lastActive !== false){
15393 var last = this.last;
15394 this.selectRange(this.last, this.lastActive-1);
15395 this.grid.getView().focusRow(this.lastActive);
15396 if(last !== false){
15400 this.selectFirstRow();
15402 this.fireEvent("afterselectionchange", this);
15404 "down" : function(e){
15406 this.selectNext(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);
15422 var view = this.grid.view;
15423 view.on("refresh", this.onRefresh, this);
15424 view.on("rowupdated", this.onRowUpdated, this);
15425 view.on("rowremoved", this.onRemove, this);
15429 onRefresh : function(){
15430 var ds = this.grid.dataSource, i, v = this.grid.view;
15431 var s = this.selections;
15432 s.each(function(r){
15433 if((i = ds.indexOfId(r.id)) != -1){
15442 onRemove : function(v, index, r){
15443 this.selections.remove(r);
15447 onRowUpdated : function(v, index, r){
15448 if(this.isSelected(r)){
15449 v.onRowSelect(index);
15455 * @param {Array} records The records to select
15456 * @param {Boolean} keepExisting (optional) True to keep existing selections
15458 selectRecords : function(records, keepExisting){
15460 this.clearSelections();
15462 var ds = this.grid.dataSource;
15463 for(var i = 0, len = records.length; i < len; i++){
15464 this.selectRow(ds.indexOf(records[i]), true);
15469 * Gets the number of selected rows.
15472 getCount : function(){
15473 return this.selections.length;
15477 * Selects the first row in the grid.
15479 selectFirstRow : function(){
15484 * Select the last row.
15485 * @param {Boolean} keepExisting (optional) True to keep existing selections
15487 selectLastRow : function(keepExisting){
15488 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15492 * Selects the row immediately following the last selected row.
15493 * @param {Boolean} keepExisting (optional) True to keep existing selections
15495 selectNext : function(keepExisting){
15496 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15497 this.selectRow(this.last+1, keepExisting);
15498 this.grid.getView().focusRow(this.last);
15503 * Selects the row that precedes the last selected row.
15504 * @param {Boolean} keepExisting (optional) True to keep existing selections
15506 selectPrevious : function(keepExisting){
15508 this.selectRow(this.last-1, keepExisting);
15509 this.grid.getView().focusRow(this.last);
15514 * Returns the selected records
15515 * @return {Array} Array of selected records
15517 getSelections : function(){
15518 return [].concat(this.selections.items);
15522 * Returns the first selected record.
15525 getSelected : function(){
15526 return this.selections.itemAt(0);
15531 * Clears all selections.
15533 clearSelections : function(fast){
15534 if(this.locked) return;
15536 var ds = this.grid.dataSource;
15537 var s = this.selections;
15538 s.each(function(r){
15539 this.deselectRow(ds.indexOfId(r.id));
15543 this.selections.clear();
15550 * Selects all rows.
15552 selectAll : function(){
15553 if(this.locked) return;
15554 this.selections.clear();
15555 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15556 this.selectRow(i, true);
15561 * Returns True if there is a selection.
15562 * @return {Boolean}
15564 hasSelection : function(){
15565 return this.selections.length > 0;
15569 * Returns True if the specified row is selected.
15570 * @param {Number/Record} record The record or index of the record to check
15571 * @return {Boolean}
15573 isSelected : function(index){
15574 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15575 return (r && this.selections.key(r.id) ? true : false);
15579 * Returns True if the specified record id is selected.
15580 * @param {String} id The id of record to check
15581 * @return {Boolean}
15583 isIdSelected : function(id){
15584 return (this.selections.key(id) ? true : false);
15588 handleMouseDown : function(e, t){
15589 var view = this.grid.getView(), rowIndex;
15590 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15593 if(e.shiftKey && this.last !== false){
15594 var last = this.last;
15595 this.selectRange(last, rowIndex, e.ctrlKey);
15596 this.last = last; // reset the last
15597 view.focusRow(rowIndex);
15599 var isSelected = this.isSelected(rowIndex);
15600 if(e.button !== 0 && isSelected){
15601 view.focusRow(rowIndex);
15602 }else if(e.ctrlKey && isSelected){
15603 this.deselectRow(rowIndex);
15604 }else if(!isSelected){
15605 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15606 view.focusRow(rowIndex);
15609 this.fireEvent("afterselectionchange", this);
15612 handleDragableRowClick : function(grid, rowIndex, e)
15614 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15615 this.selectRow(rowIndex, false);
15616 grid.view.focusRow(rowIndex);
15617 this.fireEvent("afterselectionchange", this);
15622 * Selects multiple rows.
15623 * @param {Array} rows Array of the indexes of the row to select
15624 * @param {Boolean} keepExisting (optional) True to keep existing selections
15626 selectRows : function(rows, keepExisting){
15628 this.clearSelections();
15630 for(var i = 0, len = rows.length; i < len; i++){
15631 this.selectRow(rows[i], true);
15636 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15637 * @param {Number} startRow The index of the first row in the range
15638 * @param {Number} endRow The index of the last row in the range
15639 * @param {Boolean} keepExisting (optional) True to retain existing selections
15641 selectRange : function(startRow, endRow, keepExisting){
15642 if(this.locked) return;
15644 this.clearSelections();
15646 if(startRow <= endRow){
15647 for(var i = startRow; i <= endRow; i++){
15648 this.selectRow(i, true);
15651 for(var i = startRow; i >= endRow; i--){
15652 this.selectRow(i, true);
15658 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15659 * @param {Number} startRow The index of the first row in the range
15660 * @param {Number} endRow The index of the last row in the range
15662 deselectRange : function(startRow, endRow, preventViewNotify){
15663 if(this.locked) return;
15664 for(var i = startRow; i <= endRow; i++){
15665 this.deselectRow(i, preventViewNotify);
15671 * @param {Number} row The index of the row to select
15672 * @param {Boolean} keepExisting (optional) True to keep existing selections
15674 selectRow : function(index, keepExisting, preventViewNotify){
15675 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15676 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15677 if(!keepExisting || this.singleSelect){
15678 this.clearSelections();
15680 var r = this.grid.dataSource.getAt(index);
15681 this.selections.add(r);
15682 this.last = this.lastActive = index;
15683 if(!preventViewNotify){
15684 this.grid.getView().onRowSelect(index);
15686 this.fireEvent("rowselect", this, index, r);
15687 this.fireEvent("selectionchange", this);
15693 * @param {Number} row The index of the row to deselect
15695 deselectRow : function(index, preventViewNotify){
15696 if(this.locked) return;
15697 if(this.last == index){
15700 if(this.lastActive == index){
15701 this.lastActive = false;
15703 var r = this.grid.dataSource.getAt(index);
15704 this.selections.remove(r);
15705 if(!preventViewNotify){
15706 this.grid.getView().onRowDeselect(index);
15708 this.fireEvent("rowdeselect", this, index);
15709 this.fireEvent("selectionchange", this);
15713 restoreLast : function(){
15715 this.last = this._last;
15720 acceptsNav : function(row, col, cm){
15721 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15725 onEditorKey : function(field, e){
15726 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15731 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15733 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15735 }else if(k == e.ENTER && !e.ctrlKey){
15739 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15741 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15743 }else if(k == e.ESC){
15747 g.startEditing(newCell[0], newCell[1]);
15758 * @class Roo.bootstrap.MessageBar
15759 * @extends Roo.bootstrap.Component
15760 * Bootstrap MessageBar class
15761 * @cfg {String} html contents of the MessageBar
15762 * @cfg {String} weight (info | success | warning | danger) default info
15763 * @cfg {String} beforeClass insert the bar before the given class
15764 * @cfg {Boolean} closable (true | false) default false
15765 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15768 * Create a new Element
15769 * @param {Object} config The config object
15772 Roo.bootstrap.MessageBar = function(config){
15773 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15776 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15782 beforeClass: 'bootstrap-sticky-wrap',
15784 getAutoCreate : function(){
15788 cls: 'alert alert-dismissable alert-' + this.weight,
15793 html: this.html || ''
15799 cfg.cls += ' alert-messages-fixed';
15813 onRender : function(ct, position)
15815 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15818 var cfg = Roo.apply({}, this.getAutoCreate());
15822 cfg.cls += ' ' + this.cls;
15825 cfg.style = this.style;
15827 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15829 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15832 this.el.select('>button.close').on('click', this.hide, this);
15838 if (!this.rendered) {
15844 this.fireEvent('show', this);
15850 if (!this.rendered) {
15856 this.fireEvent('hide', this);
15859 update : function()
15861 // var e = this.el.dom.firstChild;
15863 // if(this.closable){
15864 // e = e.nextSibling;
15867 // e.data = this.html || '';
15869 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';