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])));
284 Roo.bootstrap.Body = function(config){
285 Roo.bootstrap.Body.superclass.constructor.call(this, config);
286 this.el = Roo.get(document.body);
289 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
294 onRender : function(ct, position){
297 //this.el.addClass([this.fieldClass, this.cls]);
315 * @class Roo.bootstrap.ButtonGroup
316 * @extends Roo.bootstrap.Component
317 * Bootstrap ButtonGroup class
318 * @cfg {String} size lg | sm | xs (default empty normal)
319 * @cfg {String} align vertical | justified (default none)
320 * @cfg {String} direction up | down (default down)
321 * @cfg {Boolean} toolbar false | true
322 * @cfg {Boolean} btn true | false
327 * @param {Object} config The config object
330 Roo.bootstrap.ButtonGroup = function(config){
331 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
334 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
342 getAutoCreate : function(){
348 cfg.html = this.html || cfg.html;
359 if (['vertical','justified'].indexOf(this.align)!==-1) {
360 cfg.cls = 'btn-group-' + this.align;
362 if (this.align == 'justified') {
363 console.log(this.items);
367 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
368 cfg.cls += ' btn-group-' + this.size;
371 if (this.direction == 'up') {
372 cfg.cls += ' dropup' ;
388 * @class Roo.bootstrap.Button
389 * @extends Roo.bootstrap.Component
390 * Bootstrap Button class
391 * @cfg {String} html The button content
392 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
393 * @cfg {String} size empty | lg | sm | xs
394 * @cfg {String} tag empty | a | input | submit
395 * @cfg {String} href empty or href
396 * @cfg {Boolean} disabled false | true
397 * @cfg {Boolean} isClose false | true
398 * @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
399 * @cfg {String} badge text for badge
400 * @cfg {String} theme default (or empty) | glow
401 * @cfg {Boolean} inverse false | true
402 * @cfg {Boolean} toggle false | true
403 * @cfg {String} ontext text for on toggle state
404 * @cfg {String} offtext text for off toggle state
405 * @cfg {Boolean} defaulton true | false
406 * @cfg {Boolean} preventDefault (true | false) default true
407 * @cfg {Boolean} removeClass true | false remove the standard class..
408 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
411 * Create a new button
412 * @param {Object} config The config object
416 Roo.bootstrap.Button = function(config){
417 Roo.bootstrap.Button.superclass.constructor.call(this, config);
422 * When a butotn is pressed
423 * @param {Roo.EventObject} e
428 * After the button has been toggles
429 * @param {Roo.EventObject} e
430 * @param {boolean} pressed (also available as button.pressed)
436 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
454 preventDefault: true,
463 getAutoCreate : function(){
471 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
472 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
477 cfg.html = this.html || cfg.html;
479 if (this.toggle == true) {
482 cls: 'slider-frame roo-button',
487 'data-off-text':'OFF',
488 cls: 'slider-button',
494 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
495 cfg.cls += ' '+this.weight;
504 cfg["aria-hidden"] = true;
506 cfg.html = "×";
512 if (this.theme==='default') {
513 cfg.cls = 'btn roo-button';
515 //if (this.parentType != 'Navbar') {
516 this.weight = this.weight.length ? this.weight : 'default';
518 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
520 cfg.cls += ' btn-' + this.weight;
522 } else if (this.theme==='glow') {
525 cfg.cls = 'btn-glow roo-button';
527 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
529 cfg.cls += ' ' + this.weight;
535 this.cls += ' inverse';
540 cfg.cls += ' active';
544 cfg.disabled = 'disabled';
548 Roo.log('changing to ul' );
550 this.glyphicon = 'caret';
553 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
555 //gsRoo.log(this.parentType);
556 if (this.parentType === 'Navbar' && !this.parent().bar) {
557 Roo.log('changing to li?');
566 href : this.href || '#'
569 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
570 cfg.cls += ' dropdown';
577 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
579 if (this.glyphicon) {
580 cfg.html = ' ' + cfg.html;
585 cls: 'glyphicon glyphicon-' + this.glyphicon
595 // cfg.cls='btn roo-button';
599 var value = cfg.html;
604 cls: 'glyphicon glyphicon-' + this.glyphicon,
623 cfg.cls += ' dropdown';
624 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
627 if (cfg.tag !== 'a' && this.href !== '') {
628 throw "Tag must be a to set href.";
629 } else if (this.href.length > 0) {
630 cfg.href = this.href;
633 if(this.removeClass){
638 cfg.target = this.target;
643 initEvents: function() {
644 // Roo.log('init events?');
645 // Roo.log(this.el.dom);
646 if (this.el.hasClass('roo-button')) {
647 this.el.on('click', this.onClick, this);
649 this.el.select('.roo-button').on('click', this.onClick, this);
655 onClick : function(e)
661 Roo.log('button on click ');
662 if(this.preventDefault){
665 if (this.pressed === true || this.pressed === false) {
666 this.pressed = !this.pressed;
667 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
668 this.fireEvent('toggle', this, e, this.pressed);
672 this.fireEvent('click', this, e);
676 * Enables this button
680 this.disabled = false;
681 this.el.removeClass('disabled');
685 * Disable this button
689 this.disabled = true;
690 this.el.addClass('disabled');
693 * sets the active state on/off,
694 * @param {Boolean} state (optional) Force a particular state
696 setActive : function(v) {
698 this.el[v ? 'addClass' : 'removeClass']('active');
701 * toggles the current active state
703 toggleActive : function()
705 var active = this.el.hasClass('active');
706 this.setActive(!active);
723 * @class Roo.bootstrap.Column
724 * @extends Roo.bootstrap.Component
725 * Bootstrap Column class
726 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
727 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
728 * @cfg {Number} md colspan out of 12 for computer-sized screens
729 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
730 * @cfg {String} html content of column.
733 * Create a new Column
734 * @param {Object} config The config object
737 Roo.bootstrap.Column = function(config){
738 Roo.bootstrap.Column.superclass.constructor.call(this, config);
741 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
750 getAutoCreate : function(){
751 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
759 ['xs','sm','md','lg'].map(function(size){
760 if (settings[size]) {
761 cfg.cls += ' col-' + size + '-' + settings[size];
764 if (this.html.length) {
765 cfg.html = this.html;
784 * @class Roo.bootstrap.Container
785 * @extends Roo.bootstrap.Component
786 * Bootstrap Container class
787 * @cfg {Boolean} jumbotron is it a jumbotron element
788 * @cfg {String} html content of element
789 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
790 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
791 * @cfg {String} header content of header (for panel)
792 * @cfg {String} footer content of footer (for panel)
793 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
796 * Create a new Container
797 * @param {Object} config The config object
800 Roo.bootstrap.Container = function(config){
801 Roo.bootstrap.Container.superclass.constructor.call(this, config);
804 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
814 getChildContainer : function() {
820 if (this.panel.length) {
821 return this.el.select('.panel-body',true).first();
828 getAutoCreate : function(){
834 if (this.jumbotron) {
835 cfg.cls = 'jumbotron';
838 cfg.cls = this.cls + '';
841 if (this.sticky.length) {
843 var bd = Roo.get(document.body);
844 if (!bd.hasClass('bootstrap-sticky')) {
845 bd.addClass('bootstrap-sticky');
846 Roo.select('html',true).setStyle('height', '100%');
849 cfg.cls += 'bootstrap-sticky-' + this.sticky;
853 if (this.well.length) {
857 cfg.cls +=' well well-' +this.well;
867 if (this.panel.length) {
868 cfg.cls += ' panel panel-' + this.panel;
870 if (this.header.length) {
873 cls : 'panel-heading',
889 if (this.footer.length) {
891 cls : 'panel-footer',
899 body.html = this.html || cfg.html;
901 if (!cfg.cls.length) {
902 cfg.cls = 'container';
919 * @class Roo.bootstrap.Img
920 * @extends Roo.bootstrap.Component
921 * Bootstrap Img class
922 * @cfg {Boolean} imgResponsive false | true
923 * @cfg {String} border rounded | circle | thumbnail
924 * @cfg {String} src image source
925 * @cfg {String} alt image alternative text
926 * @cfg {String} href a tag href
927 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
931 * @param {Object} config The config object
934 Roo.bootstrap.Img = function(config){
935 Roo.bootstrap.Img.superclass.constructor.call(this, config);
941 * The img click event for the img.
942 * @param {Roo.EventObject} e
948 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
956 getAutoCreate : function(){
960 cls: 'img-responsive',
964 cfg.html = this.html || cfg.html;
966 cfg.src = this.src || cfg.src;
968 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
969 cfg.cls += ' img-' + this.border;
986 a.target = this.target;
992 return (this.href) ? a : cfg;
995 initEvents: function() {
998 this.el.on('click', this.onClick, this);
1002 onClick : function(e)
1004 Roo.log('img onclick');
1005 this.fireEvent('click', this, e);
1018 * @class Roo.bootstrap.Header
1019 * @extends Roo.bootstrap.Component
1020 * Bootstrap Header class
1021 * @cfg {String} html content of header
1022 * @cfg {Number} level (1|2|3|4|5|6) default 1
1025 * Create a new Header
1026 * @param {Object} config The config object
1030 Roo.bootstrap.Header = function(config){
1031 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1034 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1042 getAutoCreate : function(){
1045 tag: 'h' + (1 *this.level),
1046 html: this.html || 'fill in html'
1058 * Ext JS Library 1.1.1
1059 * Copyright(c) 2006-2007, Ext JS, LLC.
1061 * Originally Released Under LGPL - original licence link has changed is not relivant.
1064 * <script type="text/javascript">
1068 * @class Roo.bootstrap.MenuMgr
1069 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1072 Roo.bootstrap.MenuMgr = function(){
1073 var menus, active, groups = {}, attached = false, lastShow = new Date();
1075 // private - called when first menu is created
1078 active = new Roo.util.MixedCollection();
1079 Roo.get(document).addKeyListener(27, function(){
1080 if(active.length > 0){
1088 if(active && active.length > 0){
1089 var c = active.clone();
1099 if(active.length < 1){
1100 Roo.get(document).un("mouseup", onMouseDown);
1108 var last = active.last();
1109 lastShow = new Date();
1112 Roo.get(document).on("mouseup", onMouseDown);
1117 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1118 m.parentMenu.activeChild = m;
1119 }else if(last && last.isVisible()){
1120 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1125 function onBeforeHide(m){
1127 m.activeChild.hide();
1129 if(m.autoHideTimer){
1130 clearTimeout(m.autoHideTimer);
1131 delete m.autoHideTimer;
1136 function onBeforeShow(m){
1137 var pm = m.parentMenu;
1138 if(!pm && !m.allowOtherMenus){
1140 }else if(pm && pm.activeChild && active != m){
1141 pm.activeChild.hide();
1146 function onMouseDown(e){
1147 Roo.log("on MouseDown");
1148 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1156 function onBeforeCheck(mi, state){
1158 var g = groups[mi.group];
1159 for(var i = 0, l = g.length; i < l; i++){
1161 g[i].setChecked(false);
1170 * Hides all menus that are currently visible
1172 hideAll : function(){
1177 register : function(menu){
1181 menus[menu.id] = menu;
1182 menu.on("beforehide", onBeforeHide);
1183 menu.on("hide", onHide);
1184 menu.on("beforeshow", onBeforeShow);
1185 menu.on("show", onShow);
1187 if(g && menu.events["checkchange"]){
1191 groups[g].push(menu);
1192 menu.on("checkchange", onCheck);
1197 * Returns a {@link Roo.menu.Menu} object
1198 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1199 * be used to generate and return a new Menu instance.
1201 get : function(menu){
1202 if(typeof menu == "string"){ // menu id
1204 }else if(menu.events){ // menu instance
1207 /*else if(typeof menu.length == 'number'){ // array of menu items?
1208 return new Roo.bootstrap.Menu({items:menu});
1209 }else{ // otherwise, must be a config
1210 return new Roo.bootstrap.Menu(menu);
1217 unregister : function(menu){
1218 delete menus[menu.id];
1219 menu.un("beforehide", onBeforeHide);
1220 menu.un("hide", onHide);
1221 menu.un("beforeshow", onBeforeShow);
1222 menu.un("show", onShow);
1224 if(g && menu.events["checkchange"]){
1225 groups[g].remove(menu);
1226 menu.un("checkchange", onCheck);
1231 registerCheckable : function(menuItem){
1232 var g = menuItem.group;
1237 groups[g].push(menuItem);
1238 menuItem.on("beforecheckchange", onBeforeCheck);
1243 unregisterCheckable : function(menuItem){
1244 var g = menuItem.group;
1246 groups[g].remove(menuItem);
1247 menuItem.un("beforecheckchange", onBeforeCheck);
1259 * @class Roo.bootstrap.Menu
1260 * @extends Roo.bootstrap.Component
1261 * Bootstrap Menu class - container for MenuItems
1262 * @cfg {String} type type of menu
1266 * @param {Object} config The config object
1270 Roo.bootstrap.Menu = function(config){
1271 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1272 if (this.registerMenu) {
1273 Roo.bootstrap.MenuMgr.register(this);
1278 * Fires before this menu is displayed
1279 * @param {Roo.menu.Menu} this
1284 * Fires before this menu is hidden
1285 * @param {Roo.menu.Menu} this
1290 * Fires after this menu is displayed
1291 * @param {Roo.menu.Menu} this
1296 * Fires after this menu is hidden
1297 * @param {Roo.menu.Menu} this
1302 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1303 * @param {Roo.menu.Menu} this
1304 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1305 * @param {Roo.EventObject} e
1310 * Fires when the mouse is hovering over this menu
1311 * @param {Roo.menu.Menu} this
1312 * @param {Roo.EventObject} e
1313 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1318 * Fires when the mouse exits this menu
1319 * @param {Roo.menu.Menu} this
1320 * @param {Roo.EventObject} e
1321 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1326 * Fires when a menu item contained in this menu is clicked
1327 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1328 * @param {Roo.EventObject} e
1332 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1335 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1339 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1342 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1344 registerMenu : true,
1346 menuItems :false, // stores the menu items..
1352 getChildContainer : function() {
1356 getAutoCreate : function(){
1358 //if (['right'].indexOf(this.align)!==-1) {
1359 // cfg.cn[1].cls += ' pull-right'
1363 cls : 'dropdown-menu' ,
1364 style : 'z-index:1000'
1368 if (this.type === 'submenu') {
1369 cfg.cls = 'submenu active'
1374 initEvents : function() {
1376 // Roo.log("ADD event");
1377 // Roo.log(this.triggerEl.dom);
1378 this.triggerEl.on('click', this.onTriggerPress, this);
1379 this.triggerEl.addClass('dropdown-toggle');
1380 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1382 this.el.on("mouseover", this.onMouseOver, this);
1383 this.el.on("mouseout", this.onMouseOut, this);
1387 findTargetItem : function(e){
1388 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1392 //Roo.log(t); Roo.log(t.id);
1394 //Roo.log(this.menuitems);
1395 return this.menuitems.get(t.id);
1397 //return this.items.get(t.menuItemId);
1402 onClick : function(e){
1403 Roo.log("menu.onClick");
1404 var t = this.findTargetItem(e);
1410 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1411 if(t == this.activeItem && t.shouldDeactivate(e)){
1412 this.activeItem.deactivate();
1413 delete this.activeItem;
1417 this.setActiveItem(t, true);
1424 Roo.log('pass click event');
1428 this.fireEvent("click", this, t, e);
1432 onMouseOver : function(e){
1433 var t = this.findTargetItem(e);
1436 // if(t.canActivate && !t.disabled){
1437 // this.setActiveItem(t, true);
1441 this.fireEvent("mouseover", this, e, t);
1443 isVisible : function(){
1444 return !this.hidden;
1446 onMouseOut : function(e){
1447 var t = this.findTargetItem(e);
1450 // if(t == this.activeItem && t.shouldDeactivate(e)){
1451 // this.activeItem.deactivate();
1452 // delete this.activeItem;
1455 this.fireEvent("mouseout", this, e, t);
1460 * Displays this menu relative to another element
1461 * @param {String/HTMLElement/Roo.Element} element The element to align to
1462 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1463 * the element (defaults to this.defaultAlign)
1464 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1466 show : function(el, pos, parentMenu){
1467 this.parentMenu = parentMenu;
1471 this.fireEvent("beforeshow", this);
1472 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1475 * Displays this menu at a specific xy position
1476 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1477 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1479 showAt : function(xy, parentMenu, /* private: */_e){
1480 this.parentMenu = parentMenu;
1485 this.fireEvent("beforeshow", this);
1487 //xy = this.el.adjustForConstraints(xy);
1489 //this.el.setXY(xy);
1491 this.hideMenuItems();
1492 this.hidden = false;
1493 this.triggerEl.addClass('open');
1495 this.fireEvent("show", this);
1501 this.doFocus.defer(50, this);
1505 doFocus : function(){
1507 this.focusEl.focus();
1512 * Hides this menu and optionally all parent menus
1513 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1515 hide : function(deep){
1517 this.hideMenuItems();
1518 if(this.el && this.isVisible()){
1519 this.fireEvent("beforehide", this);
1520 if(this.activeItem){
1521 this.activeItem.deactivate();
1522 this.activeItem = null;
1524 this.triggerEl.removeClass('open');;
1526 this.fireEvent("hide", this);
1528 if(deep === true && this.parentMenu){
1529 this.parentMenu.hide(true);
1533 onTriggerPress : function(e)
1536 Roo.log('trigger press');
1537 //Roo.log(e.getTarget());
1538 // Roo.log(this.triggerEl.dom);
1539 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1542 if (this.isVisible()) {
1546 this.show(this.triggerEl, false, false);
1555 hideMenuItems : function()
1557 //$(backdrop).remove()
1558 Roo.select('.open',true).each(function(aa) {
1560 aa.removeClass('open');
1561 //var parent = getParent($(this))
1562 //var relatedTarget = { relatedTarget: this }
1564 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1565 //if (e.isDefaultPrevented()) return
1566 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1569 addxtypeChild : function (tree, cntr) {
1570 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1572 this.menuitems.add(comp);
1593 * @class Roo.bootstrap.MenuItem
1594 * @extends Roo.bootstrap.Component
1595 * Bootstrap MenuItem class
1596 * @cfg {String} html the menu label
1597 * @cfg {String} href the link
1598 * @cfg {Boolean} preventDefault (true | false) default true
1602 * Create a new MenuItem
1603 * @param {Object} config The config object
1607 Roo.bootstrap.MenuItem = function(config){
1608 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1613 * The raw click event for the entire grid.
1614 * @param {Roo.EventObject} e
1620 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1624 preventDefault: true,
1626 getAutoCreate : function(){
1629 cls: 'dropdown-menu-item',
1639 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1640 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1644 initEvents: function() {
1646 //this.el.select('a').on('click', this.onClick, this);
1649 onClick : function(e)
1651 Roo.log('item on click ');
1652 //if(this.preventDefault){
1653 // e.preventDefault();
1655 //this.parent().hideMenuItems();
1657 this.fireEvent('click', this, e);
1676 * @class Roo.bootstrap.MenuSeparator
1677 * @extends Roo.bootstrap.Component
1678 * Bootstrap MenuSeparator class
1681 * Create a new MenuItem
1682 * @param {Object} config The config object
1686 Roo.bootstrap.MenuSeparator = function(config){
1687 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1690 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1692 getAutoCreate : function(){
1707 <div class="modal fade">
1708 <div class="modal-dialog">
1709 <div class="modal-content">
1710 <div class="modal-header">
1711 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1712 <h4 class="modal-title">Modal title</h4>
1714 <div class="modal-body">
1715 <p>One fine body…</p>
1717 <div class="modal-footer">
1718 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1719 <button type="button" class="btn btn-primary">Save changes</button>
1721 </div><!-- /.modal-content -->
1722 </div><!-- /.modal-dialog -->
1723 </div><!-- /.modal -->
1733 * @class Roo.bootstrap.Modal
1734 * @extends Roo.bootstrap.Component
1735 * Bootstrap Modal class
1736 * @cfg {String} title Title of dialog
1737 * @cfg {Array} buttons Array of buttons or standard button set..
1740 * Create a new Modal Dialog
1741 * @param {Object} config The config object
1744 Roo.bootstrap.Modal = function(config){
1745 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1750 * The raw btnclick event for the button
1751 * @param {Roo.EventObject} e
1757 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1759 title : 'test dialog',
1763 onRender : function(ct, position)
1765 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1768 var cfg = Roo.apply({}, this.getAutoCreate());
1771 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1773 //if (!cfg.name.length) {
1777 cfg.cls += ' ' + this.cls;
1780 cfg.style = this.style;
1782 this.el = Roo.get(document.body).createChild(cfg, position);
1784 //var type = this.el.dom.type;
1786 if(this.tabIndex !== undefined){
1787 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1792 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1793 this.maskEl.enableDisplayMode("block");
1795 //this.el.addClass("x-dlg-modal");
1798 Roo.each(this.buttons, function(bb) {
1799 b = Roo.apply({}, bb);
1800 b.xns = b.xns || Roo.bootstrap;
1801 b.xtype = b.xtype || 'Button';
1802 if (typeof(b.listeners) == 'undefined') {
1803 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1806 var btn = Roo.factory(b);
1808 btn.onRender(this.el.select('.modal-footer').first());
1812 // render the children.
1815 if(typeof(this.items) != 'undefined'){
1816 var items = this.items;
1819 for(var i =0;i < items.length;i++) {
1820 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1824 this.items = nitems;
1826 //this.el.addClass([this.fieldClass, this.cls]);
1829 getAutoCreate : function(){
1834 html : this.html || ''
1842 cls: "modal-dialog",
1845 cls : "modal-content",
1848 cls : 'modal-header',
1857 cls : 'modal-title',
1865 cls : 'modal-footer'
1881 getChildContainer : function() {
1883 return this.el.select('.modal-body',true).first();
1886 getButtonContainer : function() {
1887 return this.el.select('.modal-footer',true).first();
1890 initEvents : function()
1892 this.el.select('.modal-header .close').on('click', this.hide, this);
1894 // this.addxtype(this);
1898 if (!this.rendered) {
1902 this.el.addClass('on');
1903 this.el.removeClass('fade');
1904 this.el.setStyle('display', 'block');
1905 Roo.get(document.body).addClass("x-body-masked");
1906 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1908 this.el.setStyle('zIndex', '10001');
1909 this.fireEvent('show', this);
1915 Roo.log('Modal hide?!');
1917 Roo.get(document.body).removeClass("x-body-masked");
1918 this.el.removeClass('on');
1919 this.el.addClass('fade');
1920 this.el.setStyle('display', 'none');
1921 this.fireEvent('hide', this);
1923 onButtonClick: function(btn,e)
1926 this.fireEvent('btnclick', btn.name, e);
1931 Roo.apply(Roo.bootstrap.Modal, {
1933 * Button config that displays a single OK button
1942 * Button config that displays Yes and No buttons
1958 * Button config that displays OK and Cancel buttons
1973 * Button config that displays Yes, No and Cancel buttons
2000 * @class Roo.bootstrap.Navbar
2001 * @extends Roo.bootstrap.Component
2002 * Bootstrap Navbar class
2003 * @cfg {Boolean} sidebar has side bar
2004 * @cfg {Boolean} bar is a bar?
2005 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2006 * @cfg {String} brand what is brand
2007 * @cfg {Boolean} inverse is inverted color
2008 * @cfg {String} type (nav | pills | tabs)
2009 * @cfg {Boolean} arrangement stacked | justified
2010 * @cfg {String} align (left | right) alignment
2011 * @cfg {String} brand_href href of the brand
2012 * @cfg {Boolean} main (true|false) main nav bar? default false
2016 * Create a new Navbar
2017 * @param {Object} config The config object
2021 Roo.bootstrap.Navbar = function(config){
2022 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2025 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2039 getAutoCreate : function(){
2044 if (this.sidebar === true) {
2052 if (this.bar === true) {
2060 cls: 'navbar-header',
2065 cls: 'navbar-toggle',
2066 'data-toggle': 'collapse',
2071 html: 'Toggle navigation'
2091 cls: 'collapse navbar-collapse'
2096 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2098 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2099 cfg.cls += ' navbar-' + this.position;
2100 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2103 if (this.brand !== '') {
2106 href: this.brand_href ? this.brand_href : '#',
2107 cls: 'navbar-brand',
2115 cfg.cls += ' main-nav';
2121 } else if (this.bar === false) {
2124 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2134 if (['tabs','pills'].indexOf(this.type)!==-1) {
2135 cfg.cn[0].cls += ' nav-' + this.type
2137 if (this.type!=='nav') {
2138 Roo.log('nav type must be nav/tabs/pills')
2140 cfg.cn[0].cls += ' navbar-nav'
2143 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2144 cfg.cn[0].cls += ' nav-' + this.arrangement;
2147 if (this.align === 'right') {
2148 cfg.cn[0].cls += ' navbar-right';
2151 cfg.cls += ' navbar-inverse';
2159 initEvents :function ()
2161 //Roo.log(this.el.select('.navbar-toggle',true));
2162 this.el.select('.navbar-toggle',true).on('click', function() {
2163 // Roo.log('click');
2164 this.el.select('.navbar-collapse',true).toggleClass('in');
2169 getChildContainer : function()
2171 if (this.bar === true) {
2172 return this.el.select('.collapse',true).first();
2190 * @class Roo.bootstrap.NavGroup
2191 * @extends Roo.bootstrap.Component
2192 * Bootstrap NavGroup class
2193 * @cfg {String} align left | right
2194 * @cfg {Boolean} inverse false | true
2195 * @cfg {String} type (nav|pills|tab) default nav
2198 * Create a new nav group
2199 * @param {Object} config The config object
2202 Roo.bootstrap.NavGroup = function(config){
2203 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2206 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2213 getAutoCreate : function(){
2214 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2221 if (['tabs','pills'].indexOf(this.type)!==-1) {
2222 cfg.cls += ' nav-' + this.type
2224 if (this.type!=='nav') {
2225 Roo.log('nav type must be nav/tabs/pills')
2227 cfg.cls += ' navbar-nav'
2230 if (this.parent().sidebar === true) {
2233 cls: 'dashboard-menu'
2239 if (this.form === true) {
2245 if (this.align === 'right') {
2246 cfg.cls += ' navbar-right';
2248 cfg.cls += ' navbar-left';
2252 if (this.align === 'right') {
2253 cfg.cls += ' navbar-right';
2257 cfg.cls += ' navbar-inverse';
2277 * @class Roo.bootstrap.Navbar.Item
2278 * @extends Roo.bootstrap.Component
2279 * Bootstrap Navbar.Button class
2280 * @cfg {String} href link to
2281 * @cfg {String} html content of button
2282 * @cfg {String} badge text inside badge
2283 * @cfg {String} glyphicon name of glyphicon
2284 * @cfg {String} icon name of font awesome icon
2285 * @cfg {Boolena} active Is item active
2286 * @cfg {Boolean} preventDefault (true | false) default false
2289 * Create a new Navbar Button
2290 * @param {Object} config The config object
2292 Roo.bootstrap.Navbar.Item = function(config){
2293 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2298 * The raw click event for the entire grid.
2299 * @param {Roo.EventObject} e
2305 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
2314 preventDefault : false,
2316 getAutoCreate : function(){
2318 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2320 if (this.parent().parent().sidebar === true) {
2333 cfg.cn[0].html = this.html;
2337 this.cls += ' active';
2341 cfg.cn[0].cls += ' dropdown-toggle';
2342 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2346 cfg.cn[0].tag = 'a',
2347 cfg.cn[0].href = this.href;
2350 if (this.glyphicon) {
2351 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2355 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2367 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2377 if (this.glyphicon) {
2378 if(cfg.html){cfg.html = ' ' + this.html};
2382 cls: 'glyphicon glyphicon-' + this.glyphicon
2387 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2392 cfg.cn[0].html += " <span class='caret'></span>";
2393 //}else if (!this.href) {
2394 // cfg.cn[0].tag='p';
2395 // cfg.cn[0].cls='navbar-text';
2398 cfg.cn[0].href=this.href||'#';
2399 cfg.cn[0].html=this.html;
2402 if (this.badge !== '') {
2405 cfg.cn[0].html + ' ',
2416 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2421 initEvents: function() {
2422 // Roo.log('init events?');
2423 // Roo.log(this.el.dom);
2424 this.el.select('a',true).on('click', this.onClick, this);
2427 onClick : function(e)
2429 if(this.preventDefault){
2433 if(this.fireEvent('click', this, e) === false){
2437 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2438 this.onTabsClick(e);
2442 onTabsClick : function(e)
2444 Roo.each(this.parent().el.select('.active',true).elements, function(v){
2445 v.removeClass('active');
2448 this.el.addClass('active');
2450 if(this.href && this.href.substring(0,1) == '#'){
2451 var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2453 Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2454 v.removeClass('active');
2457 tab.addClass('active');
2472 * @class Roo.bootstrap.Row
2473 * @extends Roo.bootstrap.Component
2474 * Bootstrap Row class (contains columns...)
2478 * @param {Object} config The config object
2481 Roo.bootstrap.Row = function(config){
2482 Roo.bootstrap.Row.superclass.constructor.call(this, config);
2485 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
2487 getAutoCreate : function(){
2506 * @class Roo.bootstrap.Element
2507 * @extends Roo.bootstrap.Component
2508 * Bootstrap Element class
2509 * @cfg {String} html contents of the element
2510 * @cfg {String} tag tag of the element
2511 * @cfg {String} cls class of the element
2514 * Create a new Element
2515 * @param {Object} config The config object
2518 Roo.bootstrap.Element = function(config){
2519 Roo.bootstrap.Element.superclass.constructor.call(this, config);
2522 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
2529 getAutoCreate : function(){
2554 * @class Roo.bootstrap.Pagination
2555 * @extends Roo.bootstrap.Component
2556 * Bootstrap Pagination class
2557 * @cfg {String} size xs | sm | md | lg
2558 * @cfg {Boolean} inverse false | true
2561 * Create a new Pagination
2562 * @param {Object} config The config object
2565 Roo.bootstrap.Pagination = function(config){
2566 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2569 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
2575 getAutoCreate : function(){
2581 cfg.cls += ' inverse';
2587 cfg.cls += " " + this.cls;
2605 * @class Roo.bootstrap.PaginationItem
2606 * @extends Roo.bootstrap.Component
2607 * Bootstrap PaginationItem class
2608 * @cfg {String} html text
2609 * @cfg {String} href the link
2610 * @cfg {Boolean} preventDefault (true | false) default true
2611 * @cfg {Boolean} active (true | false) default false
2615 * Create a new PaginationItem
2616 * @param {Object} config The config object
2620 Roo.bootstrap.PaginationItem = function(config){
2621 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2626 * The raw click event for the entire grid.
2627 * @param {Roo.EventObject} e
2633 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
2637 preventDefault: true,
2641 getAutoCreate : function(){
2647 href : this.href ? this.href : '#',
2648 html : this.html ? this.html : ''
2658 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2664 initEvents: function() {
2666 this.el.on('click', this.onClick, this);
2669 onClick : function(e)
2671 Roo.log('PaginationItem on click ');
2672 if(this.preventDefault){
2676 this.fireEvent('click', this, e);
2692 * @class Roo.bootstrap.Slider
2693 * @extends Roo.bootstrap.Component
2694 * Bootstrap Slider class
2697 * Create a new Slider
2698 * @param {Object} config The config object
2701 Roo.bootstrap.Slider = function(config){
2702 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2705 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
2707 getAutoCreate : function(){
2711 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2715 cls: 'ui-slider-handle ui-state-default ui-corner-all'
2733 * @class Roo.bootstrap.Table
2734 * @extends Roo.bootstrap.Component
2735 * Bootstrap Table class
2736 * @cfg {String} cls table class
2737 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2738 * @cfg {String} bgcolor Specifies the background color for a table
2739 * @cfg {Number} border Specifies whether the table cells should have borders or not
2740 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2741 * @cfg {Number} cellspacing Specifies the space between cells
2742 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2743 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2744 * @cfg {String} sortable Specifies that the table should be sortable
2745 * @cfg {String} summary Specifies a summary of the content of a table
2746 * @cfg {Number} width Specifies the width of a table
2748 * @cfg {boolean} striped Should the rows be alternative striped
2749 * @cfg {boolean} bordered Add borders to the table
2750 * @cfg {boolean} hover Add hover highlighting
2751 * @cfg {boolean} condensed Format condensed
2752 * @cfg {boolean} responsive Format condensed
2758 * Create a new Table
2759 * @param {Object} config The config object
2762 Roo.bootstrap.Table = function(config){
2763 Roo.bootstrap.Table.superclass.constructor.call(this, config);
2766 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2767 this.sm = this.selModel;
2768 this.sm.xmodule = this.xmodule || false;
2770 if (this.cm && typeof(this.cm.config) == 'undefined') {
2771 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2772 this.cm = this.colModel;
2773 this.cm.xmodule = this.xmodule || false;
2776 this.store= Roo.factory(this.store, Roo.data);
2777 this.ds = this.store;
2778 this.ds.xmodule = this.xmodule || false;
2783 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
2805 getAutoCreate : function(){
2806 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2815 cfg.cls += ' table-striped';
2818 cfg.cls += ' table-hover';
2820 if (this.bordered) {
2821 cfg.cls += ' table-bordered';
2823 if (this.condensed) {
2824 cfg.cls += ' table-condensed';
2826 if (this.responsive) {
2827 cfg.cls += ' table-responsive';
2834 cfg.cls+= ' ' +this.cls;
2837 // this lot should be simplifed...
2840 cfg.align=this.align;
2843 cfg.bgcolor=this.bgcolor;
2846 cfg.border=this.border;
2848 if (this.cellpadding) {
2849 cfg.cellpadding=this.cellpadding;
2851 if (this.cellspacing) {
2852 cfg.cellspacing=this.cellspacing;
2855 cfg.frame=this.frame;
2858 cfg.rules=this.rules;
2860 if (this.sortable) {
2861 cfg.sortable=this.sortable;
2864 cfg.summary=this.summary;
2867 cfg.width=this.width;
2870 if(this.store || this.cm){
2871 cfg.cn.push(this.renderHeader());
2872 cfg.cn.push(this.renderBody());
2873 cfg.cn.push(this.renderFooter());
2875 cfg.cls+= ' TableGrid';
2881 // initTableGrid : function()
2890 // var cm = this.cm;
2892 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2895 // html: cm.getColumnHeader(i)
2899 // cfg.push(header);
2906 initEvents : function()
2908 if(!this.store || !this.cm){
2912 Roo.log('initEvents with ds!!!!');
2914 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2915 // this.maskEl.enableDisplayMode("block");
2916 // this.maskEl.show();
2918 this.store.on('load', this.onLoad, this);
2919 this.store.on('beforeload', this.onBeforeLoad, this);
2927 renderHeader : function()
2936 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2939 html: cm.getColumnHeader(i)
2946 renderBody : function()
2956 renderFooter : function()
2968 Roo.log('ds onload');
2972 var tbody = this.el.select('tbody', true).first();
2976 if(this.store.getCount() > 0){
2977 this.store.data.each(function(d){
2983 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2984 var renderer = cm.getRenderer(i);
2988 if(typeof(renderer) !== 'undefined'){
2989 value = renderer(d.data[cm.getDataIndex(i)], false, d);
2992 if(typeof(value) === 'object'){
3002 html: (typeof(value) === 'object') ? '' : value
3007 tbody.createChild(row);
3013 Roo.each(renders, function(r){
3014 r.cfg.render(Roo.get(r.id));
3018 // if(this.loadMask){
3019 // this.maskEl.hide();
3023 onBeforeLoad : function()
3025 Roo.log('ds onBeforeLoad');
3029 // if(this.loadMask){
3030 // this.maskEl.show();
3036 this.el.select('tbody', true).first().dom.innerHTML = '';
3039 getSelectionModel : function(){
3041 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3043 return this.selModel;
3058 * @class Roo.bootstrap.TableCell
3059 * @extends Roo.bootstrap.Component
3060 * Bootstrap TableCell class
3061 * @cfg {String} html cell contain text
3062 * @cfg {String} cls cell class
3063 * @cfg {String} tag cell tag (td|th) default td
3064 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3065 * @cfg {String} align Aligns the content in a cell
3066 * @cfg {String} axis Categorizes cells
3067 * @cfg {String} bgcolor Specifies the background color of a cell
3068 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3069 * @cfg {Number} colspan Specifies the number of columns a cell should span
3070 * @cfg {String} headers Specifies one or more header cells a cell is related to
3071 * @cfg {Number} height Sets the height of a cell
3072 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3073 * @cfg {Number} rowspan Sets the number of rows a cell should span
3074 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3075 * @cfg {String} valign Vertical aligns the content in a cell
3076 * @cfg {Number} width Specifies the width of a cell
3079 * Create a new TableCell
3080 * @param {Object} config The config object
3083 Roo.bootstrap.TableCell = function(config){
3084 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3087 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3107 getAutoCreate : function(){
3108 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3128 cfg.align=this.align
3134 cfg.bgcolor=this.bgcolor
3137 cfg.charoff=this.charoff
3140 cfg.colspan=this.colspan
3143 cfg.headers=this.headers
3146 cfg.height=this.height
3149 cfg.nowrap=this.nowrap
3152 cfg.rowspan=this.rowspan
3155 cfg.scope=this.scope
3158 cfg.valign=this.valign
3161 cfg.width=this.width
3180 * @class Roo.bootstrap.TableRow
3181 * @extends Roo.bootstrap.Component
3182 * Bootstrap TableRow class
3183 * @cfg {String} cls row class
3184 * @cfg {String} align Aligns the content in a table row
3185 * @cfg {String} bgcolor Specifies a background color for a table row
3186 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3187 * @cfg {String} valign Vertical aligns the content in a table row
3190 * Create a new TableRow
3191 * @param {Object} config The config object
3194 Roo.bootstrap.TableRow = function(config){
3195 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3198 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3206 getAutoCreate : function(){
3207 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3217 cfg.align = this.align;
3220 cfg.bgcolor = this.bgcolor;
3223 cfg.charoff = this.charoff;
3226 cfg.valign = this.valign;
3244 * @class Roo.bootstrap.TableBody
3245 * @extends Roo.bootstrap.Component
3246 * Bootstrap TableBody class
3247 * @cfg {String} cls element class
3248 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3249 * @cfg {String} align Aligns the content inside the element
3250 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3251 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3254 * Create a new TableBody
3255 * @param {Object} config The config object
3258 Roo.bootstrap.TableBody = function(config){
3259 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3262 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3270 getAutoCreate : function(){
3271 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3285 cfg.align = this.align;
3288 cfg.charoff = this.charoff;
3291 cfg.valign = this.valign;
3298 // initEvents : function()
3305 // this.store = Roo.factory(this.store, Roo.data);
3306 // this.store.on('load', this.onLoad, this);
3308 // this.store.load();
3312 // onLoad: function ()
3314 // this.fireEvent('load', this);
3324 * Ext JS Library 1.1.1
3325 * Copyright(c) 2006-2007, Ext JS, LLC.
3327 * Originally Released Under LGPL - original licence link has changed is not relivant.
3330 * <script type="text/javascript">
3333 // as we use this in bootstrap.
3334 Roo.namespace('Roo.form');
3336 * @class Roo.form.Action
3337 * Internal Class used to handle form actions
3339 * @param {Roo.form.BasicForm} el The form element or its id
3340 * @param {Object} config Configuration options
3345 // define the action interface
3346 Roo.form.Action = function(form, options){
3348 this.options = options || {};
3351 * Client Validation Failed
3354 Roo.form.Action.CLIENT_INVALID = 'client';
3356 * Server Validation Failed
3359 Roo.form.Action.SERVER_INVALID = 'server';
3361 * Connect to Server Failed
3364 Roo.form.Action.CONNECT_FAILURE = 'connect';
3366 * Reading Data from Server Failed
3369 Roo.form.Action.LOAD_FAILURE = 'load';
3371 Roo.form.Action.prototype = {
3373 failureType : undefined,
3374 response : undefined,
3378 run : function(options){
3383 success : function(response){
3388 handleResponse : function(response){
3392 // default connection failure
3393 failure : function(response){
3395 this.response = response;
3396 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3397 this.form.afterAction(this, false);
3400 processResponse : function(response){
3401 this.response = response;
3402 if(!response.responseText){
3405 this.result = this.handleResponse(response);
3409 // utility functions used internally
3410 getUrl : function(appendParams){
3411 var url = this.options.url || this.form.url || this.form.el.dom.action;
3413 var p = this.getParams();
3415 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3421 getMethod : function(){
3422 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3425 getParams : function(){
3426 var bp = this.form.baseParams;
3427 var p = this.options.params;
3429 if(typeof p == "object"){
3430 p = Roo.urlEncode(Roo.applyIf(p, bp));
3431 }else if(typeof p == 'string' && bp){
3432 p += '&' + Roo.urlEncode(bp);
3435 p = Roo.urlEncode(bp);
3440 createCallback : function(){
3442 success: this.success,
3443 failure: this.failure,
3445 timeout: (this.form.timeout*1000),
3446 upload: this.form.fileUpload ? this.success : undefined
3451 Roo.form.Action.Submit = function(form, options){
3452 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3455 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3458 haveProgress : false,
3459 uploadComplete : false,
3461 // uploadProgress indicator.
3462 uploadProgress : function()
3464 if (!this.form.progressUrl) {
3468 if (!this.haveProgress) {
3469 Roo.MessageBox.progress("Uploading", "Uploading");
3471 if (this.uploadComplete) {
3472 Roo.MessageBox.hide();
3476 this.haveProgress = true;
3478 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3480 var c = new Roo.data.Connection();
3482 url : this.form.progressUrl,
3487 success : function(req){
3488 //console.log(data);
3492 rdata = Roo.decode(req.responseText)
3494 Roo.log("Invalid data from server..");
3498 if (!rdata || !rdata.success) {
3500 Roo.MessageBox.alert(Roo.encode(rdata));
3503 var data = rdata.data;
3505 if (this.uploadComplete) {
3506 Roo.MessageBox.hide();
3511 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3512 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3515 this.uploadProgress.defer(2000,this);
3518 failure: function(data) {
3519 Roo.log('progress url failed ');
3530 // run get Values on the form, so it syncs any secondary forms.
3531 this.form.getValues();
3533 var o = this.options;
3534 var method = this.getMethod();
3535 var isPost = method == 'POST';
3536 if(o.clientValidation === false || this.form.isValid()){
3538 if (this.form.progressUrl) {
3539 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3540 (new Date() * 1) + '' + Math.random());
3545 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3546 form:this.form.el.dom,
3547 url:this.getUrl(!isPost),
3549 params:isPost ? this.getParams() : null,
3550 isUpload: this.form.fileUpload
3553 this.uploadProgress();
3555 }else if (o.clientValidation !== false){ // client validation failed
3556 this.failureType = Roo.form.Action.CLIENT_INVALID;
3557 this.form.afterAction(this, false);
3561 success : function(response)
3563 this.uploadComplete= true;
3564 if (this.haveProgress) {
3565 Roo.MessageBox.hide();
3569 var result = this.processResponse(response);
3570 if(result === true || result.success){
3571 this.form.afterAction(this, true);
3575 this.form.markInvalid(result.errors);
3576 this.failureType = Roo.form.Action.SERVER_INVALID;
3578 this.form.afterAction(this, false);
3580 failure : function(response)
3582 this.uploadComplete= true;
3583 if (this.haveProgress) {
3584 Roo.MessageBox.hide();
3587 this.response = response;
3588 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3589 this.form.afterAction(this, false);
3592 handleResponse : function(response){
3593 if(this.form.errorReader){
3594 var rs = this.form.errorReader.read(response);
3597 for(var i = 0, len = rs.records.length; i < len; i++) {
3598 var r = rs.records[i];
3602 if(errors.length < 1){
3606 success : rs.success,
3612 ret = Roo.decode(response.responseText);
3616 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3626 Roo.form.Action.Load = function(form, options){
3627 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3628 this.reader = this.form.reader;
3631 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3636 Roo.Ajax.request(Roo.apply(
3637 this.createCallback(), {
3638 method:this.getMethod(),
3639 url:this.getUrl(false),
3640 params:this.getParams()
3644 success : function(response){
3646 var result = this.processResponse(response);
3647 if(result === true || !result.success || !result.data){
3648 this.failureType = Roo.form.Action.LOAD_FAILURE;
3649 this.form.afterAction(this, false);
3652 this.form.clearInvalid();
3653 this.form.setValues(result.data);
3654 this.form.afterAction(this, true);
3657 handleResponse : function(response){
3658 if(this.form.reader){
3659 var rs = this.form.reader.read(response);
3660 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3662 success : rs.success,
3666 return Roo.decode(response.responseText);
3670 Roo.form.Action.ACTION_TYPES = {
3671 'load' : Roo.form.Action.Load,
3672 'submit' : Roo.form.Action.Submit
3681 * @class Roo.bootstrap.Form
3682 * @extends Roo.bootstrap.Component
3683 * Bootstrap Form class
3684 * @cfg {String} method GET | POST (default POST)
3685 * @cfg {String} labelAlign top | left (default top)
3686 * @cfg {String} align left | right - for navbars
3691 * @param {Object} config The config object
3695 Roo.bootstrap.Form = function(config){
3696 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3699 * @event clientvalidation
3700 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3701 * @param {Form} this
3702 * @param {Boolean} valid true if the form has passed client-side validation
3704 clientvalidation: true,
3706 * @event beforeaction
3707 * Fires before any action is performed. Return false to cancel the action.
3708 * @param {Form} this
3709 * @param {Action} action The action to be performed
3713 * @event actionfailed
3714 * Fires when an action fails.
3715 * @param {Form} this
3716 * @param {Action} action The action that failed
3718 actionfailed : true,
3720 * @event actioncomplete
3721 * Fires when an action is completed.
3722 * @param {Form} this
3723 * @param {Action} action The action that completed
3725 actioncomplete : true
3730 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3733 * @cfg {String} method
3734 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3739 * The URL to use for form actions if one isn't supplied in the action options.
3742 * @cfg {Boolean} fileUpload
3743 * Set to true if this form is a file upload.
3747 * @cfg {Object} baseParams
3748 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3752 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3756 * @cfg {Sting} align (left|right) for navbar forms
3761 activeAction : null,
3764 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3765 * element by passing it or its id or mask the form itself by passing in true.
3768 waitMsgTarget : false,
3773 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3774 * element by passing it or its id or mask the form itself by passing in true.
3778 getAutoCreate : function(){
3782 method : this.method || 'POST',
3783 id : this.id || Roo.id(),
3786 if (this.parent().xtype.match(/^Nav/)) {
3787 cfg.cls = 'navbar-form navbar-' + this.align;
3791 if (this.labelAlign == 'left' ) {
3792 cfg.cls += ' form-horizontal';
3798 initEvents : function()
3800 this.el.on('submit', this.onSubmit, this);
3805 onSubmit : function(e){
3810 * Returns true if client-side validation on the form is successful.
3813 isValid : function(){
3814 var items = this.getItems();
3816 items.each(function(f){
3825 * Returns true if any fields in this form have changed since their original load.
3828 isDirty : function(){
3830 var items = this.getItems();
3831 items.each(function(f){
3841 * Performs a predefined action (submit or load) or custom actions you define on this form.
3842 * @param {String} actionName The name of the action type
3843 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3844 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3845 * accept other config options):
3847 Property Type Description
3848 ---------------- --------------- ----------------------------------------------------------------------------------
3849 url String The url for the action (defaults to the form's url)
3850 method String The form method to use (defaults to the form's method, or POST if not defined)
3851 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3852 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3853 validate the form on the client (defaults to false)
3855 * @return {BasicForm} this
3857 doAction : function(action, options){
3858 if(typeof action == 'string'){
3859 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3861 if(this.fireEvent('beforeaction', this, action) !== false){
3862 this.beforeAction(action);
3863 action.run.defer(100, action);
3869 beforeAction : function(action){
3870 var o = action.options;
3872 // not really supported yet.. ??
3874 //if(this.waitMsgTarget === true){
3875 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3876 //}else if(this.waitMsgTarget){
3877 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3878 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3880 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3886 afterAction : function(action, success){
3887 this.activeAction = null;
3888 var o = action.options;
3890 //if(this.waitMsgTarget === true){
3892 //}else if(this.waitMsgTarget){
3893 // this.waitMsgTarget.unmask();
3895 // Roo.MessageBox.updateProgress(1);
3896 // Roo.MessageBox.hide();
3903 Roo.callback(o.success, o.scope, [this, action]);
3904 this.fireEvent('actioncomplete', this, action);
3908 // failure condition..
3909 // we have a scenario where updates need confirming.
3910 // eg. if a locking scenario exists..
3911 // we look for { errors : { needs_confirm : true }} in the response.
3913 (typeof(action.result) != 'undefined') &&
3914 (typeof(action.result.errors) != 'undefined') &&
3915 (typeof(action.result.errors.needs_confirm) != 'undefined')
3918 Roo.log("not supported yet");
3921 Roo.MessageBox.confirm(
3922 "Change requires confirmation",
3923 action.result.errorMsg,
3928 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
3938 Roo.callback(o.failure, o.scope, [this, action]);
3939 // show an error message if no failed handler is set..
3940 if (!this.hasListener('actionfailed')) {
3941 Roo.log("need to add dialog support");
3943 Roo.MessageBox.alert("Error",
3944 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
3945 action.result.errorMsg :
3946 "Saving Failed, please check your entries or try again"
3951 this.fireEvent('actionfailed', this, action);
3956 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
3957 * @param {String} id The value to search for
3960 findField : function(id){
3961 var items = this.getItems();
3962 var field = items.get(id);
3964 items.each(function(f){
3965 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
3972 return field || null;
3975 * Mark fields in this form invalid in bulk.
3976 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
3977 * @return {BasicForm} this
3979 markInvalid : function(errors){
3980 if(errors instanceof Array){
3981 for(var i = 0, len = errors.length; i < len; i++){
3982 var fieldError = errors[i];
3983 var f = this.findField(fieldError.id);
3985 f.markInvalid(fieldError.msg);
3991 if(typeof errors[id] != 'function' && (field = this.findField(id))){
3992 field.markInvalid(errors[id]);
3996 //Roo.each(this.childForms || [], function (f) {
3997 // f.markInvalid(errors);
4004 * Set values for fields in this form in bulk.
4005 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4006 * @return {BasicForm} this
4008 setValues : function(values){
4009 if(values instanceof Array){ // array of objects
4010 for(var i = 0, len = values.length; i < len; i++){
4012 var f = this.findField(v.id);
4014 f.setValue(v.value);
4015 if(this.trackResetOnLoad){
4016 f.originalValue = f.getValue();
4020 }else{ // object hash
4023 if(typeof values[id] != 'function' && (field = this.findField(id))){
4025 if (field.setFromData &&
4027 field.displayField &&
4028 // combos' with local stores can
4029 // be queried via setValue()
4030 // to set their value..
4031 (field.store && !field.store.isLocal)
4035 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4036 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4037 field.setFromData(sd);
4040 field.setValue(values[id]);
4044 if(this.trackResetOnLoad){
4045 field.originalValue = field.getValue();
4051 //Roo.each(this.childForms || [], function (f) {
4052 // f.setValues(values);
4059 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4060 * they are returned as an array.
4061 * @param {Boolean} asString
4064 getValues : function(asString){
4065 //if (this.childForms) {
4066 // copy values from the child forms
4067 // Roo.each(this.childForms, function (f) {
4068 // this.setValues(f.getValues());
4074 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4075 if(asString === true){
4078 return Roo.urlDecode(fs);
4082 * Returns the fields in this form as an object with key/value pairs.
4083 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4086 getFieldValues : function(with_hidden)
4088 var items = this.getItems();
4090 items.each(function(f){
4094 var v = f.getValue();
4095 if (f.inputType =='radio') {
4096 if (typeof(ret[f.getName()]) == 'undefined') {
4097 ret[f.getName()] = ''; // empty..
4100 if (!f.el.dom.checked) {
4108 // not sure if this supported any more..
4109 if ((typeof(v) == 'object') && f.getRawValue) {
4110 v = f.getRawValue() ; // dates..
4112 // combo boxes where name != hiddenName...
4113 if (f.name != f.getName()) {
4114 ret[f.name] = f.getRawValue();
4116 ret[f.getName()] = v;
4123 * Clears all invalid messages in this form.
4124 * @return {BasicForm} this
4126 clearInvalid : function(){
4127 var items = this.getItems();
4129 items.each(function(f){
4140 * @return {BasicForm} this
4143 var items = this.getItems();
4144 items.each(function(f){
4148 Roo.each(this.childForms || [], function (f) {
4155 getItems : function()
4157 var r=new Roo.util.MixedCollection(false, function(o){
4158 return o.id || (o.id = Roo.id());
4160 var iter = function(el) {
4167 Roo.each(el.items,function(e) {
4186 * Ext JS Library 1.1.1
4187 * Copyright(c) 2006-2007, Ext JS, LLC.
4189 * Originally Released Under LGPL - original licence link has changed is not relivant.
4192 * <script type="text/javascript">
4195 * @class Roo.form.VTypes
4196 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4199 Roo.form.VTypes = function(){
4200 // closure these in so they are only created once.
4201 var alpha = /^[a-zA-Z_]+$/;
4202 var alphanum = /^[a-zA-Z0-9_]+$/;
4203 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4204 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4206 // All these messages and functions are configurable
4209 * The function used to validate email addresses
4210 * @param {String} value The email address
4212 'email' : function(v){
4213 return email.test(v);
4216 * The error text to display when the email validation function returns false
4219 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4221 * The keystroke filter mask to be applied on email input
4224 'emailMask' : /[a-z0-9_\.\-@]/i,
4227 * The function used to validate URLs
4228 * @param {String} value The URL
4230 'url' : function(v){
4234 * The error text to display when the url validation function returns false
4237 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4240 * The function used to validate alpha values
4241 * @param {String} value The value
4243 'alpha' : function(v){
4244 return alpha.test(v);
4247 * The error text to display when the alpha validation function returns false
4250 'alphaText' : 'This field should only contain letters and _',
4252 * The keystroke filter mask to be applied on alpha input
4255 'alphaMask' : /[a-z_]/i,
4258 * The function used to validate alphanumeric values
4259 * @param {String} value The value
4261 'alphanum' : function(v){
4262 return alphanum.test(v);
4265 * The error text to display when the alphanumeric validation function returns false
4268 'alphanumText' : 'This field should only contain letters, numbers and _',
4270 * The keystroke filter mask to be applied on alphanumeric input
4273 'alphanumMask' : /[a-z0-9_]/i
4283 * @class Roo.bootstrap.Input
4284 * @extends Roo.bootstrap.Component
4285 * Bootstrap Input class
4286 * @cfg {Boolean} disabled is it disabled
4287 * @cfg {String} fieldLabel - the label associated
4288 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4289 * @cfg {String} name name of the input
4290 * @cfg {string} fieldLabel - the label associated
4291 * @cfg {string} inputType - input / file submit ...
4292 * @cfg {string} placeholder - placeholder to put in text.
4293 * @cfg {string} before - input group add on before
4294 * @cfg {string} after - input group add on after
4295 * @cfg {string} size - (lg|sm) or leave empty..
4296 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4297 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4298 * @cfg {Number} md colspan out of 12 for computer-sized screens
4299 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4300 * @cfg {string} value default value of the input
4301 * @cfg {Number} labelWidth set the width of label (0-12)
4302 * @cfg {String} labelAlign (top|left)
4306 * Create a new Input
4307 * @param {Object} config The config object
4310 Roo.bootstrap.Input = function(config){
4311 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4316 * Fires when this field receives input focus.
4317 * @param {Roo.form.Field} this
4322 * Fires when this field loses input focus.
4323 * @param {Roo.form.Field} this
4328 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4329 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4330 * @param {Roo.form.Field} this
4331 * @param {Roo.EventObject} e The event object
4336 * Fires just before the field blurs if the field value has changed.
4337 * @param {Roo.form.Field} this
4338 * @param {Mixed} newValue The new value
4339 * @param {Mixed} oldValue The original value
4344 * Fires after the field has been marked as invalid.
4345 * @param {Roo.form.Field} this
4346 * @param {String} msg The validation message
4351 * Fires after the field has been validated with no errors.
4352 * @param {Roo.form.Field} this
4357 * Fires after the key up
4358 * @param {Roo.form.Field} this
4359 * @param {Roo.EventObject} e The event Object
4365 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4367 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4368 automatic validation (defaults to "keyup").
4370 validationEvent : "keyup",
4372 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4374 validateOnBlur : true,
4376 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4378 validationDelay : 250,
4380 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4382 focusClass : "x-form-focus", // not needed???
4386 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4388 invalidClass : "has-error",
4391 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4393 selectOnFocus : false,
4396 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4400 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4405 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4407 disableKeyFilter : false,
4410 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4414 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4418 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4420 blankText : "This field is required",
4423 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4427 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4429 maxLength : Number.MAX_VALUE,
4431 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4433 minLengthText : "The minimum length for this field is {0}",
4435 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4437 maxLengthText : "The maximum length for this field is {0}",
4441 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4442 * If available, this function will be called only after the basic validators all return true, and will be passed the
4443 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4447 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4448 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4449 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4453 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4475 parentLabelAlign : function()
4478 while (parent.parent()) {
4479 parent = parent.parent();
4480 if (typeof(parent.labelAlign) !='undefined') {
4481 return parent.labelAlign;
4488 getAutoCreate : function(){
4490 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4496 if(this.inputType != 'hidden'){
4497 cfg.cls = 'form-group' //input-group
4503 type : this.inputType,
4505 cls : 'form-control',
4506 placeholder : this.placeholder || ''
4510 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4511 input.maxLength = this.maxLength;
4514 if (this.disabled) {
4515 input.disabled=true;
4519 input.name = this.name;
4522 input.cls += ' input-' + this.size;
4525 ['xs','sm','md','lg'].map(function(size){
4526 if (settings[size]) {
4527 cfg.cls += ' col-' + size + '-' + settings[size];
4531 var inputblock = input;
4533 if (this.before || this.after) {
4536 cls : 'input-group',
4540 inputblock.cn.push({
4542 cls : 'input-group-addon',
4546 inputblock.cn.push(input);
4548 inputblock.cn.push({
4550 cls : 'input-group-addon',
4557 if (align ==='left' && this.fieldLabel.length) {
4558 Roo.log("left and has label");
4564 cls : 'control-label col-sm-' + this.labelWidth,
4565 html : this.fieldLabel
4569 cls : "col-sm-" + (12 - this.labelWidth),
4576 } else if ( this.fieldLabel.length) {
4582 //cls : 'input-group-addon',
4583 html : this.fieldLabel
4593 Roo.log(" no label && no align");
4602 Roo.log('input-parentType: ' + this.parentType);
4604 if (this.parentType === 'Navbar' && this.parent().bar) {
4605 cfg.cls += ' navbar-form';
4613 * return the real input element.
4615 inputEl: function ()
4617 return this.el.select('input.form-control',true).first();
4619 setDisabled : function(v)
4621 var i = this.inputEl().dom;
4623 i.removeAttribute('disabled');
4627 i.setAttribute('disabled','true');
4629 initEvents : function()
4632 this.inputEl().on("keydown" , this.fireKey, this);
4633 this.inputEl().on("focus", this.onFocus, this);
4634 this.inputEl().on("blur", this.onBlur, this);
4636 this.inputEl().relayEvent('keyup', this);
4638 // reference to original value for reset
4639 this.originalValue = this.getValue();
4640 //Roo.form.TextField.superclass.initEvents.call(this);
4641 if(this.validationEvent == 'keyup'){
4642 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4643 this.inputEl().on('keyup', this.filterValidation, this);
4645 else if(this.validationEvent !== false){
4646 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4649 if(this.selectOnFocus){
4650 this.on("focus", this.preFocus, this);
4653 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4654 this.inputEl().on("keypress", this.filterKeys, this);
4657 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4658 this.el.on("click", this.autoSize, this);
4661 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4662 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4666 filterValidation : function(e){
4667 if(!e.isNavKeyPress()){
4668 this.validationTask.delay(this.validationDelay);
4672 * Validates the field value
4673 * @return {Boolean} True if the value is valid, else false
4675 validate : function(){
4676 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4677 if(this.disabled || this.validateValue(this.getRawValue())){
4678 this.clearInvalid();
4686 * Validates a value according to the field's validation rules and marks the field as invalid
4687 * if the validation fails
4688 * @param {Mixed} value The value to validate
4689 * @return {Boolean} True if the value is valid, else false
4691 validateValue : function(value){
4692 if(value.length < 1) { // if it's blank
4693 if(this.allowBlank){
4694 this.clearInvalid();
4697 this.markInvalid(this.blankText);
4701 if(value.length < this.minLength){
4702 this.markInvalid(String.format(this.minLengthText, this.minLength));
4705 if(value.length > this.maxLength){
4706 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4710 var vt = Roo.form.VTypes;
4711 if(!vt[this.vtype](value, this)){
4712 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4716 if(typeof this.validator == "function"){
4717 var msg = this.validator(value);
4719 this.markInvalid(msg);
4723 if(this.regex && !this.regex.test(value)){
4724 this.markInvalid(this.regexText);
4733 fireKey : function(e){
4734 //Roo.log('field ' + e.getKey());
4735 if(e.isNavKeyPress()){
4736 this.fireEvent("specialkey", this, e);
4739 focus : function (selectText){
4741 this.inputEl().focus();
4742 if(selectText === true){
4743 this.inputEl().dom.select();
4749 onFocus : function(){
4750 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4751 // this.el.addClass(this.focusClass);
4754 this.hasFocus = true;
4755 this.startValue = this.getValue();
4756 this.fireEvent("focus", this);
4760 beforeBlur : Roo.emptyFn,
4764 onBlur : function(){
4766 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4767 //this.el.removeClass(this.focusClass);
4769 this.hasFocus = false;
4770 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4773 var v = this.getValue();
4774 if(String(v) !== String(this.startValue)){
4775 this.fireEvent('change', this, v, this.startValue);
4777 this.fireEvent("blur", this);
4781 * Resets the current field value to the originally loaded value and clears any validation messages
4784 this.setValue(this.originalValue);
4785 this.clearInvalid();
4788 * Returns the name of the field
4789 * @return {Mixed} name The name field
4791 getName: function(){
4795 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4796 * @return {Mixed} value The field value
4798 getValue : function(){
4799 return this.inputEl().getValue();
4802 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4803 * @return {Mixed} value The field value
4805 getRawValue : function(){
4806 var v = this.inputEl().getValue();
4812 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4813 * @param {Mixed} value The value to set
4815 setRawValue : function(v){
4816 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4819 selectText : function(start, end){
4820 var v = this.getRawValue();
4822 start = start === undefined ? 0 : start;
4823 end = end === undefined ? v.length : end;
4824 var d = this.inputEl().dom;
4825 if(d.setSelectionRange){
4826 d.setSelectionRange(start, end);
4827 }else if(d.createTextRange){
4828 var range = d.createTextRange();
4829 range.moveStart("character", start);
4830 range.moveEnd("character", v.length-end);
4837 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4838 * @param {Mixed} value The value to set
4840 setValue : function(v){
4843 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4849 processValue : function(value){
4850 if(this.stripCharsRe){
4851 var newValue = value.replace(this.stripCharsRe, '');
4852 if(newValue !== value){
4853 this.setRawValue(newValue);
4860 preFocus : function(){
4862 if(this.selectOnFocus){
4863 this.inputEl().dom.select();
4866 filterKeys : function(e){
4868 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4871 var c = e.getCharCode(), cc = String.fromCharCode(c);
4872 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4875 if(!this.maskRe.test(cc)){
4880 * Clear any invalid styles/messages for this field
4882 clearInvalid : function(){
4884 if(!this.el || this.preventMark){ // not rendered
4887 this.el.removeClass(this.invalidClass);
4889 switch(this.msgTarget){
4891 this.el.dom.qtip = '';
4894 this.el.dom.title = '';
4898 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4903 this.errorIcon.dom.qtip = '';
4904 this.errorIcon.hide();
4905 this.un('resize', this.alignErrorIcon, this);
4909 var t = Roo.getDom(this.msgTarget);
4911 t.style.display = 'none';
4915 this.fireEvent('valid', this);
4918 * Mark this field as invalid
4919 * @param {String} msg The validation message
4921 markInvalid : function(msg){
4922 if(!this.el || this.preventMark){ // not rendered
4925 this.el.addClass(this.invalidClass);
4927 msg = msg || this.invalidText;
4928 switch(this.msgTarget){
4930 this.el.dom.qtip = msg;
4931 this.el.dom.qclass = 'x-form-invalid-tip';
4932 if(Roo.QuickTips){ // fix for floating editors interacting with DND
4933 Roo.QuickTips.enable();
4937 this.el.dom.title = msg;
4941 var elp = this.el.findParent('.x-form-element', 5, true);
4942 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
4943 this.errorEl.setWidth(elp.getWidth(true)-20);
4945 this.errorEl.update(msg);
4946 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
4949 if(!this.errorIcon){
4950 var elp = this.el.findParent('.x-form-element', 5, true);
4951 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
4953 this.alignErrorIcon();
4954 this.errorIcon.dom.qtip = msg;
4955 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
4956 this.errorIcon.show();
4957 this.on('resize', this.alignErrorIcon, this);
4960 var t = Roo.getDom(this.msgTarget);
4962 t.style.display = this.msgDisplay;
4966 this.fireEvent('invalid', this, msg);
4969 SafariOnKeyDown : function(event)
4971 // this is a workaround for a password hang bug on chrome/ webkit.
4973 var isSelectAll = false;
4975 if(this.inputEl().dom.selectionEnd > 0){
4976 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
4978 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
4979 event.preventDefault();
4984 if(isSelectAll){ // backspace and delete key
4986 event.preventDefault();
4987 // this is very hacky as keydown always get's upper case.
4989 var cc = String.fromCharCode(event.getCharCode());
4990 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
4994 adjustWidth : function(tag, w){
4995 tag = tag.toLowerCase();
4996 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
4997 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5001 if(tag == 'textarea'){
5004 }else if(Roo.isOpera){
5008 if(tag == 'textarea'){
5027 * @class Roo.bootstrap.TextArea
5028 * @extends Roo.bootstrap.Input
5029 * Bootstrap TextArea class
5030 * @cfg {Number} cols Specifies the visible width of a text area
5031 * @cfg {Number} rows Specifies the visible number of lines in a text area
5032 * @cfg {Number} readOnly Specifies that a text area should be read-only
5033 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5034 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5035 * @cfg {string} html text
5038 * Create a new TextArea
5039 * @param {Object} config The config object
5042 Roo.bootstrap.TextArea = function(config){
5043 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5047 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5057 getAutoCreate : function(){
5059 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5070 value : this.value || '',
5071 html: this.html || '',
5072 cls : 'form-control',
5073 placeholder : this.placeholder || ''
5077 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5078 input.maxLength = this.maxLength;
5082 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5086 input.cols = this.cols;
5089 if (this.readOnly) {
5090 input.readonly = true;
5094 input.name = this.name;
5098 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5102 ['xs','sm','md','lg'].map(function(size){
5103 if (settings[size]) {
5104 cfg.cls += ' col-' + size + '-' + settings[size];
5108 var inputblock = input;
5110 if (this.before || this.after) {
5113 cls : 'input-group',
5117 inputblock.cn.push({
5119 cls : 'input-group-addon',
5123 inputblock.cn.push(input);
5125 inputblock.cn.push({
5127 cls : 'input-group-addon',
5134 if (align ==='left' && this.fieldLabel.length) {
5135 Roo.log("left and has label");
5141 cls : 'control-label col-sm-' + this.labelWidth,
5142 html : this.fieldLabel
5146 cls : "col-sm-" + (12 - this.labelWidth),
5153 } else if ( this.fieldLabel.length) {
5159 //cls : 'input-group-addon',
5160 html : this.fieldLabel
5170 Roo.log(" no label && no align");
5180 if (this.disabled) {
5181 input.disabled=true;
5188 * return the real textarea element.
5190 inputEl: function ()
5192 return this.el.select('textarea.form-control',true).first();
5200 * trigger field - base class for combo..
5205 * @class Roo.bootstrap.TriggerField
5206 * @extends Roo.bootstrap.Input
5207 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5208 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5209 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5210 * for which you can provide a custom implementation. For example:
5212 var trigger = new Roo.bootstrap.TriggerField();
5213 trigger.onTriggerClick = myTriggerFn;
5214 trigger.applyTo('my-field');
5217 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5218 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5219 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5220 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5222 * Create a new TriggerField.
5223 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5224 * to the base TextField)
5226 Roo.bootstrap.TriggerField = function(config){
5227 this.mimicing = false;
5228 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5231 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5233 * @cfg {String} triggerClass A CSS class to apply to the trigger
5236 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5240 /** @cfg {Boolean} grow @hide */
5241 /** @cfg {Number} growMin @hide */
5242 /** @cfg {Number} growMax @hide */
5248 autoSize: Roo.emptyFn,
5255 actionMode : 'wrap',
5259 getAutoCreate : function(){
5261 var parent = this.parent();
5263 var align = this.parentLabelAlign();
5268 cls: 'form-group' //input-group
5275 type : this.inputType,
5276 cls : 'form-control',
5277 autocomplete: 'off',
5278 placeholder : this.placeholder || ''
5282 input.name = this.name;
5285 input.cls += ' input-' + this.size;
5288 if (this.disabled) {
5289 input.disabled=true;
5292 var inputblock = input;
5294 if (this.before || this.after) {
5297 cls : 'input-group',
5301 inputblock.cn.push({
5303 cls : 'input-group-addon',
5307 inputblock.cn.push(input);
5309 inputblock.cn.push({
5311 cls : 'input-group-addon',
5324 cls: 'form-hidden-field'
5332 Roo.log('multiple');
5340 cls: 'form-hidden-field'
5344 cls: 'select2-choices',
5348 cls: 'select2-search-field',
5361 cls: 'select2-container input-group',
5366 cls: 'typeahead typeahead-long dropdown-menu',
5367 style: 'display:none'
5375 cls : 'input-group-addon btn dropdown-toggle',
5383 cls: 'combobox-clear',
5397 combobox.cls += ' select2-container-multi';
5400 if (align ==='left' && this.fieldLabel.length) {
5402 Roo.log("left and has label");
5408 cls : 'control-label col-sm-' + this.labelWidth,
5409 html : this.fieldLabel
5413 cls : "col-sm-" + (12 - this.labelWidth),
5420 } else if ( this.fieldLabel.length) {
5426 //cls : 'input-group-addon',
5427 html : this.fieldLabel
5437 Roo.log(" no label && no align");
5444 ['xs','sm','md','lg'].map(function(size){
5445 if (settings[size]) {
5446 cfg.cls += ' col-' + size + '-' + settings[size];
5457 onResize : function(w, h){
5458 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5459 // if(typeof w == 'number'){
5460 // var x = w - this.trigger.getWidth();
5461 // this.inputEl().setWidth(this.adjustWidth('input', x));
5462 // this.trigger.setStyle('left', x+'px');
5467 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5470 getResizeEl : function(){
5471 return this.inputEl();
5475 getPositionEl : function(){
5476 return this.inputEl();
5480 alignErrorIcon : function(){
5481 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5485 initEvents : function(){
5487 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5488 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5490 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5491 if(this.hideTrigger){
5492 this.trigger.setDisplayed(false);
5494 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5498 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5501 //this.trigger.addClassOnOver('x-form-trigger-over');
5502 //this.trigger.addClassOnClick('x-form-trigger-click');
5505 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5510 initTrigger : function(){
5515 onDestroy : function(){
5517 this.trigger.removeAllListeners();
5518 // this.trigger.remove();
5521 // this.wrap.remove();
5523 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5527 onFocus : function(){
5528 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5531 this.wrap.addClass('x-trigger-wrap-focus');
5532 this.mimicing = true;
5533 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5534 if(this.monitorTab){
5535 this.el.on("keydown", this.checkTab, this);
5542 checkTab : function(e){
5543 if(e.getKey() == e.TAB){
5549 onBlur : function(){
5554 mimicBlur : function(e, t){
5556 if(!this.wrap.contains(t) && this.validateBlur()){
5563 triggerBlur : function(){
5564 this.mimicing = false;
5565 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5566 if(this.monitorTab){
5567 this.el.un("keydown", this.checkTab, this);
5569 //this.wrap.removeClass('x-trigger-wrap-focus');
5570 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5574 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5575 validateBlur : function(e, t){
5580 onDisable : function(){
5581 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5583 // this.wrap.addClass('x-item-disabled');
5588 onEnable : function(){
5589 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5591 // this.el.removeClass('x-item-disabled');
5596 onShow : function(){
5597 var ae = this.getActionEl();
5600 ae.dom.style.display = '';
5601 ae.dom.style.visibility = 'visible';
5607 onHide : function(){
5608 var ae = this.getActionEl();
5609 ae.dom.style.display = 'none';
5613 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5614 * by an implementing function.
5616 * @param {EventObject} e
5618 onTriggerClick : Roo.emptyFn
5622 * Ext JS Library 1.1.1
5623 * Copyright(c) 2006-2007, Ext JS, LLC.
5625 * Originally Released Under LGPL - original licence link has changed is not relivant.
5628 * <script type="text/javascript">
5633 * @class Roo.data.SortTypes
5635 * Defines the default sorting (casting?) comparison functions used when sorting data.
5637 Roo.data.SortTypes = {
5639 * Default sort that does nothing
5640 * @param {Mixed} s The value being converted
5641 * @return {Mixed} The comparison value
5648 * The regular expression used to strip tags
5652 stripTagsRE : /<\/?[^>]+>/gi,
5655 * Strips all HTML tags to sort on text only
5656 * @param {Mixed} s The value being converted
5657 * @return {String} The comparison value
5659 asText : function(s){
5660 return String(s).replace(this.stripTagsRE, "");
5664 * Strips all HTML tags to sort on text only - Case insensitive
5665 * @param {Mixed} s The value being converted
5666 * @return {String} The comparison value
5668 asUCText : function(s){
5669 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5673 * Case insensitive string
5674 * @param {Mixed} s The value being converted
5675 * @return {String} The comparison value
5677 asUCString : function(s) {
5678 return String(s).toUpperCase();
5683 * @param {Mixed} s The value being converted
5684 * @return {Number} The comparison value
5686 asDate : function(s) {
5690 if(s instanceof Date){
5693 return Date.parse(String(s));
5698 * @param {Mixed} s The value being converted
5699 * @return {Float} The comparison value
5701 asFloat : function(s) {
5702 var val = parseFloat(String(s).replace(/,/g, ""));
5703 if(isNaN(val)) val = 0;
5709 * @param {Mixed} s The value being converted
5710 * @return {Number} The comparison value
5712 asInt : function(s) {
5713 var val = parseInt(String(s).replace(/,/g, ""));
5714 if(isNaN(val)) val = 0;
5719 * Ext JS Library 1.1.1
5720 * Copyright(c) 2006-2007, Ext JS, LLC.
5722 * Originally Released Under LGPL - original licence link has changed is not relivant.
5725 * <script type="text/javascript">
5729 * @class Roo.data.Record
5730 * Instances of this class encapsulate both record <em>definition</em> information, and record
5731 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5732 * to access Records cached in an {@link Roo.data.Store} object.<br>
5734 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5735 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5738 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5740 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5741 * {@link #create}. The parameters are the same.
5742 * @param {Array} data An associative Array of data values keyed by the field name.
5743 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5744 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5745 * not specified an integer id is generated.
5747 Roo.data.Record = function(data, id){
5748 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5753 * Generate a constructor for a specific record layout.
5754 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5755 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5756 * Each field definition object may contain the following properties: <ul>
5757 * <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,
5758 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5759 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5760 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5761 * is being used, then this is a string containing the javascript expression to reference the data relative to
5762 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5763 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5764 * this may be omitted.</p></li>
5765 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5766 * <ul><li>auto (Default, implies no conversion)</li>
5771 * <li>date</li></ul></p></li>
5772 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5773 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5774 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5775 * by the Reader into an object that will be stored in the Record. It is passed the
5776 * following parameters:<ul>
5777 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5779 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5781 * <br>usage:<br><pre><code>
5782 var TopicRecord = Roo.data.Record.create(
5783 {name: 'title', mapping: 'topic_title'},
5784 {name: 'author', mapping: 'username'},
5785 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5786 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5787 {name: 'lastPoster', mapping: 'user2'},
5788 {name: 'excerpt', mapping: 'post_text'}
5791 var myNewRecord = new TopicRecord({
5792 title: 'Do my job please',
5795 lastPost: new Date(),
5796 lastPoster: 'Animal',
5797 excerpt: 'No way dude!'
5799 myStore.add(myNewRecord);
5804 Roo.data.Record.create = function(o){
5806 f.superclass.constructor.apply(this, arguments);
5808 Roo.extend(f, Roo.data.Record);
5809 var p = f.prototype;
5810 p.fields = new Roo.util.MixedCollection(false, function(field){
5813 for(var i = 0, len = o.length; i < len; i++){
5814 p.fields.add(new Roo.data.Field(o[i]));
5816 f.getField = function(name){
5817 return p.fields.get(name);
5822 Roo.data.Record.AUTO_ID = 1000;
5823 Roo.data.Record.EDIT = 'edit';
5824 Roo.data.Record.REJECT = 'reject';
5825 Roo.data.Record.COMMIT = 'commit';
5827 Roo.data.Record.prototype = {
5829 * Readonly flag - true if this record has been modified.
5838 join : function(store){
5843 * Set the named field to the specified value.
5844 * @param {String} name The name of the field to set.
5845 * @param {Object} value The value to set the field to.
5847 set : function(name, value){
5848 if(this.data[name] == value){
5855 if(typeof this.modified[name] == 'undefined'){
5856 this.modified[name] = this.data[name];
5858 this.data[name] = value;
5859 if(!this.editing && this.store){
5860 this.store.afterEdit(this);
5865 * Get the value of the named field.
5866 * @param {String} name The name of the field to get the value of.
5867 * @return {Object} The value of the field.
5869 get : function(name){
5870 return this.data[name];
5874 beginEdit : function(){
5875 this.editing = true;
5880 cancelEdit : function(){
5881 this.editing = false;
5882 delete this.modified;
5886 endEdit : function(){
5887 this.editing = false;
5888 if(this.dirty && this.store){
5889 this.store.afterEdit(this);
5894 * Usually called by the {@link Roo.data.Store} which owns the Record.
5895 * Rejects all changes made to the Record since either creation, or the last commit operation.
5896 * Modified fields are reverted to their original values.
5898 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5899 * of reject operations.
5901 reject : function(){
5902 var m = this.modified;
5904 if(typeof m[n] != "function"){
5905 this.data[n] = m[n];
5909 delete this.modified;
5910 this.editing = false;
5912 this.store.afterReject(this);
5917 * Usually called by the {@link Roo.data.Store} which owns the Record.
5918 * Commits all changes made to the Record since either creation, or the last commit operation.
5920 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5921 * of commit operations.
5923 commit : function(){
5925 delete this.modified;
5926 this.editing = false;
5928 this.store.afterCommit(this);
5933 hasError : function(){
5934 return this.error != null;
5938 clearError : function(){
5943 * Creates a copy of this record.
5944 * @param {String} id (optional) A new record id if you don't want to use this record's id
5947 copy : function(newId) {
5948 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
5952 * Ext JS Library 1.1.1
5953 * Copyright(c) 2006-2007, Ext JS, LLC.
5955 * Originally Released Under LGPL - original licence link has changed is not relivant.
5958 * <script type="text/javascript">
5964 * @class Roo.data.Store
5965 * @extends Roo.util.Observable
5966 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
5967 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
5969 * 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
5970 * has no knowledge of the format of the data returned by the Proxy.<br>
5972 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
5973 * instances from the data object. These records are cached and made available through accessor functions.
5975 * Creates a new Store.
5976 * @param {Object} config A config object containing the objects needed for the Store to access data,
5977 * and read the data into Records.
5979 Roo.data.Store = function(config){
5980 this.data = new Roo.util.MixedCollection(false);
5981 this.data.getKey = function(o){
5984 this.baseParams = {};
5991 "multisort" : "_multisort"
5994 if(config && config.data){
5995 this.inlineData = config.data;
5999 Roo.apply(this, config);
6001 if(this.reader){ // reader passed
6002 this.reader = Roo.factory(this.reader, Roo.data);
6003 this.reader.xmodule = this.xmodule || false;
6004 if(!this.recordType){
6005 this.recordType = this.reader.recordType;
6007 if(this.reader.onMetaChange){
6008 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6012 if(this.recordType){
6013 this.fields = this.recordType.prototype.fields;
6019 * @event datachanged
6020 * Fires when the data cache has changed, and a widget which is using this Store
6021 * as a Record cache should refresh its view.
6022 * @param {Store} this
6027 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6028 * @param {Store} this
6029 * @param {Object} meta The JSON metadata
6034 * Fires when Records have been added to the Store
6035 * @param {Store} this
6036 * @param {Roo.data.Record[]} records The array of Records added
6037 * @param {Number} index The index at which the record(s) were added
6042 * Fires when a Record has been removed from the Store
6043 * @param {Store} this
6044 * @param {Roo.data.Record} record The Record that was removed
6045 * @param {Number} index The index at which the record was removed
6050 * Fires when a Record has been updated
6051 * @param {Store} this
6052 * @param {Roo.data.Record} record The Record that was updated
6053 * @param {String} operation The update operation being performed. Value may be one of:
6055 Roo.data.Record.EDIT
6056 Roo.data.Record.REJECT
6057 Roo.data.Record.COMMIT
6063 * Fires when the data cache has been cleared.
6064 * @param {Store} this
6069 * Fires before a request is made for a new data object. If the beforeload handler returns false
6070 * the load action will be canceled.
6071 * @param {Store} this
6072 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6076 * @event beforeloadadd
6077 * Fires after a new set of Records has been loaded.
6078 * @param {Store} this
6079 * @param {Roo.data.Record[]} records The Records that were loaded
6080 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6082 beforeloadadd : true,
6085 * Fires after a new set of Records has been loaded, before they are added to the store.
6086 * @param {Store} this
6087 * @param {Roo.data.Record[]} records The Records that were loaded
6088 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6089 * @params {Object} return from reader
6093 * @event loadexception
6094 * Fires if an exception occurs in the Proxy during loading.
6095 * Called with the signature of the Proxy's "loadexception" event.
6096 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6099 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6100 * @param {Object} load options
6101 * @param {Object} jsonData from your request (normally this contains the Exception)
6103 loadexception : true
6107 this.proxy = Roo.factory(this.proxy, Roo.data);
6108 this.proxy.xmodule = this.xmodule || false;
6109 this.relayEvents(this.proxy, ["loadexception"]);
6111 this.sortToggle = {};
6112 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6114 Roo.data.Store.superclass.constructor.call(this);
6116 if(this.inlineData){
6117 this.loadData(this.inlineData);
6118 delete this.inlineData;
6122 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6124 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6125 * without a remote query - used by combo/forms at present.
6129 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6132 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6135 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6136 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6139 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6140 * on any HTTP request
6143 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6146 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6150 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6151 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6156 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6157 * loaded or when a record is removed. (defaults to false).
6159 pruneModifiedRecords : false,
6165 * Add Records to the Store and fires the add event.
6166 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6168 add : function(records){
6169 records = [].concat(records);
6170 for(var i = 0, len = records.length; i < len; i++){
6171 records[i].join(this);
6173 var index = this.data.length;
6174 this.data.addAll(records);
6175 this.fireEvent("add", this, records, index);
6179 * Remove a Record from the Store and fires the remove event.
6180 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6182 remove : function(record){
6183 var index = this.data.indexOf(record);
6184 this.data.removeAt(index);
6185 if(this.pruneModifiedRecords){
6186 this.modified.remove(record);
6188 this.fireEvent("remove", this, record, index);
6192 * Remove all Records from the Store and fires the clear event.
6194 removeAll : function(){
6196 if(this.pruneModifiedRecords){
6199 this.fireEvent("clear", this);
6203 * Inserts Records to the Store at the given index and fires the add event.
6204 * @param {Number} index The start index at which to insert the passed Records.
6205 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6207 insert : function(index, records){
6208 records = [].concat(records);
6209 for(var i = 0, len = records.length; i < len; i++){
6210 this.data.insert(index, records[i]);
6211 records[i].join(this);
6213 this.fireEvent("add", this, records, index);
6217 * Get the index within the cache of the passed Record.
6218 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6219 * @return {Number} The index of the passed Record. Returns -1 if not found.
6221 indexOf : function(record){
6222 return this.data.indexOf(record);
6226 * Get the index within the cache of the Record with the passed id.
6227 * @param {String} id The id of the Record to find.
6228 * @return {Number} The index of the Record. Returns -1 if not found.
6230 indexOfId : function(id){
6231 return this.data.indexOfKey(id);
6235 * Get the Record with the specified id.
6236 * @param {String} id The id of the Record to find.
6237 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6239 getById : function(id){
6240 return this.data.key(id);
6244 * Get the Record at the specified index.
6245 * @param {Number} index The index of the Record to find.
6246 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6248 getAt : function(index){
6249 return this.data.itemAt(index);
6253 * Returns a range of Records between specified indices.
6254 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6255 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6256 * @return {Roo.data.Record[]} An array of Records
6258 getRange : function(start, end){
6259 return this.data.getRange(start, end);
6263 storeOptions : function(o){
6264 o = Roo.apply({}, o);
6267 this.lastOptions = o;
6271 * Loads the Record cache from the configured Proxy using the configured Reader.
6273 * If using remote paging, then the first load call must specify the <em>start</em>
6274 * and <em>limit</em> properties in the options.params property to establish the initial
6275 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6277 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6278 * and this call will return before the new data has been loaded. Perform any post-processing
6279 * in a callback function, or in a "load" event handler.</strong>
6281 * @param {Object} options An object containing properties which control loading options:<ul>
6282 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6283 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6284 * passed the following arguments:<ul>
6285 * <li>r : Roo.data.Record[]</li>
6286 * <li>options: Options object from the load call</li>
6287 * <li>success: Boolean success indicator</li></ul></li>
6288 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6289 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6292 load : function(options){
6293 options = options || {};
6294 if(this.fireEvent("beforeload", this, options) !== false){
6295 this.storeOptions(options);
6296 var p = Roo.apply(options.params || {}, this.baseParams);
6297 // if meta was not loaded from remote source.. try requesting it.
6298 if (!this.reader.metaFromRemote) {
6301 if(this.sortInfo && this.remoteSort){
6302 var pn = this.paramNames;
6303 p[pn["sort"]] = this.sortInfo.field;
6304 p[pn["dir"]] = this.sortInfo.direction;
6306 if (this.multiSort) {
6307 var pn = this.paramNames;
6308 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6311 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6316 * Reloads the Record cache from the configured Proxy using the configured Reader and
6317 * the options from the last load operation performed.
6318 * @param {Object} options (optional) An object containing properties which may override the options
6319 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6320 * the most recently used options are reused).
6322 reload : function(options){
6323 this.load(Roo.applyIf(options||{}, this.lastOptions));
6327 // Called as a callback by the Reader during a load operation.
6328 loadRecords : function(o, options, success){
6329 if(!o || success === false){
6330 if(success !== false){
6331 this.fireEvent("load", this, [], options, o);
6333 if(options.callback){
6334 options.callback.call(options.scope || this, [], options, false);
6338 // if data returned failure - throw an exception.
6339 if (o.success === false) {
6340 // show a message if no listener is registered.
6341 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6342 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6344 // loadmask wil be hooked into this..
6345 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6348 var r = o.records, t = o.totalRecords || r.length;
6350 this.fireEvent("beforeloadadd", this, r, options, o);
6352 if(!options || options.add !== true){
6353 if(this.pruneModifiedRecords){
6356 for(var i = 0, len = r.length; i < len; i++){
6360 this.data = this.snapshot;
6361 delete this.snapshot;
6364 this.data.addAll(r);
6365 this.totalLength = t;
6367 this.fireEvent("datachanged", this);
6369 this.totalLength = Math.max(t, this.data.length+r.length);
6372 this.fireEvent("load", this, r, options, o);
6373 if(options.callback){
6374 options.callback.call(options.scope || this, r, options, true);
6380 * Loads data from a passed data block. A Reader which understands the format of the data
6381 * must have been configured in the constructor.
6382 * @param {Object} data The data block from which to read the Records. The format of the data expected
6383 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6384 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6386 loadData : function(o, append){
6387 var r = this.reader.readRecords(o);
6388 this.loadRecords(r, {add: append}, true);
6392 * Gets the number of cached records.
6394 * <em>If using paging, this may not be the total size of the dataset. If the data object
6395 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6396 * the data set size</em>
6398 getCount : function(){
6399 return this.data.length || 0;
6403 * Gets the total number of records in the dataset as returned by the server.
6405 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6406 * the dataset size</em>
6408 getTotalCount : function(){
6409 return this.totalLength || 0;
6413 * Returns the sort state of the Store as an object with two properties:
6415 field {String} The name of the field by which the Records are sorted
6416 direction {String} The sort order, "ASC" or "DESC"
6419 getSortState : function(){
6420 return this.sortInfo;
6424 applySort : function(){
6425 if(this.sortInfo && !this.remoteSort){
6426 var s = this.sortInfo, f = s.field;
6427 var st = this.fields.get(f).sortType;
6428 var fn = function(r1, r2){
6429 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6430 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6432 this.data.sort(s.direction, fn);
6433 if(this.snapshot && this.snapshot != this.data){
6434 this.snapshot.sort(s.direction, fn);
6440 * Sets the default sort column and order to be used by the next load operation.
6441 * @param {String} fieldName The name of the field to sort by.
6442 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6444 setDefaultSort : function(field, dir){
6445 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6450 * If remote sorting is used, the sort is performed on the server, and the cache is
6451 * reloaded. If local sorting is used, the cache is sorted internally.
6452 * @param {String} fieldName The name of the field to sort by.
6453 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6455 sort : function(fieldName, dir){
6456 var f = this.fields.get(fieldName);
6458 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6460 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6461 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6466 this.sortToggle[f.name] = dir;
6467 this.sortInfo = {field: f.name, direction: dir};
6468 if(!this.remoteSort){
6470 this.fireEvent("datachanged", this);
6472 this.load(this.lastOptions);
6477 * Calls the specified function for each of the Records in the cache.
6478 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6479 * Returning <em>false</em> aborts and exits the iteration.
6480 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6482 each : function(fn, scope){
6483 this.data.each(fn, scope);
6487 * Gets all records modified since the last commit. Modified records are persisted across load operations
6488 * (e.g., during paging).
6489 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6491 getModifiedRecords : function(){
6492 return this.modified;
6496 createFilterFn : function(property, value, anyMatch){
6497 if(!value.exec){ // not a regex
6498 value = String(value);
6499 if(value.length == 0){
6502 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6505 return value.test(r.data[property]);
6510 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6511 * @param {String} property A field on your records
6512 * @param {Number} start The record index to start at (defaults to 0)
6513 * @param {Number} end The last record index to include (defaults to length - 1)
6514 * @return {Number} The sum
6516 sum : function(property, start, end){
6517 var rs = this.data.items, v = 0;
6519 end = (end || end === 0) ? end : rs.length-1;
6521 for(var i = start; i <= end; i++){
6522 v += (rs[i].data[property] || 0);
6528 * Filter the records by a specified property.
6529 * @param {String} field A field on your records
6530 * @param {String/RegExp} value Either a string that the field
6531 * should start with or a RegExp to test against the field
6532 * @param {Boolean} anyMatch True to match any part not just the beginning
6534 filter : function(property, value, anyMatch){
6535 var fn = this.createFilterFn(property, value, anyMatch);
6536 return fn ? this.filterBy(fn) : this.clearFilter();
6540 * Filter by a function. The specified function will be called with each
6541 * record in this data source. If the function returns true the record is included,
6542 * otherwise it is filtered.
6543 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6544 * @param {Object} scope (optional) The scope of the function (defaults to this)
6546 filterBy : function(fn, scope){
6547 this.snapshot = this.snapshot || this.data;
6548 this.data = this.queryBy(fn, scope||this);
6549 this.fireEvent("datachanged", this);
6553 * Query the records by a specified property.
6554 * @param {String} field A field on your records
6555 * @param {String/RegExp} value Either a string that the field
6556 * should start with or a RegExp to test against the field
6557 * @param {Boolean} anyMatch True to match any part not just the beginning
6558 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6560 query : function(property, value, anyMatch){
6561 var fn = this.createFilterFn(property, value, anyMatch);
6562 return fn ? this.queryBy(fn) : this.data.clone();
6566 * Query by a function. The specified function will be called with each
6567 * record in this data source. If the function returns true the record is included
6569 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6570 * @param {Object} scope (optional) The scope of the function (defaults to this)
6571 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6573 queryBy : function(fn, scope){
6574 var data = this.snapshot || this.data;
6575 return data.filterBy(fn, scope||this);
6579 * Collects unique values for a particular dataIndex from this store.
6580 * @param {String} dataIndex The property to collect
6581 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6582 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6583 * @return {Array} An array of the unique values
6585 collect : function(dataIndex, allowNull, bypassFilter){
6586 var d = (bypassFilter === true && this.snapshot) ?
6587 this.snapshot.items : this.data.items;
6588 var v, sv, r = [], l = {};
6589 for(var i = 0, len = d.length; i < len; i++){
6590 v = d[i].data[dataIndex];
6592 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6601 * Revert to a view of the Record cache with no filtering applied.
6602 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6604 clearFilter : function(suppressEvent){
6605 if(this.snapshot && this.snapshot != this.data){
6606 this.data = this.snapshot;
6607 delete this.snapshot;
6608 if(suppressEvent !== true){
6609 this.fireEvent("datachanged", this);
6615 afterEdit : function(record){
6616 if(this.modified.indexOf(record) == -1){
6617 this.modified.push(record);
6619 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6623 afterReject : function(record){
6624 this.modified.remove(record);
6625 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6629 afterCommit : function(record){
6630 this.modified.remove(record);
6631 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6635 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6636 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6638 commitChanges : function(){
6639 var m = this.modified.slice(0);
6641 for(var i = 0, len = m.length; i < len; i++){
6647 * Cancel outstanding changes on all changed records.
6649 rejectChanges : function(){
6650 var m = this.modified.slice(0);
6652 for(var i = 0, len = m.length; i < len; i++){
6657 onMetaChange : function(meta, rtype, o){
6658 this.recordType = rtype;
6659 this.fields = rtype.prototype.fields;
6660 delete this.snapshot;
6661 this.sortInfo = meta.sortInfo || this.sortInfo;
6663 this.fireEvent('metachange', this, this.reader.meta);
6667 * Ext JS Library 1.1.1
6668 * Copyright(c) 2006-2007, Ext JS, LLC.
6670 * Originally Released Under LGPL - original licence link has changed is not relivant.
6673 * <script type="text/javascript">
6677 * @class Roo.data.SimpleStore
6678 * @extends Roo.data.Store
6679 * Small helper class to make creating Stores from Array data easier.
6680 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6681 * @cfg {Array} fields An array of field definition objects, or field name strings.
6682 * @cfg {Array} data The multi-dimensional array of data
6684 * @param {Object} config
6686 Roo.data.SimpleStore = function(config){
6687 Roo.data.SimpleStore.superclass.constructor.call(this, {
6689 reader: new Roo.data.ArrayReader({
6692 Roo.data.Record.create(config.fields)
6694 proxy : new Roo.data.MemoryProxy(config.data)
6698 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6700 * Ext JS Library 1.1.1
6701 * Copyright(c) 2006-2007, Ext JS, LLC.
6703 * Originally Released Under LGPL - original licence link has changed is not relivant.
6706 * <script type="text/javascript">
6711 * @extends Roo.data.Store
6712 * @class Roo.data.JsonStore
6713 * Small helper class to make creating Stores for JSON data easier. <br/>
6715 var store = new Roo.data.JsonStore({
6716 url: 'get-images.php',
6718 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6721 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6722 * JsonReader and HttpProxy (unless inline data is provided).</b>
6723 * @cfg {Array} fields An array of field definition objects, or field name strings.
6725 * @param {Object} config
6727 Roo.data.JsonStore = function(c){
6728 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6729 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6730 reader: new Roo.data.JsonReader(c, c.fields)
6733 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6735 * Ext JS Library 1.1.1
6736 * Copyright(c) 2006-2007, Ext JS, LLC.
6738 * Originally Released Under LGPL - original licence link has changed is not relivant.
6741 * <script type="text/javascript">
6745 Roo.data.Field = function(config){
6746 if(typeof config == "string"){
6747 config = {name: config};
6749 Roo.apply(this, config);
6755 var st = Roo.data.SortTypes;
6756 // named sortTypes are supported, here we look them up
6757 if(typeof this.sortType == "string"){
6758 this.sortType = st[this.sortType];
6761 // set default sortType for strings and dates
6765 this.sortType = st.asUCString;
6768 this.sortType = st.asDate;
6771 this.sortType = st.none;
6776 var stripRe = /[\$,%]/g;
6778 // prebuilt conversion function for this field, instead of
6779 // switching every time we're reading a value
6781 var cv, dateFormat = this.dateFormat;
6786 cv = function(v){ return v; };
6789 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6793 return v !== undefined && v !== null && v !== '' ?
6794 parseInt(String(v).replace(stripRe, ""), 10) : '';
6799 return v !== undefined && v !== null && v !== '' ?
6800 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6805 cv = function(v){ return v === true || v === "true" || v == 1; };
6812 if(v instanceof Date){
6816 if(dateFormat == "timestamp"){
6817 return new Date(v*1000);
6819 return Date.parseDate(v, dateFormat);
6821 var parsed = Date.parse(v);
6822 return parsed ? new Date(parsed) : null;
6831 Roo.data.Field.prototype = {
6839 * Ext JS Library 1.1.1
6840 * Copyright(c) 2006-2007, Ext JS, LLC.
6842 * Originally Released Under LGPL - original licence link has changed is not relivant.
6845 * <script type="text/javascript">
6848 // Base class for reading structured data from a data source. This class is intended to be
6849 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6852 * @class Roo.data.DataReader
6853 * Base class for reading structured data from a data source. This class is intended to be
6854 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6857 Roo.data.DataReader = function(meta, recordType){
6861 this.recordType = recordType instanceof Array ?
6862 Roo.data.Record.create(recordType) : recordType;
6865 Roo.data.DataReader.prototype = {
6867 * Create an empty record
6868 * @param {Object} data (optional) - overlay some values
6869 * @return {Roo.data.Record} record created.
6871 newRow : function(d) {
6873 this.recordType.prototype.fields.each(function(c) {
6875 case 'int' : da[c.name] = 0; break;
6876 case 'date' : da[c.name] = new Date(); break;
6877 case 'float' : da[c.name] = 0.0; break;
6878 case 'boolean' : da[c.name] = false; break;
6879 default : da[c.name] = ""; break;
6883 return new this.recordType(Roo.apply(da, d));
6888 * Ext JS Library 1.1.1
6889 * Copyright(c) 2006-2007, Ext JS, LLC.
6891 * Originally Released Under LGPL - original licence link has changed is not relivant.
6894 * <script type="text/javascript">
6898 * @class Roo.data.DataProxy
6899 * @extends Roo.data.Observable
6900 * This class is an abstract base class for implementations which provide retrieval of
6901 * unformatted data objects.<br>
6903 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
6904 * (of the appropriate type which knows how to parse the data object) to provide a block of
6905 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
6907 * Custom implementations must implement the load method as described in
6908 * {@link Roo.data.HttpProxy#load}.
6910 Roo.data.DataProxy = function(){
6914 * Fires before a network request is made to retrieve a data object.
6915 * @param {Object} This DataProxy object.
6916 * @param {Object} params The params parameter to the load function.
6921 * Fires before the load method's callback is called.
6922 * @param {Object} This DataProxy object.
6923 * @param {Object} o The data object.
6924 * @param {Object} arg The callback argument object passed to the load function.
6928 * @event loadexception
6929 * Fires if an Exception occurs during data retrieval.
6930 * @param {Object} This DataProxy object.
6931 * @param {Object} o The data object.
6932 * @param {Object} arg The callback argument object passed to the load function.
6933 * @param {Object} e The Exception.
6935 loadexception : true
6937 Roo.data.DataProxy.superclass.constructor.call(this);
6940 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
6943 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
6947 * Ext JS Library 1.1.1
6948 * Copyright(c) 2006-2007, Ext JS, LLC.
6950 * Originally Released Under LGPL - original licence link has changed is not relivant.
6953 * <script type="text/javascript">
6956 * @class Roo.data.MemoryProxy
6957 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
6958 * to the Reader when its load method is called.
6960 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
6962 Roo.data.MemoryProxy = function(data){
6966 Roo.data.MemoryProxy.superclass.constructor.call(this);
6970 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
6972 * Load data from the requested source (in this case an in-memory
6973 * data object passed to the constructor), read the data object into
6974 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
6975 * process that block using the passed callback.
6976 * @param {Object} params This parameter is not used by the MemoryProxy class.
6977 * @param {Roo.data.DataReader} reader The Reader object which converts the data
6978 * object into a block of Roo.data.Records.
6979 * @param {Function} callback The function into which to pass the block of Roo.data.records.
6980 * The function must be passed <ul>
6981 * <li>The Record block object</li>
6982 * <li>The "arg" argument from the load function</li>
6983 * <li>A boolean success indicator</li>
6985 * @param {Object} scope The scope in which to call the callback
6986 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
6988 load : function(params, reader, callback, scope, arg){
6989 params = params || {};
6992 result = reader.readRecords(this.data);
6994 this.fireEvent("loadexception", this, arg, null, e);
6995 callback.call(scope, null, arg, false);
6998 callback.call(scope, result, arg, true);
7002 update : function(params, records){
7007 * Ext JS Library 1.1.1
7008 * Copyright(c) 2006-2007, Ext JS, LLC.
7010 * Originally Released Under LGPL - original licence link has changed is not relivant.
7013 * <script type="text/javascript">
7016 * @class Roo.data.HttpProxy
7017 * @extends Roo.data.DataProxy
7018 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7019 * configured to reference a certain URL.<br><br>
7021 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7022 * from which the running page was served.<br><br>
7024 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7026 * Be aware that to enable the browser to parse an XML document, the server must set
7027 * the Content-Type header in the HTTP response to "text/xml".
7029 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7030 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7031 * will be used to make the request.
7033 Roo.data.HttpProxy = function(conn){
7034 Roo.data.HttpProxy.superclass.constructor.call(this);
7035 // is conn a conn config or a real conn?
7037 this.useAjax = !conn || !conn.events;
7041 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7042 // thse are take from connection...
7045 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7048 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7049 * extra parameters to each request made by this object. (defaults to undefined)
7052 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7053 * to each request made by this object. (defaults to undefined)
7056 * @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)
7059 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7062 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7068 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7072 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7073 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7074 * a finer-grained basis than the DataProxy events.
7076 getConnection : function(){
7077 return this.useAjax ? Roo.Ajax : this.conn;
7081 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7082 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7083 * process that block using the passed callback.
7084 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7085 * for the request to the remote server.
7086 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7087 * object into a block of Roo.data.Records.
7088 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7089 * The function must be passed <ul>
7090 * <li>The Record block object</li>
7091 * <li>The "arg" argument from the load function</li>
7092 * <li>A boolean success indicator</li>
7094 * @param {Object} scope The scope in which to call the callback
7095 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7097 load : function(params, reader, callback, scope, arg){
7098 if(this.fireEvent("beforeload", this, params) !== false){
7100 params : params || {},
7102 callback : callback,
7107 callback : this.loadResponse,
7111 Roo.applyIf(o, this.conn);
7112 if(this.activeRequest){
7113 Roo.Ajax.abort(this.activeRequest);
7115 this.activeRequest = Roo.Ajax.request(o);
7117 this.conn.request(o);
7120 callback.call(scope||this, null, arg, false);
7125 loadResponse : function(o, success, response){
7126 delete this.activeRequest;
7128 this.fireEvent("loadexception", this, o, response);
7129 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7134 result = o.reader.read(response);
7136 this.fireEvent("loadexception", this, o, response, e);
7137 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7141 this.fireEvent("load", this, o, o.request.arg);
7142 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7146 update : function(dataSet){
7151 updateResponse : function(dataSet){
7156 * Ext JS Library 1.1.1
7157 * Copyright(c) 2006-2007, Ext JS, LLC.
7159 * Originally Released Under LGPL - original licence link has changed is not relivant.
7162 * <script type="text/javascript">
7166 * @class Roo.data.ScriptTagProxy
7167 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7168 * other than the originating domain of the running page.<br><br>
7170 * <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
7171 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7173 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7174 * source code that is used as the source inside a <script> tag.<br><br>
7176 * In order for the browser to process the returned data, the server must wrap the data object
7177 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7178 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7179 * depending on whether the callback name was passed:
7182 boolean scriptTag = false;
7183 String cb = request.getParameter("callback");
7186 response.setContentType("text/javascript");
7188 response.setContentType("application/x-json");
7190 Writer out = response.getWriter();
7192 out.write(cb + "(");
7194 out.print(dataBlock.toJsonString());
7201 * @param {Object} config A configuration object.
7203 Roo.data.ScriptTagProxy = function(config){
7204 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7205 Roo.apply(this, config);
7206 this.head = document.getElementsByTagName("head")[0];
7209 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7211 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7213 * @cfg {String} url The URL from which to request the data object.
7216 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7220 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7221 * the server the name of the callback function set up by the load call to process the returned data object.
7222 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7223 * javascript output which calls this named function passing the data object as its only parameter.
7225 callbackParam : "callback",
7227 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7228 * name to the request.
7233 * Load data from the configured URL, read the data object into
7234 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7235 * process that block using the passed callback.
7236 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7237 * for the request to the remote server.
7238 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7239 * object into a block of Roo.data.Records.
7240 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7241 * The function must be passed <ul>
7242 * <li>The Record block object</li>
7243 * <li>The "arg" argument from the load function</li>
7244 * <li>A boolean success indicator</li>
7246 * @param {Object} scope The scope in which to call the callback
7247 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7249 load : function(params, reader, callback, scope, arg){
7250 if(this.fireEvent("beforeload", this, params) !== false){
7252 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7255 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7257 url += "&_dc=" + (new Date().getTime());
7259 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7262 cb : "stcCallback"+transId,
7263 scriptId : "stcScript"+transId,
7267 callback : callback,
7273 window[trans.cb] = function(o){
7274 conn.handleResponse(o, trans);
7277 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7279 if(this.autoAbort !== false){
7283 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7285 var script = document.createElement("script");
7286 script.setAttribute("src", url);
7287 script.setAttribute("type", "text/javascript");
7288 script.setAttribute("id", trans.scriptId);
7289 this.head.appendChild(script);
7293 callback.call(scope||this, null, arg, false);
7298 isLoading : function(){
7299 return this.trans ? true : false;
7303 * Abort the current server request.
7306 if(this.isLoading()){
7307 this.destroyTrans(this.trans);
7312 destroyTrans : function(trans, isLoaded){
7313 this.head.removeChild(document.getElementById(trans.scriptId));
7314 clearTimeout(trans.timeoutId);
7316 window[trans.cb] = undefined;
7318 delete window[trans.cb];
7321 // if hasn't been loaded, wait for load to remove it to prevent script error
7322 window[trans.cb] = function(){
7323 window[trans.cb] = undefined;
7325 delete window[trans.cb];
7332 handleResponse : function(o, trans){
7334 this.destroyTrans(trans, true);
7337 result = trans.reader.readRecords(o);
7339 this.fireEvent("loadexception", this, o, trans.arg, e);
7340 trans.callback.call(trans.scope||window, null, trans.arg, false);
7343 this.fireEvent("load", this, o, trans.arg);
7344 trans.callback.call(trans.scope||window, result, trans.arg, true);
7348 handleFailure : function(trans){
7350 this.destroyTrans(trans, false);
7351 this.fireEvent("loadexception", this, null, trans.arg);
7352 trans.callback.call(trans.scope||window, null, trans.arg, false);
7356 * Ext JS Library 1.1.1
7357 * Copyright(c) 2006-2007, Ext JS, LLC.
7359 * Originally Released Under LGPL - original licence link has changed is not relivant.
7362 * <script type="text/javascript">
7366 * @class Roo.data.JsonReader
7367 * @extends Roo.data.DataReader
7368 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7369 * based on mappings in a provided Roo.data.Record constructor.
7371 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7372 * in the reply previously.
7377 var RecordDef = Roo.data.Record.create([
7378 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7379 {name: 'occupation'} // This field will use "occupation" as the mapping.
7381 var myReader = new Roo.data.JsonReader({
7382 totalProperty: "results", // The property which contains the total dataset size (optional)
7383 root: "rows", // The property which contains an Array of row objects
7384 id: "id" // The property within each row object that provides an ID for the record (optional)
7388 * This would consume a JSON file like this:
7390 { 'results': 2, 'rows': [
7391 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7392 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7395 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7396 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7397 * paged from the remote server.
7398 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7399 * @cfg {String} root name of the property which contains the Array of row objects.
7400 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7402 * Create a new JsonReader
7403 * @param {Object} meta Metadata configuration options
7404 * @param {Object} recordType Either an Array of field definition objects,
7405 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7407 Roo.data.JsonReader = function(meta, recordType){
7410 // set some defaults:
7412 totalProperty: 'total',
7413 successProperty : 'success',
7418 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7420 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7423 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7424 * Used by Store query builder to append _requestMeta to params.
7427 metaFromRemote : false,
7429 * This method is only used by a DataProxy which has retrieved data from a remote server.
7430 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7431 * @return {Object} data A data block which is used by an Roo.data.Store object as
7432 * a cache of Roo.data.Records.
7434 read : function(response){
7435 var json = response.responseText;
7437 var o = /* eval:var:o */ eval("("+json+")");
7439 throw {message: "JsonReader.read: Json object not found"};
7445 this.metaFromRemote = true;
7446 this.meta = o.metaData;
7447 this.recordType = Roo.data.Record.create(o.metaData.fields);
7448 this.onMetaChange(this.meta, this.recordType, o);
7450 return this.readRecords(o);
7453 // private function a store will implement
7454 onMetaChange : function(meta, recordType, o){
7461 simpleAccess: function(obj, subsc) {
7468 getJsonAccessor: function(){
7470 return function(expr) {
7472 return(re.test(expr))
7473 ? new Function("obj", "return obj." + expr)
7483 * Create a data block containing Roo.data.Records from an XML document.
7484 * @param {Object} o An object which contains an Array of row objects in the property specified
7485 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7486 * which contains the total size of the dataset.
7487 * @return {Object} data A data block which is used by an Roo.data.Store object as
7488 * a cache of Roo.data.Records.
7490 readRecords : function(o){
7492 * After any data loads, the raw JSON data is available for further custom processing.
7496 var s = this.meta, Record = this.recordType,
7497 f = Record.prototype.fields, fi = f.items, fl = f.length;
7499 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7501 if(s.totalProperty) {
7502 this.getTotal = this.getJsonAccessor(s.totalProperty);
7504 if(s.successProperty) {
7505 this.getSuccess = this.getJsonAccessor(s.successProperty);
7507 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7509 var g = this.getJsonAccessor(s.id);
7510 this.getId = function(rec) {
7512 return (r === undefined || r === "") ? null : r;
7515 this.getId = function(){return null;};
7518 for(var jj = 0; jj < fl; jj++){
7520 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7521 this.ef[jj] = this.getJsonAccessor(map);
7525 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7526 if(s.totalProperty){
7527 var vt = parseInt(this.getTotal(o), 10);
7532 if(s.successProperty){
7533 var vs = this.getSuccess(o);
7534 if(vs === false || vs === 'false'){
7539 for(var i = 0; i < c; i++){
7542 var id = this.getId(n);
7543 for(var j = 0; j < fl; j++){
7545 var v = this.ef[j](n);
7547 Roo.log('missing convert for ' + f.name);
7551 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7553 var record = new Record(values, id);
7555 records[i] = record;
7561 totalRecords : totalRecords
7566 * Ext JS Library 1.1.1
7567 * Copyright(c) 2006-2007, Ext JS, LLC.
7569 * Originally Released Under LGPL - original licence link has changed is not relivant.
7572 * <script type="text/javascript">
7576 * @class Roo.data.ArrayReader
7577 * @extends Roo.data.DataReader
7578 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7579 * Each element of that Array represents a row of data fields. The
7580 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7581 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7585 var RecordDef = Roo.data.Record.create([
7586 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7587 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7589 var myReader = new Roo.data.ArrayReader({
7590 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7594 * This would consume an Array like this:
7596 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7598 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7600 * Create a new JsonReader
7601 * @param {Object} meta Metadata configuration options.
7602 * @param {Object} recordType Either an Array of field definition objects
7603 * as specified to {@link Roo.data.Record#create},
7604 * or an {@link Roo.data.Record} object
7605 * created using {@link Roo.data.Record#create}.
7607 Roo.data.ArrayReader = function(meta, recordType){
7608 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7611 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7613 * Create a data block containing Roo.data.Records from an XML document.
7614 * @param {Object} o An Array of row objects which represents the dataset.
7615 * @return {Object} data A data block which is used by an Roo.data.Store object as
7616 * a cache of Roo.data.Records.
7618 readRecords : function(o){
7619 var sid = this.meta ? this.meta.id : null;
7620 var recordType = this.recordType, fields = recordType.prototype.fields;
7623 for(var i = 0; i < root.length; i++){
7626 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7627 for(var j = 0, jlen = fields.length; j < jlen; j++){
7628 var f = fields.items[j];
7629 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7630 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7634 var record = new recordType(values, id);
7636 records[records.length] = record;
7640 totalRecords : records.length
7649 * @class Roo.bootstrap.ComboBox
7650 * @extends Roo.bootstrap.TriggerField
7651 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7652 * @cfg {Boolean} append (true|false) default false
7654 * Create a new ComboBox.
7655 * @param {Object} config Configuration options
7657 Roo.bootstrap.ComboBox = function(config){
7658 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7662 * Fires when the dropdown list is expanded
7663 * @param {Roo.bootstrap.ComboBox} combo This combo box
7668 * Fires when the dropdown list is collapsed
7669 * @param {Roo.bootstrap.ComboBox} combo This combo box
7673 * @event beforeselect
7674 * Fires before a list item is selected. Return false to cancel the selection.
7675 * @param {Roo.bootstrap.ComboBox} combo This combo box
7676 * @param {Roo.data.Record} record The data record returned from the underlying store
7677 * @param {Number} index The index of the selected item in the dropdown list
7679 'beforeselect' : true,
7682 * Fires when a list item is selected
7683 * @param {Roo.bootstrap.ComboBox} combo This combo box
7684 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7685 * @param {Number} index The index of the selected item in the dropdown list
7689 * @event beforequery
7690 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7691 * The event object passed has these properties:
7692 * @param {Roo.bootstrap.ComboBox} combo This combo box
7693 * @param {String} query The query
7694 * @param {Boolean} forceAll true to force "all" query
7695 * @param {Boolean} cancel true to cancel the query
7696 * @param {Object} e The query event object
7698 'beforequery': true,
7701 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7702 * @param {Roo.bootstrap.ComboBox} combo This combo box
7707 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7708 * @param {Roo.bootstrap.ComboBox} combo This combo box
7709 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7714 * Fires when the remove value from the combobox array
7715 * @param {Roo.bootstrap.ComboBox} combo This combo box
7722 this.selectedIndex = -1;
7723 if(this.mode == 'local'){
7724 if(config.queryDelay === undefined){
7725 this.queryDelay = 10;
7727 if(config.minChars === undefined){
7733 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7736 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7737 * rendering into an Roo.Editor, defaults to false)
7740 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7741 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7744 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7747 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7748 * the dropdown list (defaults to undefined, with no header element)
7752 * @cfg {String/Roo.Template} tpl The template to use to render the output
7756 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7758 listWidth: undefined,
7760 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7761 * mode = 'remote' or 'text' if mode = 'local')
7763 displayField: undefined,
7765 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7766 * mode = 'remote' or 'value' if mode = 'local').
7767 * Note: use of a valueField requires the user make a selection
7768 * in order for a value to be mapped.
7770 valueField: undefined,
7774 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7775 * field's data value (defaults to the underlying DOM element's name)
7777 hiddenName: undefined,
7779 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7783 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7785 selectedClass: 'active',
7788 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7792 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7793 * anchor positions (defaults to 'tl-bl')
7795 listAlign: 'tl-bl?',
7797 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7801 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7802 * query specified by the allQuery config option (defaults to 'query')
7804 triggerAction: 'query',
7806 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7807 * (defaults to 4, does not apply if editable = false)
7811 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7812 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7816 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7817 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7821 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7822 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7826 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7827 * when editable = true (defaults to false)
7829 selectOnFocus:false,
7831 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7833 queryParam: 'query',
7835 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7836 * when mode = 'remote' (defaults to 'Loading...')
7838 loadingText: 'Loading...',
7840 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7844 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7848 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7849 * traditional select (defaults to true)
7853 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7857 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7861 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7862 * listWidth has a higher value)
7866 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7867 * allow the user to set arbitrary text into the field (defaults to false)
7869 forceSelection:false,
7871 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7872 * if typeAhead = true (defaults to 250)
7874 typeAheadDelay : 250,
7876 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7877 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7879 valueNotFoundText : undefined,
7881 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7886 * @cfg {Boolean} disableClear Disable showing of clear button.
7888 disableClear : false,
7890 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
7892 alwaysQuery : false,
7895 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
7909 // element that contains real text value.. (when hidden is used..)
7912 initEvents: function(){
7915 throw "can not find store for combo";
7917 this.store = Roo.factory(this.store, Roo.data);
7921 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
7924 if(this.hiddenName){
7926 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
7928 this.hiddenField.dom.value =
7929 this.hiddenValue !== undefined ? this.hiddenValue :
7930 this.value !== undefined ? this.value : '';
7932 // prevent input submission
7933 this.el.dom.removeAttribute('name');
7934 this.hiddenField.dom.setAttribute('name', this.hiddenName);
7939 // this.el.dom.setAttribute('autocomplete', 'off');
7942 var cls = 'x-combo-list';
7943 this.list = this.el.select('ul.dropdown-menu',true).first();
7945 //this.list = new Roo.Layer({
7946 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
7949 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
7950 this.list.setWidth(lw);
7952 this.list.on('mouseover', this.onViewOver, this);
7953 this.list.on('mousemove', this.onViewMove, this);
7955 this.list.on('scroll', this.onViewScroll, this);
7958 this.list.swallowEvent('mousewheel');
7959 this.assetHeight = 0;
7962 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
7963 this.assetHeight += this.header.getHeight();
7966 this.innerList = this.list.createChild({cls:cls+'-inner'});
7967 this.innerList.on('mouseover', this.onViewOver, this);
7968 this.innerList.on('mousemove', this.onViewMove, this);
7969 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
7971 if(this.allowBlank && !this.pageSize && !this.disableClear){
7972 this.footer = this.list.createChild({cls:cls+'-ft'});
7973 this.pageTb = new Roo.Toolbar(this.footer);
7977 this.footer = this.list.createChild({cls:cls+'-ft'});
7978 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
7979 {pageSize: this.pageSize});
7983 if (this.pageTb && this.allowBlank && !this.disableClear) {
7985 this.pageTb.add(new Roo.Toolbar.Fill(), {
7986 cls: 'x-btn-icon x-btn-clear',
7992 _this.onSelect(false, -1);
7997 this.assetHeight += this.footer.getHeight();
8002 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8005 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8006 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8008 //this.view.wrapEl.setDisplayed(false);
8009 this.view.on('click', this.onViewClick, this);
8013 this.store.on('beforeload', this.onBeforeLoad, this);
8014 this.store.on('load', this.onLoad, this);
8015 this.store.on('loadexception', this.onLoadException, this);
8018 this.resizer = new Roo.Resizable(this.list, {
8019 pinned:true, handles:'se'
8021 this.resizer.on('resize', function(r, w, h){
8022 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8024 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8025 this.restrictHeight();
8027 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8031 this.editable = true;
8032 this.setEditable(false);
8037 if (typeof(this.events.add.listeners) != 'undefined') {
8039 this.addicon = this.wrap.createChild(
8040 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8042 this.addicon.on('click', function(e) {
8043 this.fireEvent('add', this);
8046 if (typeof(this.events.edit.listeners) != 'undefined') {
8048 this.editicon = this.wrap.createChild(
8049 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8051 this.editicon.setStyle('margin-left', '40px');
8053 this.editicon.on('click', function(e) {
8055 // we fire even if inothing is selected..
8056 this.fireEvent('edit', this, this.lastData );
8062 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8064 this.inKeyMode = true;
8068 "down" : function(e){
8069 if(!this.isExpanded()){
8070 this.onTriggerClick();
8072 this.inKeyMode = true;
8077 "enter" : function(e){
8082 "esc" : function(e){
8086 "tab" : function(e){
8089 if(this.fireEvent("specialkey", this, e)){
8090 this.onViewClick(false);
8098 doRelay : function(foo, bar, hname){
8099 if(hname == 'down' || this.scope.isExpanded()){
8100 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8109 this.queryDelay = Math.max(this.queryDelay || 10,
8110 this.mode == 'local' ? 10 : 250);
8113 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8116 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8118 if(this.editable !== false){
8119 this.inputEl().on("keyup", this.onKeyUp, this);
8121 if(this.forceSelection){
8122 this.on('blur', this.doForce, this);
8126 this.choices = this.el.select('ul.select2-choices', true).first();
8127 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8131 onDestroy : function(){
8133 this.view.setStore(null);
8134 this.view.el.removeAllListeners();
8135 this.view.el.remove();
8136 this.view.purgeListeners();
8139 this.list.dom.innerHTML = '';
8142 this.store.un('beforeload', this.onBeforeLoad, this);
8143 this.store.un('load', this.onLoad, this);
8144 this.store.un('loadexception', this.onLoadException, this);
8146 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8150 fireKey : function(e){
8151 if(e.isNavKeyPress() && !this.list.isVisible()){
8152 this.fireEvent("specialkey", this, e);
8157 onResize: function(w, h){
8158 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8160 // if(typeof w != 'number'){
8161 // // we do not handle it!?!?
8164 // var tw = this.trigger.getWidth();
8165 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8166 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8168 // this.inputEl().setWidth( this.adjustWidth('input', x));
8170 // //this.trigger.setStyle('left', x+'px');
8172 // if(this.list && this.listWidth === undefined){
8173 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8174 // this.list.setWidth(lw);
8175 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8183 * Allow or prevent the user from directly editing the field text. If false is passed,
8184 * the user will only be able to select from the items defined in the dropdown list. This method
8185 * is the runtime equivalent of setting the 'editable' config option at config time.
8186 * @param {Boolean} value True to allow the user to directly edit the field text
8188 setEditable : function(value){
8189 if(value == this.editable){
8192 this.editable = value;
8194 this.inputEl().dom.setAttribute('readOnly', true);
8195 this.inputEl().on('mousedown', this.onTriggerClick, this);
8196 this.inputEl().addClass('x-combo-noedit');
8198 this.inputEl().dom.setAttribute('readOnly', false);
8199 this.inputEl().un('mousedown', this.onTriggerClick, this);
8200 this.inputEl().removeClass('x-combo-noedit');
8206 onBeforeLoad : function(combo,opts){
8211 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8213 this.restrictHeight();
8214 this.selectedIndex = -1;
8218 onLoad : function(){
8220 this.hasQuery = false;
8226 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8227 this.loading.hide();
8230 if(this.store.getCount() > 0){
8232 this.restrictHeight();
8233 if(this.lastQuery == this.allQuery){
8235 this.inputEl().dom.select();
8237 if(!this.selectByValue(this.value, true)){
8238 this.select(0, true);
8242 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8243 this.taTask.delay(this.typeAheadDelay);
8247 this.onEmptyResults();
8253 onLoadException : function()
8255 this.hasQuery = false;
8257 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8258 this.loading.hide();
8262 Roo.log(this.store.reader.jsonData);
8263 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8265 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8271 onTypeAhead : function(){
8272 if(this.store.getCount() > 0){
8273 var r = this.store.getAt(0);
8274 var newValue = r.data[this.displayField];
8275 var len = newValue.length;
8276 var selStart = this.getRawValue().length;
8278 if(selStart != len){
8279 this.setRawValue(newValue);
8280 this.selectText(selStart, newValue.length);
8286 onSelect : function(record, index){
8288 if(this.fireEvent('beforeselect', this, record, index) !== false){
8290 this.setFromData(index > -1 ? record.data : false);
8293 this.fireEvent('select', this, record, index);
8298 * Returns the currently selected field value or empty string if no value is set.
8299 * @return {String} value The selected value
8301 getValue : function(){
8304 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8307 if(this.valueField){
8308 return typeof this.value != 'undefined' ? this.value : '';
8310 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8315 * Clears any text/value currently set in the field
8317 clearValue : function(){
8318 if(this.hiddenField){
8319 this.hiddenField.dom.value = '';
8322 this.setRawValue('');
8323 this.lastSelectionText = '';
8328 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8329 * will be displayed in the field. If the value does not match the data value of an existing item,
8330 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8331 * Otherwise the field will be blank (although the value will still be set).
8332 * @param {String} value The value to match
8334 setValue : function(v){
8341 if(this.valueField){
8342 var r = this.findRecord(this.valueField, v);
8344 text = r.data[this.displayField];
8345 }else if(this.valueNotFoundText !== undefined){
8346 text = this.valueNotFoundText;
8349 this.lastSelectionText = text;
8350 if(this.hiddenField){
8351 this.hiddenField.dom.value = v;
8353 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8357 * @property {Object} the last set data for the element
8362 * Sets the value of the field based on a object which is related to the record format for the store.
8363 * @param {Object} value the value to set as. or false on reset?
8365 setFromData : function(o){
8372 var dv = ''; // display value
8373 var vv = ''; // value value..
8375 if (this.displayField) {
8376 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8378 // this is an error condition!!!
8379 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8382 if(this.valueField){
8383 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8386 if(this.hiddenField){
8387 this.hiddenField.dom.value = vv;
8389 this.lastSelectionText = dv;
8390 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8394 // no hidden field.. - we store the value in 'value', but still display
8395 // display field!!!!
8396 this.lastSelectionText = dv;
8397 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8404 // overridden so that last data is reset..
8405 this.setValue(this.originalValue);
8406 this.clearInvalid();
8407 this.lastData = false;
8409 this.view.clearSelections();
8413 findRecord : function(prop, value){
8415 if(this.store.getCount() > 0){
8416 this.store.each(function(r){
8417 if(r.data[prop] == value){
8429 // returns hidden if it's set..
8430 if (!this.rendered) {return ''};
8431 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8435 onViewMove : function(e, t){
8436 this.inKeyMode = false;
8440 onViewOver : function(e, t){
8441 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8444 var item = this.view.findItemFromChild(t);
8446 var index = this.view.indexOf(item);
8447 this.select(index, false);
8452 onViewClick : function(doFocus)
8454 var index = this.view.getSelectedIndexes()[0];
8455 var r = this.store.getAt(index);
8457 this.onSelect(r, index);
8459 if(doFocus !== false && !this.blockFocus){
8460 this.inputEl().focus();
8465 restrictHeight : function(){
8466 //this.innerList.dom.style.height = '';
8467 //var inner = this.innerList.dom;
8468 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8469 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8470 //this.list.beginUpdate();
8471 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8472 this.list.alignTo(this.inputEl(), this.listAlign);
8473 //this.list.endUpdate();
8477 onEmptyResults : function(){
8482 * Returns true if the dropdown list is expanded, else false.
8484 isExpanded : function(){
8485 return this.list.isVisible();
8489 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8490 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8491 * @param {String} value The data value of the item to select
8492 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8493 * selected item if it is not currently in view (defaults to true)
8494 * @return {Boolean} True if the value matched an item in the list, else false
8496 selectByValue : function(v, scrollIntoView){
8497 if(v !== undefined && v !== null){
8498 var r = this.findRecord(this.valueField || this.displayField, v);
8500 this.select(this.store.indexOf(r), scrollIntoView);
8508 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8509 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8510 * @param {Number} index The zero-based index of the list item to select
8511 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8512 * selected item if it is not currently in view (defaults to true)
8514 select : function(index, scrollIntoView){
8515 this.selectedIndex = index;
8516 this.view.select(index);
8517 if(scrollIntoView !== false){
8518 var el = this.view.getNode(index);
8520 //this.innerList.scrollChildIntoView(el, false);
8527 selectNext : function(){
8528 var ct = this.store.getCount();
8530 if(this.selectedIndex == -1){
8532 }else if(this.selectedIndex < ct-1){
8533 this.select(this.selectedIndex+1);
8539 selectPrev : function(){
8540 var ct = this.store.getCount();
8542 if(this.selectedIndex == -1){
8544 }else if(this.selectedIndex != 0){
8545 this.select(this.selectedIndex-1);
8551 onKeyUp : function(e){
8552 if(this.editable !== false && !e.isSpecialKey()){
8553 this.lastKey = e.getKey();
8554 this.dqTask.delay(this.queryDelay);
8559 validateBlur : function(){
8560 return !this.list || !this.list.isVisible();
8564 initQuery : function(){
8565 this.doQuery(this.getRawValue());
8569 doForce : function(){
8570 if(this.el.dom.value.length > 0){
8572 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8578 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8579 * query allowing the query action to be canceled if needed.
8580 * @param {String} query The SQL query to execute
8581 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8582 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8583 * saved in the current store (defaults to false)
8585 doQuery : function(q, forceAll){
8587 if(q === undefined || q === null){
8596 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8601 forceAll = qe.forceAll;
8602 if(forceAll === true || (q.length >= this.minChars)){
8604 this.hasQuery = true;
8606 if(this.lastQuery != q || this.alwaysQuery){
8608 if(this.mode == 'local'){
8609 this.selectedIndex = -1;
8611 this.store.clearFilter();
8613 this.store.filter(this.displayField, q);
8617 this.store.baseParams[this.queryParam] = q;
8619 var options = {params : this.getParams(q)};
8623 options.params.start = this.page * this.pageSize;
8626 this.store.load(options);
8630 this.selectedIndex = -1;
8635 this.loadNext = false;
8639 getParams : function(q){
8641 //p[this.queryParam] = q;
8645 p.limit = this.pageSize;
8651 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8653 collapse : function(){
8654 if(!this.isExpanded()){
8659 Roo.get(document).un('mousedown', this.collapseIf, this);
8660 Roo.get(document).un('mousewheel', this.collapseIf, this);
8661 if (!this.editable) {
8662 Roo.get(document).un('keydown', this.listKeyPress, this);
8664 this.fireEvent('collapse', this);
8668 collapseIf : function(e){
8669 var in_combo = e.within(this.el);
8670 var in_list = e.within(this.list);
8672 if (in_combo || in_list) {
8673 //e.stopPropagation();
8682 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8684 expand : function(){
8686 if(this.isExpanded() || !this.hasFocus){
8690 this.list.alignTo(this.inputEl(), this.listAlign);
8692 Roo.get(document).on('mousedown', this.collapseIf, this);
8693 Roo.get(document).on('mousewheel', this.collapseIf, this);
8694 if (!this.editable) {
8695 Roo.get(document).on('keydown', this.listKeyPress, this);
8698 this.fireEvent('expand', this);
8702 // Implements the default empty TriggerField.onTriggerClick function
8703 onTriggerClick : function()
8705 Roo.log('trigger click');
8712 this.loadNext = false;
8714 if(this.isExpanded()){
8716 if (!this.blockFocus) {
8717 this.inputEl().focus();
8721 this.hasFocus = true;
8722 if(this.triggerAction == 'all') {
8723 this.doQuery(this.allQuery, true);
8725 this.doQuery(this.getRawValue());
8727 if (!this.blockFocus) {
8728 this.inputEl().focus();
8732 listKeyPress : function(e)
8734 //Roo.log('listkeypress');
8735 // scroll to first matching element based on key pres..
8736 if (e.isSpecialKey()) {
8739 var k = String.fromCharCode(e.getKey()).toUpperCase();
8742 var csel = this.view.getSelectedNodes();
8743 var cselitem = false;
8745 var ix = this.view.indexOf(csel[0]);
8746 cselitem = this.store.getAt(ix);
8747 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8753 this.store.each(function(v) {
8755 // start at existing selection.
8756 if (cselitem.id == v.id) {
8762 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8763 match = this.store.indexOf(v);
8769 if (match === false) {
8770 return true; // no more action?
8773 this.view.select(match);
8774 var sn = Roo.get(this.view.getSelectedNodes()[0])
8775 //sn.scrollIntoView(sn.dom.parentNode, false);
8778 onViewScroll : function(e, t){
8780 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8784 this.hasQuery = true;
8786 this.loading = this.list.select('.loading', true).first();
8788 if(this.loading === null){
8789 this.list.createChild({
8791 cls: 'loading select2-more-results select2-active',
8792 html: 'Loading more results...'
8795 this.loading = this.list.select('.loading', true).first();
8797 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8799 this.loading.hide();
8802 this.loading.show();
8807 this.loadNext = true;
8809 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8814 addItem : function(o)
8816 var dv = ''; // display value
8818 if (this.displayField) {
8819 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8821 // this is an error condition!!!
8822 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8829 var choice = this.choices.createChild({
8831 cls: 'select2-search-choice',
8840 cls: 'select2-search-choice-close',
8845 }, this.searchField);
8847 var close = choice.select('a.select2-search-choice-close', true).first()
8849 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8856 this.inputEl().dom.value = '';
8860 onRemoveItem : function(e, _self, o)
8862 Roo.log('remove item');
8863 var index = this.item.indexOf(o.data) * 1;
8866 Roo.log('not this item?!');
8870 this.item.splice(index, 1);
8875 this.fireEvent('remove', this);
8879 syncValue : function()
8881 if(!this.item.length){
8888 Roo.each(this.item, function(i){
8889 if(_this.valueField){
8890 value.push(i[_this.valueField]);
8897 this.value = value.join(',');
8899 if(this.hiddenField){
8900 this.hiddenField.dom.value = this.value;
8905 * @cfg {Boolean} grow
8909 * @cfg {Number} growMin
8913 * @cfg {Number} growMax
8923 * Ext JS Library 1.1.1
8924 * Copyright(c) 2006-2007, Ext JS, LLC.
8926 * Originally Released Under LGPL - original licence link has changed is not relivant.
8929 * <script type="text/javascript">
8934 * @extends Roo.util.Observable
8935 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
8936 * This class also supports single and multi selection modes. <br>
8937 * Create a data model bound view:
8939 var store = new Roo.data.Store(...);
8941 var view = new Roo.View({
8943 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
8946 selectedClass: "ydataview-selected",
8950 // listen for node click?
8951 view.on("click", function(vw, index, node, e){
8952 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
8956 dataModel.load("foobar.xml");
8958 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
8960 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
8961 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
8963 * Note: old style constructor is still suported (container, template, config)
8967 * @param {Object} config The config object
8970 Roo.View = function(config, depreciated_tpl, depreciated_config){
8972 if (typeof(depreciated_tpl) == 'undefined') {
8973 // new way.. - universal constructor.
8974 Roo.apply(this, config);
8975 this.el = Roo.get(this.el);
8978 this.el = Roo.get(config);
8979 this.tpl = depreciated_tpl;
8980 Roo.apply(this, depreciated_config);
8982 this.wrapEl = this.el.wrap().wrap();
8983 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
8986 if(typeof(this.tpl) == "string"){
8987 this.tpl = new Roo.Template(this.tpl);
8989 // support xtype ctors..
8990 this.tpl = new Roo.factory(this.tpl, Roo);
9002 * @event beforeclick
9003 * Fires before a click is processed. Returns false to cancel the default action.
9004 * @param {Roo.View} this
9005 * @param {Number} index The index of the target node
9006 * @param {HTMLElement} node The target node
9007 * @param {Roo.EventObject} e The raw event object
9009 "beforeclick" : true,
9012 * Fires when a template node is clicked.
9013 * @param {Roo.View} this
9014 * @param {Number} index The index of the target node
9015 * @param {HTMLElement} node The target node
9016 * @param {Roo.EventObject} e The raw event object
9021 * Fires when a template node is double clicked.
9022 * @param {Roo.View} this
9023 * @param {Number} index The index of the target node
9024 * @param {HTMLElement} node The target node
9025 * @param {Roo.EventObject} e The raw event object
9029 * @event contextmenu
9030 * Fires when a template node is right clicked.
9031 * @param {Roo.View} this
9032 * @param {Number} index The index of the target node
9033 * @param {HTMLElement} node The target node
9034 * @param {Roo.EventObject} e The raw event object
9036 "contextmenu" : true,
9038 * @event selectionchange
9039 * Fires when the selected nodes change.
9040 * @param {Roo.View} this
9041 * @param {Array} selections Array of the selected nodes
9043 "selectionchange" : true,
9046 * @event beforeselect
9047 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9048 * @param {Roo.View} this
9049 * @param {HTMLElement} node The node to be selected
9050 * @param {Array} selections Array of currently selected nodes
9052 "beforeselect" : true,
9054 * @event preparedata
9055 * Fires on every row to render, to allow you to change the data.
9056 * @param {Roo.View} this
9057 * @param {Object} data to be rendered (change this)
9059 "preparedata" : true
9067 "click": this.onClick,
9068 "dblclick": this.onDblClick,
9069 "contextmenu": this.onContextMenu,
9073 this.selections = [];
9075 this.cmp = new Roo.CompositeElementLite([]);
9077 this.store = Roo.factory(this.store, Roo.data);
9078 this.setStore(this.store, true);
9081 if ( this.footer && this.footer.xtype) {
9083 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9085 this.footer.dataSource = this.store
9086 this.footer.container = fctr;
9087 this.footer = Roo.factory(this.footer, Roo);
9088 fctr.insertFirst(this.el);
9090 // this is a bit insane - as the paging toolbar seems to detach the el..
9091 // dom.parentNode.parentNode.parentNode
9092 // they get detached?
9096 Roo.View.superclass.constructor.call(this);
9101 Roo.extend(Roo.View, Roo.util.Observable, {
9104 * @cfg {Roo.data.Store} store Data store to load data from.
9109 * @cfg {String|Roo.Element} el The container element.
9114 * @cfg {String|Roo.Template} tpl The template used by this View
9118 * @cfg {String} dataName the named area of the template to use as the data area
9119 * Works with domtemplates roo-name="name"
9123 * @cfg {String} selectedClass The css class to add to selected nodes
9125 selectedClass : "x-view-selected",
9127 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9132 * @cfg {String} text to display on mask (default Loading)
9136 * @cfg {Boolean} multiSelect Allow multiple selection
9138 multiSelect : false,
9140 * @cfg {Boolean} singleSelect Allow single selection
9142 singleSelect: false,
9145 * @cfg {Boolean} toggleSelect - selecting
9147 toggleSelect : false,
9150 * Returns the element this view is bound to.
9151 * @return {Roo.Element}
9160 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9162 refresh : function(){
9166 // if we are using something like 'domtemplate', then
9167 // the what gets used is:
9168 // t.applySubtemplate(NAME, data, wrapping data..)
9169 // the outer template then get' applied with
9170 // the store 'extra data'
9171 // and the body get's added to the
9172 // roo-name="data" node?
9173 // <span class='roo-tpl-{name}'></span> ?????
9177 this.clearSelections();
9180 var records = this.store.getRange();
9181 if(records.length < 1) {
9183 // is this valid?? = should it render a template??
9185 this.el.update(this.emptyText);
9189 if (this.dataName) {
9190 this.el.update(t.apply(this.store.meta)); //????
9191 el = this.el.child('.roo-tpl-' + this.dataName);
9194 for(var i = 0, len = records.length; i < len; i++){
9195 var data = this.prepareData(records[i].data, i, records[i]);
9196 this.fireEvent("preparedata", this, data, i, records[i]);
9197 html[html.length] = Roo.util.Format.trim(
9199 t.applySubtemplate(this.dataName, data, this.store.meta) :
9206 el.update(html.join(""));
9207 this.nodes = el.dom.childNodes;
9208 this.updateIndexes(0);
9213 * Function to override to reformat the data that is sent to
9214 * the template for each node.
9215 * DEPRICATED - use the preparedata event handler.
9216 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9217 * a JSON object for an UpdateManager bound view).
9219 prepareData : function(data, index, record)
9221 this.fireEvent("preparedata", this, data, index, record);
9225 onUpdate : function(ds, record){
9226 Roo.log('on update');
9227 this.clearSelections();
9228 var index = this.store.indexOf(record);
9229 var n = this.nodes[index];
9230 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9231 n.parentNode.removeChild(n);
9232 this.updateIndexes(index, index);
9238 onAdd : function(ds, records, index)
9240 Roo.log(['on Add', ds, records, index] );
9241 this.clearSelections();
9242 if(this.nodes.length == 0){
9246 var n = this.nodes[index];
9247 for(var i = 0, len = records.length; i < len; i++){
9248 var d = this.prepareData(records[i].data, i, records[i]);
9250 this.tpl.insertBefore(n, d);
9253 this.tpl.append(this.el, d);
9256 this.updateIndexes(index);
9259 onRemove : function(ds, record, index){
9260 Roo.log('onRemove');
9261 this.clearSelections();
9262 var el = this.dataName ?
9263 this.el.child('.roo-tpl-' + this.dataName) :
9266 el.dom.removeChild(this.nodes[index]);
9267 this.updateIndexes(index);
9271 * Refresh an individual node.
9272 * @param {Number} index
9274 refreshNode : function(index){
9275 this.onUpdate(this.store, this.store.getAt(index));
9278 updateIndexes : function(startIndex, endIndex){
9279 var ns = this.nodes;
9280 startIndex = startIndex || 0;
9281 endIndex = endIndex || ns.length - 1;
9282 for(var i = startIndex; i <= endIndex; i++){
9283 ns[i].nodeIndex = i;
9288 * Changes the data store this view uses and refresh the view.
9289 * @param {Store} store
9291 setStore : function(store, initial){
9292 if(!initial && this.store){
9293 this.store.un("datachanged", this.refresh);
9294 this.store.un("add", this.onAdd);
9295 this.store.un("remove", this.onRemove);
9296 this.store.un("update", this.onUpdate);
9297 this.store.un("clear", this.refresh);
9298 this.store.un("beforeload", this.onBeforeLoad);
9299 this.store.un("load", this.onLoad);
9300 this.store.un("loadexception", this.onLoad);
9304 store.on("datachanged", this.refresh, this);
9305 store.on("add", this.onAdd, this);
9306 store.on("remove", this.onRemove, this);
9307 store.on("update", this.onUpdate, this);
9308 store.on("clear", this.refresh, this);
9309 store.on("beforeload", this.onBeforeLoad, this);
9310 store.on("load", this.onLoad, this);
9311 store.on("loadexception", this.onLoad, this);
9319 * onbeforeLoad - masks the loading area.
9322 onBeforeLoad : function(store,opts)
9324 Roo.log('onBeforeLoad');
9328 this.el.mask(this.mask ? this.mask : "Loading" );
9330 onLoad : function ()
9337 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9338 * @param {HTMLElement} node
9339 * @return {HTMLElement} The template node
9341 findItemFromChild : function(node){
9342 var el = this.dataName ?
9343 this.el.child('.roo-tpl-' + this.dataName,true) :
9346 if(!node || node.parentNode == el){
9349 var p = node.parentNode;
9350 while(p && p != el){
9351 if(p.parentNode == el){
9360 onClick : function(e){
9361 var item = this.findItemFromChild(e.getTarget());
9363 var index = this.indexOf(item);
9364 if(this.onItemClick(item, index, e) !== false){
9365 this.fireEvent("click", this, index, item, e);
9368 this.clearSelections();
9373 onContextMenu : function(e){
9374 var item = this.findItemFromChild(e.getTarget());
9376 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9381 onDblClick : function(e){
9382 var item = this.findItemFromChild(e.getTarget());
9384 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9388 onItemClick : function(item, index, e)
9390 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9393 if (this.toggleSelect) {
9394 var m = this.isSelected(item) ? 'unselect' : 'select';
9397 _t[m](item, true, false);
9400 if(this.multiSelect || this.singleSelect){
9401 if(this.multiSelect && e.shiftKey && this.lastSelection){
9402 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9404 this.select(item, this.multiSelect && e.ctrlKey);
9405 this.lastSelection = item;
9413 * Get the number of selected nodes.
9416 getSelectionCount : function(){
9417 return this.selections.length;
9421 * Get the currently selected nodes.
9422 * @return {Array} An array of HTMLElements
9424 getSelectedNodes : function(){
9425 return this.selections;
9429 * Get the indexes of the selected nodes.
9432 getSelectedIndexes : function(){
9433 var indexes = [], s = this.selections;
9434 for(var i = 0, len = s.length; i < len; i++){
9435 indexes.push(s[i].nodeIndex);
9441 * Clear all selections
9442 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9444 clearSelections : function(suppressEvent){
9445 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9446 this.cmp.elements = this.selections;
9447 this.cmp.removeClass(this.selectedClass);
9448 this.selections = [];
9450 this.fireEvent("selectionchange", this, this.selections);
9456 * Returns true if the passed node is selected
9457 * @param {HTMLElement/Number} node The node or node index
9460 isSelected : function(node){
9461 var s = this.selections;
9465 node = this.getNode(node);
9466 return s.indexOf(node) !== -1;
9471 * @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
9472 * @param {Boolean} keepExisting (optional) true to keep existing selections
9473 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9475 select : function(nodeInfo, keepExisting, suppressEvent){
9476 if(nodeInfo instanceof Array){
9478 this.clearSelections(true);
9480 for(var i = 0, len = nodeInfo.length; i < len; i++){
9481 this.select(nodeInfo[i], true, true);
9485 var node = this.getNode(nodeInfo);
9486 if(!node || this.isSelected(node)){
9487 return; // already selected.
9490 this.clearSelections(true);
9492 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9493 Roo.fly(node).addClass(this.selectedClass);
9494 this.selections.push(node);
9496 this.fireEvent("selectionchange", this, this.selections);
9504 * @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
9505 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9506 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9508 unselect : function(nodeInfo, keepExisting, suppressEvent)
9510 if(nodeInfo instanceof Array){
9511 Roo.each(this.selections, function(s) {
9512 this.unselect(s, nodeInfo);
9516 var node = this.getNode(nodeInfo);
9517 if(!node || !this.isSelected(node)){
9518 Roo.log("not selected");
9519 return; // not selected.
9523 Roo.each(this.selections, function(s) {
9525 Roo.fly(node).removeClass(this.selectedClass);
9532 this.selections= ns;
9533 this.fireEvent("selectionchange", this, this.selections);
9537 * Gets a template node.
9538 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9539 * @return {HTMLElement} The node or null if it wasn't found
9541 getNode : function(nodeInfo){
9542 if(typeof nodeInfo == "string"){
9543 return document.getElementById(nodeInfo);
9544 }else if(typeof nodeInfo == "number"){
9545 return this.nodes[nodeInfo];
9551 * Gets a range template nodes.
9552 * @param {Number} startIndex
9553 * @param {Number} endIndex
9554 * @return {Array} An array of nodes
9556 getNodes : function(start, end){
9557 var ns = this.nodes;
9559 end = typeof end == "undefined" ? ns.length - 1 : end;
9562 for(var i = start; i <= end; i++){
9566 for(var i = start; i >= end; i--){
9574 * Finds the index of the passed node
9575 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9576 * @return {Number} The index of the node or -1
9578 indexOf : function(node){
9579 node = this.getNode(node);
9580 if(typeof node.nodeIndex == "number"){
9581 return node.nodeIndex;
9583 var ns = this.nodes;
9584 for(var i = 0, len = ns.length; i < len; i++){
9595 * based on jquery fullcalendar
9599 Roo.bootstrap = Roo.bootstrap || {};
9601 * @class Roo.bootstrap.Calendar
9602 * @extends Roo.bootstrap.Component
9603 * Bootstrap Calendar class
9604 * @cfg {Boolean} loadMask (true|false) default false
9607 * Create a new Container
9608 * @param {Object} config The config object
9613 Roo.bootstrap.Calendar = function(config){
9614 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9618 * Fires when a date is selected
9619 * @param {DatePicker} this
9620 * @param {Date} date The selected date
9624 * @event monthchange
9625 * Fires when the displayed month changes
9626 * @param {DatePicker} this
9627 * @param {Date} date The selected month
9629 'monthchange': true,
9632 * Fires when mouse over an event
9633 * @param {Calendar} this
9634 * @param {event} Event
9639 * Fires when the mouse leaves an
9640 * @param {Calendar} this
9646 * Fires when the mouse click an
9647 * @param {Calendar} this
9656 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9659 * @cfg {Number} startDay
9660 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9666 getAutoCreate : function(){
9669 var fc_button = function(name, corner, style, content ) {
9670 return Roo.apply({},{
9672 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9674 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9677 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9685 style : 'width:100%',
9692 cls : 'fc-header-left',
9694 fc_button('prev', 'left', 'arrow', '‹' ),
9695 fc_button('next', 'right', 'arrow', '›' ),
9696 { tag: 'span', cls: 'fc-header-space' },
9697 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9705 cls : 'fc-header-center',
9709 cls: 'fc-header-title',
9712 html : 'month / year'
9720 cls : 'fc-header-right',
9722 /* fc_button('month', 'left', '', 'month' ),
9723 fc_button('week', '', '', 'week' ),
9724 fc_button('day', 'right', '', 'day' )
9736 var cal_heads = function() {
9738 // fixme - handle this.
9740 for (var i =0; i < Date.dayNames.length; i++) {
9741 var d = Date.dayNames[i];
9744 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9745 html : d.substring(0,3)
9749 ret[0].cls += ' fc-first';
9750 ret[6].cls += ' fc-last';
9753 var cal_cell = function(n) {
9756 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9761 cls: 'fc-day-number',
9765 cls: 'fc-day-content',
9769 style: 'position: relative;' // height: 17px;
9781 var cal_rows = function() {
9784 for (var r = 0; r < 6; r++) {
9791 for (var i =0; i < Date.dayNames.length; i++) {
9792 var d = Date.dayNames[i];
9793 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9796 row.cn[0].cls+=' fc-first';
9797 row.cn[0].cn[0].style = 'min-height:90px';
9798 row.cn[6].cls+=' fc-last';
9802 ret[0].cls += ' fc-first';
9803 ret[4].cls += ' fc-prev-last';
9804 ret[5].cls += ' fc-last';
9811 cls: 'fc-border-separate',
9812 style : 'width:100%',
9820 cls : 'fc-first fc-last',
9839 style : "position: relative;",
9842 cls : 'fc-view fc-view-month fc-grid',
9843 style : 'position: relative',
9844 unselectable : 'on',
9847 cls : 'fc-event-container',
9848 style : 'position:absolute;z-index:8;top:0;left:0;'
9866 initEvents : function()
9869 throw "can not find store for calendar";
9875 style: "text-align:center",
9879 style: "background-color:white;width:50%;margin:250 auto",
9883 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
9894 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
9896 var size = this.el.select('.fc-content', true).first().getSize();
9897 this.maskEl.setSize(size.width, size.height);
9898 this.maskEl.enableDisplayMode("block");
9903 this.store = Roo.factory(this.store, Roo.data);
9904 this.store.on('load', this.onLoad, this);
9905 this.store.on('beforeload', this.onBeforeLoad, this);
9909 this.cells = this.el.select('.fc-day',true);
9910 //Roo.log(this.cells);
9911 this.textNodes = this.el.query('.fc-day-number');
9912 this.cells.addClassOnOver('fc-state-hover');
9914 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
9915 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
9916 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
9917 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
9919 this.on('monthchange', this.onMonthChange, this);
9921 this.update(new Date().clearTime());
9924 resize : function() {
9925 var sz = this.el.getSize();
9927 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
9928 this.el.select('.fc-day-content div',true).setHeight(34);
9933 showPrevMonth : function(e){
9934 this.update(this.activeDate.add("mo", -1));
9936 showToday : function(e){
9937 this.update(new Date().clearTime());
9940 showNextMonth : function(e){
9941 this.update(this.activeDate.add("mo", 1));
9945 showPrevYear : function(){
9946 this.update(this.activeDate.add("y", -1));
9950 showNextYear : function(){
9951 this.update(this.activeDate.add("y", 1));
9956 update : function(date)
9958 var vd = this.activeDate;
9959 this.activeDate = date;
9960 // if(vd && this.el){
9961 // var t = date.getTime();
9962 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
9963 // Roo.log('using add remove');
9965 // this.fireEvent('monthchange', this, date);
9967 // this.cells.removeClass("fc-state-highlight");
9968 // this.cells.each(function(c){
9969 // if(c.dateValue == t){
9970 // c.addClass("fc-state-highlight");
9971 // setTimeout(function(){
9972 // try{c.dom.firstChild.focus();}catch(e){}
9982 var days = date.getDaysInMonth();
9984 var firstOfMonth = date.getFirstDateOfMonth();
9985 var startingPos = firstOfMonth.getDay()-this.startDay;
9987 if(startingPos < this.startDay){
9991 var pm = date.add(Date.MONTH, -1);
9992 var prevStart = pm.getDaysInMonth()-startingPos;
9994 this.cells = this.el.select('.fc-day',true);
9995 this.textNodes = this.el.query('.fc-day-number');
9996 this.cells.addClassOnOver('fc-state-hover');
9998 var cells = this.cells.elements;
9999 var textEls = this.textNodes;
10001 Roo.each(cells, function(cell){
10002 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10005 days += startingPos;
10007 // convert everything to numbers so it's fast
10008 var day = 86400000;
10009 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10012 //Roo.log(prevStart);
10014 var today = new Date().clearTime().getTime();
10015 var sel = date.clearTime().getTime();
10016 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10017 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10018 var ddMatch = this.disabledDatesRE;
10019 var ddText = this.disabledDatesText;
10020 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10021 var ddaysText = this.disabledDaysText;
10022 var format = this.format;
10024 var setCellClass = function(cal, cell){
10026 //Roo.log('set Cell Class');
10028 var t = d.getTime();
10032 cell.dateValue = t;
10034 cell.className += " fc-today";
10035 cell.className += " fc-state-highlight";
10036 cell.title = cal.todayText;
10039 // disable highlight in other month..
10040 //cell.className += " fc-state-highlight";
10045 cell.className = " fc-state-disabled";
10046 cell.title = cal.minText;
10050 cell.className = " fc-state-disabled";
10051 cell.title = cal.maxText;
10055 if(ddays.indexOf(d.getDay()) != -1){
10056 cell.title = ddaysText;
10057 cell.className = " fc-state-disabled";
10060 if(ddMatch && format){
10061 var fvalue = d.dateFormat(format);
10062 if(ddMatch.test(fvalue)){
10063 cell.title = ddText.replace("%0", fvalue);
10064 cell.className = " fc-state-disabled";
10068 if (!cell.initialClassName) {
10069 cell.initialClassName = cell.dom.className;
10072 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10077 for(; i < startingPos; i++) {
10078 textEls[i].innerHTML = (++prevStart);
10079 d.setDate(d.getDate()+1);
10081 cells[i].className = "fc-past fc-other-month";
10082 setCellClass(this, cells[i]);
10087 for(; i < days; i++){
10088 intDay = i - startingPos + 1;
10089 textEls[i].innerHTML = (intDay);
10090 d.setDate(d.getDate()+1);
10092 cells[i].className = ''; // "x-date-active";
10093 setCellClass(this, cells[i]);
10097 for(; i < 42; i++) {
10098 textEls[i].innerHTML = (++extraDays);
10099 d.setDate(d.getDate()+1);
10101 cells[i].className = "fc-future fc-other-month";
10102 setCellClass(this, cells[i]);
10105 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10107 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10109 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10110 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10112 if(totalRows != 6){
10113 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10114 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10117 this.fireEvent('monthchange', this, date);
10121 if(!this.internalRender){
10122 var main = this.el.dom.firstChild;
10123 var w = main.offsetWidth;
10124 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10125 Roo.fly(main).setWidth(w);
10126 this.internalRender = true;
10127 // opera does not respect the auto grow header center column
10128 // then, after it gets a width opera refuses to recalculate
10129 // without a second pass
10130 if(Roo.isOpera && !this.secondPass){
10131 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10132 this.secondPass = true;
10133 this.update.defer(10, this, [date]);
10140 findCell : function(dt) {
10141 dt = dt.clearTime().getTime();
10143 this.cells.each(function(c){
10144 //Roo.log("check " +c.dateValue + '?=' + dt);
10145 if(c.dateValue == dt){
10155 findCells : function(ev) {
10156 var s = ev.start.clone().clearTime().getTime();
10158 var e= ev.end.clone().clearTime().getTime();
10161 this.cells.each(function(c){
10162 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10164 if(c.dateValue > e){
10167 if(c.dateValue < s){
10176 findBestRow: function(cells)
10180 for (var i =0 ; i < cells.length;i++) {
10181 ret = Math.max(cells[i].rows || 0,ret);
10188 addItem : function(ev)
10190 // look for vertical location slot in
10191 var cells = this.findCells(ev);
10193 ev.row = this.findBestRow(cells);
10195 // work out the location.
10199 for(var i =0; i < cells.length; i++) {
10207 if (crow.start.getY() == cells[i].getY()) {
10209 crow.end = cells[i];
10225 for (var i = 0; i < cells.length;i++) {
10226 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10230 this.calevents.push(ev);
10233 clearEvents: function() {
10235 if(!this.calevents){
10239 Roo.each(this.cells.elements, function(c){
10243 Roo.each(this.calevents, function(e) {
10244 Roo.each(e.els, function(el) {
10245 el.un('mouseenter' ,this.onEventEnter, this);
10246 el.un('mouseleave' ,this.onEventLeave, this);
10253 renderEvents: function()
10255 // first make sure there is enough space..
10257 this.cells.each(function(c) {
10259 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10262 for (var e = 0; e < this.calevents.length; e++) {
10263 var ev = this.calevents[e];
10264 var cells = ev.cells;
10265 var rows = ev.rows;
10267 for(var i =0; i < rows.length; i++) {
10270 // how many rows should it span..
10273 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10274 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10276 unselectable : "on",
10279 cls: 'fc-event-inner',
10283 // cls: 'fc-event-time',
10284 // html : cells.length > 1 ? '' : ev.time
10288 cls: 'fc-event-title',
10289 html : String.format('{0}', ev.title)
10296 cls: 'ui-resizable-handle ui-resizable-e',
10297 html : '  '
10303 cfg.cls += ' fc-event-start';
10305 if ((i+1) == rows.length) {
10306 cfg.cls += ' fc-event-end';
10309 var ctr = this.el.select('.fc-event-container',true).first();
10310 var cg = ctr.createChild(cfg);
10312 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10313 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10314 cg.on('click', this.onEventClick, this, ev);
10318 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10319 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10321 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10322 cg.setWidth(ebox.right - sbox.x -2);
10330 onEventEnter: function (e, el,event,d) {
10331 this.fireEvent('evententer', this, el, event);
10334 onEventLeave: function (e, el,event,d) {
10335 this.fireEvent('eventleave', this, el, event);
10338 onEventClick: function (e, el,event,d) {
10339 this.fireEvent('eventclick', this, el, event);
10342 onMonthChange: function () {
10346 onLoad: function ()
10348 this.calevents = [];
10351 if(this.store.getCount() > 0){
10352 this.store.data.each(function(d){
10355 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10356 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10357 time : d.data.start_time,
10358 title : d.data.title,
10359 description : d.data.description,
10360 venue : d.data.venue
10365 this.renderEvents();
10368 this.maskEl.hide();
10372 onBeforeLoad: function()
10374 this.clearEvents();
10377 this.maskEl.show();
10391 * @class Roo.bootstrap.Popover
10392 * @extends Roo.bootstrap.Component
10393 * Bootstrap Popover class
10394 * @cfg {String} html contents of the popover (or false to use children..)
10395 * @cfg {String} title of popover (or false to hide)
10396 * @cfg {String} placement how it is placed
10397 * @cfg {String} trigger click || hover (or false to trigger manually)
10398 * @cfg {String} over what (parent or false to trigger manually.)
10401 * Create a new Popover
10402 * @param {Object} config The config object
10405 Roo.bootstrap.Popover = function(config){
10406 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10409 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10411 title: 'Fill in a title',
10414 placement : 'right',
10415 trigger : 'hover', // hover
10419 can_build_overlaid : false,
10421 getChildContainer : function()
10423 return this.el.select('.popover-content',true).first();
10426 getAutoCreate : function(){
10427 Roo.log('make popover?');
10429 cls : 'popover roo-dynamic',
10430 style: 'display:block',
10436 cls : 'popover-inner',
10440 cls: 'popover-title',
10444 cls : 'popover-content',
10455 setTitle: function(str)
10457 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10459 setContent: function(str)
10461 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10463 // as it get's added to the bottom of the page.
10464 onRender : function(ct, position)
10466 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10468 var cfg = Roo.apply({}, this.getAutoCreate());
10472 cfg.cls += ' ' + this.cls;
10475 cfg.style = this.style;
10477 Roo.log("adding to ")
10478 this.el = Roo.get(document.body).createChild(cfg, position);
10484 initEvents : function()
10486 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10487 this.el.enableDisplayMode('block');
10489 if (this.over === false) {
10492 if (this.triggers === false) {
10495 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10496 var triggers = this.trigger ? this.trigger.split(' ') : [];
10497 Roo.each(triggers, function(trigger) {
10499 if (trigger == 'click') {
10500 on_el.on('click', this.toggle, this);
10501 } else if (trigger != 'manual') {
10502 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10503 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10505 on_el.on(eventIn ,this.enter, this);
10506 on_el.on(eventOut, this.leave, this);
10517 toggle : function () {
10518 this.hoverState == 'in' ? this.leave() : this.enter();
10521 enter : function () {
10524 clearTimeout(this.timeout);
10526 this.hoverState = 'in'
10528 if (!this.delay || !this.delay.show) {
10533 this.timeout = setTimeout(function () {
10534 if (_t.hoverState == 'in') {
10537 }, this.delay.show)
10539 leave : function() {
10540 clearTimeout(this.timeout);
10542 this.hoverState = 'out'
10544 if (!this.delay || !this.delay.hide) {
10549 this.timeout = setTimeout(function () {
10550 if (_t.hoverState == 'out') {
10553 }, this.delay.hide)
10556 show : function (on_el)
10559 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10562 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10563 if (this.html !== false) {
10564 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10566 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10567 if (!this.title.length) {
10568 this.el.select('.popover-title',true).hide();
10571 var placement = typeof this.placement == 'function' ?
10572 this.placement.call(this, this.el, on_el) :
10575 var autoToken = /\s?auto?\s?/i;
10576 var autoPlace = autoToken.test(placement);
10578 placement = placement.replace(autoToken, '') || 'top';
10582 //this.el.setXY([0,0]);
10584 this.el.dom.style.display='block';
10585 this.el.addClass(placement);
10587 //this.el.appendTo(on_el);
10589 var p = this.getPosition();
10590 var box = this.el.getBox();
10595 var align = Roo.bootstrap.Popover.alignment[placement]
10596 this.el.alignTo(on_el, align[0],align[1]);
10597 //var arrow = this.el.select('.arrow',true).first();
10598 //arrow.set(align[2],
10600 this.el.addClass('in');
10601 this.hoverState = null;
10603 if (this.el.hasClass('fade')) {
10610 this.el.setXY([0,0]);
10611 this.el.removeClass('in');
10618 Roo.bootstrap.Popover.alignment = {
10619 'left' : ['r-l', [-10,0], 'right'],
10620 'right' : ['l-r', [10,0], 'left'],
10621 'bottom' : ['t-b', [0,10], 'top'],
10622 'top' : [ 'b-t', [0,-10], 'bottom']
10633 * @class Roo.bootstrap.Progress
10634 * @extends Roo.bootstrap.Component
10635 * Bootstrap Progress class
10636 * @cfg {Boolean} striped striped of the progress bar
10637 * @cfg {Boolean} active animated of the progress bar
10641 * Create a new Progress
10642 * @param {Object} config The config object
10645 Roo.bootstrap.Progress = function(config){
10646 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10649 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10654 getAutoCreate : function(){
10662 cfg.cls += ' progress-striped';
10666 cfg.cls += ' active';
10685 * @class Roo.bootstrap.ProgressBar
10686 * @extends Roo.bootstrap.Component
10687 * Bootstrap ProgressBar class
10688 * @cfg {Number} aria_valuenow aria-value now
10689 * @cfg {Number} aria_valuemin aria-value min
10690 * @cfg {Number} aria_valuemax aria-value max
10691 * @cfg {String} label label for the progress bar
10692 * @cfg {String} panel (success | info | warning | danger )
10693 * @cfg {String} role role of the progress bar
10694 * @cfg {String} sr_only text
10698 * Create a new ProgressBar
10699 * @param {Object} config The config object
10702 Roo.bootstrap.ProgressBar = function(config){
10703 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10706 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10710 aria_valuemax : 100,
10716 getAutoCreate : function()
10721 cls: 'progress-bar',
10722 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10734 cfg.role = this.role;
10737 if(this.aria_valuenow){
10738 cfg['aria-valuenow'] = this.aria_valuenow;
10741 if(this.aria_valuemin){
10742 cfg['aria-valuemin'] = this.aria_valuemin;
10745 if(this.aria_valuemax){
10746 cfg['aria-valuemax'] = this.aria_valuemax;
10749 if(this.label && !this.sr_only){
10750 cfg.html = this.label;
10754 cfg.cls += ' progress-bar-' + this.panel;
10760 update : function(aria_valuenow)
10762 this.aria_valuenow = aria_valuenow;
10764 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10779 * @class Roo.bootstrap.TabPanel
10780 * @extends Roo.bootstrap.Component
10781 * Bootstrap TabPanel class
10782 * @cfg {Boolean} active panel active
10783 * @cfg {String} html panel content
10784 * @cfg {String} tabId tab relate id
10788 * Create a new TabPanel
10789 * @param {Object} config The config object
10792 Roo.bootstrap.TabPanel = function(config){
10793 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10796 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10802 getAutoCreate : function(){
10806 html: this.html || ''
10810 cfg.cls += ' active';
10814 cfg.tabId = this.tabId;
10832 * @class Roo.bootstrap.DateField
10833 * @extends Roo.bootstrap.Input
10834 * Bootstrap DateField class
10835 * @cfg {Number} weekStart default 0
10836 * @cfg {Number} weekStart default 0
10837 * @cfg {Number} viewMode default empty, (months|years)
10838 * @cfg {Number} minViewMode default empty, (months|years)
10839 * @cfg {Number} startDate default -Infinity
10840 * @cfg {Number} endDate default Infinity
10841 * @cfg {Boolean} todayHighlight default false
10842 * @cfg {Boolean} todayBtn default false
10843 * @cfg {Boolean} calendarWeeks default false
10844 * @cfg {Object} daysOfWeekDisabled default empty
10846 * @cfg {Boolean} keyboardNavigation default true
10847 * @cfg {String} language default en
10850 * Create a new DateField
10851 * @param {Object} config The config object
10854 Roo.bootstrap.DateField = function(config){
10855 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10858 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
10861 * @cfg {String} format
10862 * The default date format string which can be overriden for localization support. The format must be
10863 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
10867 * @cfg {String} altFormats
10868 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
10869 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
10871 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
10879 todayHighlight : false,
10885 keyboardNavigation: true,
10887 calendarWeeks: false,
10889 startDate: -Infinity,
10893 daysOfWeekDisabled: [],
10897 UTCDate: function()
10899 return new Date(Date.UTC.apply(Date, arguments));
10902 UTCToday: function()
10904 var today = new Date();
10905 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
10908 getDate: function() {
10909 var d = this.getUTCDate();
10910 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
10913 getUTCDate: function() {
10917 setDate: function(d) {
10918 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
10921 setUTCDate: function(d) {
10923 this.setValue(this.formatDate(this.date));
10926 onRender: function(ct, position)
10929 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
10931 this.language = this.language || 'en';
10932 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
10933 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
10935 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
10936 this.format = this.format || 'm/d/y';
10937 this.isInline = false;
10938 this.isInput = true;
10939 this.component = this.el.select('.add-on', true).first() || false;
10940 this.component = (this.component && this.component.length === 0) ? false : this.component;
10941 this.hasInput = this.component && this.inputEL().length;
10943 if (typeof(this.minViewMode === 'string')) {
10944 switch (this.minViewMode) {
10946 this.minViewMode = 1;
10949 this.minViewMode = 2;
10952 this.minViewMode = 0;
10957 if (typeof(this.viewMode === 'string')) {
10958 switch (this.viewMode) {
10971 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
10973 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
10975 this.picker().on('mousedown', this.onMousedown, this);
10976 this.picker().on('click', this.onClick, this);
10978 this.picker().addClass('datepicker-dropdown');
10980 this.startViewMode = this.viewMode;
10983 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
10984 if(!this.calendarWeeks){
10989 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
10990 v.attr('colspan', function(i, val){
10991 return parseInt(val) + 1;
10996 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
10998 this.setStartDate(this.startDate);
10999 this.setEndDate(this.endDate);
11001 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11008 if(this.isInline) {
11013 picker : function()
11015 return this.el.select('.datepicker', true).first();
11018 fillDow: function()
11020 var dowCnt = this.weekStart;
11029 if(this.calendarWeeks){
11037 while (dowCnt < this.weekStart + 7) {
11041 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11045 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11048 fillMonths: function()
11051 var months = this.picker().select('>.datepicker-months td', true).first();
11053 months.dom.innerHTML = '';
11059 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11062 months.createChild(month);
11067 update: function(){
11069 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11071 if (this.date < this.startDate) {
11072 this.viewDate = new Date(this.startDate);
11073 } else if (this.date > this.endDate) {
11074 this.viewDate = new Date(this.endDate);
11076 this.viewDate = new Date(this.date);
11083 var d = new Date(this.viewDate),
11084 year = d.getUTCFullYear(),
11085 month = d.getUTCMonth(),
11086 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11087 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11088 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11089 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11090 currentDate = this.date && this.date.valueOf(),
11091 today = this.UTCToday();
11093 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11095 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11097 // this.picker.select('>tfoot th.today').
11098 // .text(dates[this.language].today)
11099 // .toggle(this.todayBtn !== false);
11101 this.updateNavArrows();
11104 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11106 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11108 prevMonth.setUTCDate(day);
11110 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11112 var nextMonth = new Date(prevMonth);
11114 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11116 nextMonth = nextMonth.valueOf();
11118 var fillMonths = false;
11120 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11122 while(prevMonth.valueOf() < nextMonth) {
11125 if (prevMonth.getUTCDay() === this.weekStart) {
11127 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11135 if(this.calendarWeeks){
11136 // ISO 8601: First week contains first thursday.
11137 // ISO also states week starts on Monday, but we can be more abstract here.
11139 // Start of current week: based on weekstart/current date
11140 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11141 // Thursday of this week
11142 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11143 // First Thursday of year, year from thursday
11144 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11145 // Calendar week: ms between thursdays, div ms per day, div 7 days
11146 calWeek = (th - yth) / 864e5 / 7 + 1;
11148 fillMonths.cn.push({
11156 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11158 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11161 if (this.todayHighlight &&
11162 prevMonth.getUTCFullYear() == today.getFullYear() &&
11163 prevMonth.getUTCMonth() == today.getMonth() &&
11164 prevMonth.getUTCDate() == today.getDate()) {
11165 clsName += ' today';
11168 if (currentDate && prevMonth.valueOf() === currentDate) {
11169 clsName += ' active';
11172 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11173 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11174 clsName += ' disabled';
11177 fillMonths.cn.push({
11179 cls: 'day ' + clsName,
11180 html: prevMonth.getDate()
11183 prevMonth.setDate(prevMonth.getDate()+1);
11186 var currentYear = this.date && this.date.getUTCFullYear();
11187 var currentMonth = this.date && this.date.getUTCMonth();
11189 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11191 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11192 v.removeClass('active');
11194 if(currentYear === year && k === currentMonth){
11195 v.addClass('active');
11198 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11199 v.addClass('disabled');
11205 year = parseInt(year/10, 10) * 10;
11207 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11209 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11212 for (var i = -1; i < 11; i++) {
11213 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11215 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11223 showMode: function(dir) {
11225 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11227 Roo.each(this.picker().select('>div',true).elements, function(v){
11228 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11231 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11236 if(this.isInline) return;
11238 this.picker().removeClass(['bottom', 'top']);
11240 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11242 * place to the top of element!
11246 this.picker().addClass('top');
11247 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11252 this.picker().addClass('bottom');
11254 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11257 parseDate : function(value){
11258 if(!value || value instanceof Date){
11261 var v = Date.parseDate(value, this.format);
11262 if (!v && this.useIso) {
11263 v = Date.parseDate(value, 'Y-m-d');
11265 if(!v && this.altFormats){
11266 if(!this.altFormatsArray){
11267 this.altFormatsArray = this.altFormats.split("|");
11269 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11270 v = Date.parseDate(value, this.altFormatsArray[i]);
11276 formatDate : function(date, fmt){
11277 return (!date || !(date instanceof Date)) ?
11278 date : date.dateFormat(fmt || this.format);
11281 onFocus : function()
11283 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11287 onBlur : function()
11289 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11295 this.picker().show();
11302 if(this.isInline) return;
11303 this.picker().hide();
11304 this.viewMode = this.startViewMode;
11309 onMousedown: function(e){
11310 e.stopPropagation();
11311 e.preventDefault();
11314 keyup: function(e){
11315 Roo.bootstrap.DateField.superclass.keyup.call(this);
11320 fireKey: function(e){
11321 if (!this.picker().isVisible()){
11322 if (e.keyCode == 27) // allow escape to hide and re-show picker
11326 var dateChanged = false,
11328 newDate, newViewDate;
11332 e.preventDefault();
11336 if (!this.keyboardNavigation) break;
11337 dir = e.keyCode == 37 ? -1 : 1;
11340 newDate = this.moveYear(this.date, dir);
11341 newViewDate = this.moveYear(this.viewDate, dir);
11342 } else if (e.shiftKey){
11343 newDate = this.moveMonth(this.date, dir);
11344 newViewDate = this.moveMonth(this.viewDate, dir);
11346 newDate = new Date(this.date);
11347 newDate.setUTCDate(this.date.getUTCDate() + dir);
11348 newViewDate = new Date(this.viewDate);
11349 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11351 if (this.dateWithinRange(newDate)){
11352 this.date = newDate;
11353 this.viewDate = newViewDate;
11354 this.setValue(this.formatDate(this.date));
11356 e.preventDefault();
11357 dateChanged = true;
11362 if (!this.keyboardNavigation) break;
11363 dir = e.keyCode == 38 ? -1 : 1;
11365 newDate = this.moveYear(this.date, dir);
11366 newViewDate = this.moveYear(this.viewDate, dir);
11367 } else if (e.shiftKey){
11368 newDate = this.moveMonth(this.date, dir);
11369 newViewDate = this.moveMonth(this.viewDate, dir);
11371 newDate = new Date(this.date);
11372 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11373 newViewDate = new Date(this.viewDate);
11374 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11376 if (this.dateWithinRange(newDate)){
11377 this.date = newDate;
11378 this.viewDate = newViewDate;
11379 this.setValue(this.formatDate(this.date));
11381 e.preventDefault();
11382 dateChanged = true;
11386 this.setValue(this.formatDate(this.date));
11388 e.preventDefault();
11391 this.setValue(this.formatDate(this.date));
11398 onClick: function(e) {
11399 e.stopPropagation();
11400 e.preventDefault();
11402 var target = e.getTarget();
11404 if(target.nodeName.toLowerCase() === 'i'){
11405 target = Roo.get(target).dom.parentNode;
11408 var nodeName = target.nodeName;
11409 var className = target.className;
11410 var html = target.innerHTML;
11412 switch(nodeName.toLowerCase()) {
11414 switch(className) {
11420 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11421 switch(this.viewMode){
11423 this.viewDate = this.moveMonth(this.viewDate, dir);
11427 this.viewDate = this.moveYear(this.viewDate, dir);
11433 var date = new Date();
11434 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11436 this.setValue(this.formatDate(this.date));
11442 if (className.indexOf('disabled') === -1) {
11443 this.viewDate.setUTCDate(1);
11444 if (className.indexOf('month') !== -1) {
11445 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11447 var year = parseInt(html, 10) || 0;
11448 this.viewDate.setUTCFullYear(year);
11457 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11458 var day = parseInt(html, 10) || 1;
11459 var year = this.viewDate.getUTCFullYear(),
11460 month = this.viewDate.getUTCMonth();
11462 if (className.indexOf('old') !== -1) {
11469 } else if (className.indexOf('new') !== -1) {
11477 this.date = this.UTCDate(year, month, day,0,0,0,0);
11478 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11480 this.setValue(this.formatDate(this.date));
11487 setStartDate: function(startDate){
11488 this.startDate = startDate || -Infinity;
11489 if (this.startDate !== -Infinity) {
11490 this.startDate = this.parseDate(this.startDate);
11493 this.updateNavArrows();
11496 setEndDate: function(endDate){
11497 this.endDate = endDate || Infinity;
11498 if (this.endDate !== Infinity) {
11499 this.endDate = this.parseDate(this.endDate);
11502 this.updateNavArrows();
11505 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11506 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11507 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11508 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11510 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11511 return parseInt(d, 10);
11514 this.updateNavArrows();
11517 updateNavArrows: function() {
11518 var d = new Date(this.viewDate),
11519 year = d.getUTCFullYear(),
11520 month = d.getUTCMonth();
11522 Roo.each(this.picker().select('.prev', true).elements, function(v){
11524 switch (this.viewMode) {
11527 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11533 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11540 Roo.each(this.picker().select('.next', true).elements, function(v){
11542 switch (this.viewMode) {
11545 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11551 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11559 moveMonth: function(date, dir){
11560 if (!dir) return date;
11561 var new_date = new Date(date.valueOf()),
11562 day = new_date.getUTCDate(),
11563 month = new_date.getUTCMonth(),
11564 mag = Math.abs(dir),
11566 dir = dir > 0 ? 1 : -1;
11569 // If going back one month, make sure month is not current month
11570 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11572 return new_date.getUTCMonth() == month;
11574 // If going forward one month, make sure month is as expected
11575 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11577 return new_date.getUTCMonth() != new_month;
11579 new_month = month + dir;
11580 new_date.setUTCMonth(new_month);
11581 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11582 if (new_month < 0 || new_month > 11)
11583 new_month = (new_month + 12) % 12;
11585 // For magnitudes >1, move one month at a time...
11586 for (var i=0; i<mag; i++)
11587 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11588 new_date = this.moveMonth(new_date, dir);
11589 // ...then reset the day, keeping it in the new month
11590 new_month = new_date.getUTCMonth();
11591 new_date.setUTCDate(day);
11593 return new_month != new_date.getUTCMonth();
11596 // Common date-resetting loop -- if date is beyond end of month, make it
11599 new_date.setUTCDate(--day);
11600 new_date.setUTCMonth(new_month);
11605 moveYear: function(date, dir){
11606 return this.moveMonth(date, dir*12);
11609 dateWithinRange: function(date){
11610 return date >= this.startDate && date <= this.endDate;
11614 remove: function() {
11615 this.picker().remove();
11620 Roo.apply(Roo.bootstrap.DateField, {
11631 html: '<i class="icon-arrow-left"/>'
11641 html: '<i class="icon-arrow-right"/>'
11683 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11684 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11685 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11686 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11687 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11700 navFnc: 'FullYear',
11705 navFnc: 'FullYear',
11710 Roo.apply(Roo.bootstrap.DateField, {
11714 cls: 'datepicker dropdown-menu',
11718 cls: 'datepicker-days',
11722 cls: 'table-condensed',
11724 Roo.bootstrap.DateField.head,
11728 Roo.bootstrap.DateField.footer
11735 cls: 'datepicker-months',
11739 cls: 'table-condensed',
11741 Roo.bootstrap.DateField.head,
11742 Roo.bootstrap.DateField.content,
11743 Roo.bootstrap.DateField.footer
11750 cls: 'datepicker-years',
11754 cls: 'table-condensed',
11756 Roo.bootstrap.DateField.head,
11757 Roo.bootstrap.DateField.content,
11758 Roo.bootstrap.DateField.footer
11777 * @class Roo.bootstrap.TimeField
11778 * @extends Roo.bootstrap.Input
11779 * Bootstrap DateField class
11783 * Create a new TimeField
11784 * @param {Object} config The config object
11787 Roo.bootstrap.TimeField = function(config){
11788 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11791 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
11794 * @cfg {String} format
11795 * The default time format string which can be overriden for localization support. The format must be
11796 * valid according to {@link Date#parseDate} (defaults to 'H:i').
11800 onRender: function(ct, position)
11803 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11805 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11807 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11809 this.pop = this.picker().select('>.datepicker-time',true).first();
11810 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
11812 this.picker().on('mousedown', this.onMousedown, this);
11813 this.picker().on('click', this.onClick, this);
11815 this.picker().addClass('datepicker-dropdown');
11820 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11821 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11822 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11823 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11824 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11825 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
11829 onClick: function(e) {
11830 e.stopPropagation();
11831 e.preventDefault();
11834 picker : function()
11836 return this.el.select('.datepicker', true).first();
11839 fillTime: function()
11841 var time = this.pop.select('tbody', true).first();
11843 time.dom.innerHTML = '';
11858 cls: 'hours-up glyphicon glyphicon-chevron-up'
11878 cls: 'minutes-up glyphicon glyphicon-chevron-up'
11899 cls: 'timepicker-hour',
11914 cls: 'timepicker-minute',
11929 cls: 'btn btn-primary period',
11951 cls: 'hours-down glyphicon glyphicon-chevron-down'
11971 cls: 'minutes-down glyphicon glyphicon-chevron-down'
11989 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
11996 var hours = this.time.getHours();
11997 var minutes = this.time.getMinutes();
12010 hours = hours - 12;
12014 hours = '0' + hours;
12018 minutes = '0' + minutes;
12021 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12022 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12023 this.pop.select('button', true).first().dom.innerHTML = period;
12029 this.picker().removeClass(['bottom', 'top']);
12031 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12033 * place to the top of element!
12037 this.picker().addClass('top');
12038 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12043 this.picker().addClass('bottom');
12045 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12048 onFocus : function()
12050 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12054 onBlur : function()
12056 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12062 this.picker().show();
12070 this.picker().hide();
12075 setTime : function()
12078 this.setValue(this.time.format(this.format));
12081 onMousedown: function(e){
12082 e.stopPropagation();
12083 e.preventDefault();
12086 onIncrementHours: function()
12088 Roo.log('onIncrementHours');
12089 this.time = this.time.add(Date.HOUR, 1);
12094 onDecrementHours: function()
12096 Roo.log('onDecrementHours');
12097 this.time = this.time.add(Date.HOUR, -1);
12101 onIncrementMinutes: function()
12103 Roo.log('onIncrementMinutes');
12104 this.time = this.time.add(Date.MINUTE, 1);
12108 onDecrementMinutes: function()
12110 Roo.log('onDecrementMinutes');
12111 this.time = this.time.add(Date.MINUTE, -1);
12115 onTogglePeriod: function()
12117 Roo.log('onTogglePeriod');
12118 this.time = this.time.add(Date.HOUR, 12);
12125 Roo.apply(Roo.bootstrap.TimeField, {
12155 cls: 'btn btn-info ok',
12167 Roo.apply(Roo.bootstrap.TimeField, {
12171 cls: 'datepicker dropdown-menu',
12175 cls: 'datepicker-time',
12179 cls: 'table-condensed',
12181 Roo.bootstrap.TimeField.content,
12182 Roo.bootstrap.TimeField.footer
12201 * @class Roo.bootstrap.CheckBox
12202 * @extends Roo.bootstrap.Input
12203 * Bootstrap CheckBox class
12205 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12206 * @cfg {String} boxLabel The text that appears beside the checkbox
12207 * @cfg {Boolean} checked initnal the element
12210 * Create a new CheckBox
12211 * @param {Object} config The config object
12214 Roo.bootstrap.CheckBox = function(config){
12215 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12220 * Fires when the element is checked or unchecked.
12221 * @param {Roo.bootstrap.CheckBox} this This input
12222 * @param {Boolean} checked The new checked value
12228 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12230 inputType: 'checkbox',
12236 getAutoCreate : function()
12238 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12244 cfg.cls = 'form-group' //input-group
12249 type : this.inputType,
12250 value : (!this.checked) ? this.valueOff : this.value,
12252 placeholder : this.placeholder || ''
12256 if (this.disabled) {
12257 input.disabled=true;
12261 input.checked = this.checked;
12265 input.name = this.name;
12269 input.cls += ' input-' + this.size;
12273 ['xs','sm','md','lg'].map(function(size){
12274 if (settings[size]) {
12275 cfg.cls += ' col-' + size + '-' + settings[size];
12279 var inputblock = input;
12281 if (this.before || this.after) {
12284 cls : 'input-group',
12288 inputblock.cn.push({
12290 cls : 'input-group-addon',
12294 inputblock.cn.push(input);
12296 inputblock.cn.push({
12298 cls : 'input-group-addon',
12305 if (align ==='left' && this.fieldLabel.length) {
12306 Roo.log("left and has label");
12312 cls : 'control-label col-md-' + this.labelWidth,
12313 html : this.fieldLabel
12317 cls : "col-md-" + (12 - this.labelWidth),
12324 } else if ( this.fieldLabel.length) {
12331 cls: 'control-label box-input-label',
12332 //cls : 'input-group-addon',
12333 html : this.fieldLabel
12343 Roo.log(" no label && no align");
12358 html: this.boxLabel
12367 * return the real input element.
12369 inputEl: function ()
12371 return this.el.select('input.form-box',true).first();
12374 initEvents : function()
12376 Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12378 this.inputEl().on('click', this.onClick, this);
12382 onClick : function()
12384 this.setChecked(!this.checked);
12387 setChecked : function(state,suppressEvent)
12389 this.checked = state;
12391 if(suppressEvent !== true){
12392 this.fireEvent('check', this, state);
12395 this.inputEl().dom.value = state ? this.value : this.valueOff;
12409 * @class Roo.bootstrap.Radio
12410 * @extends Roo.bootstrap.CheckBox
12411 * Bootstrap Radio class
12414 * Create a new Radio
12415 * @param {Object} config The config object
12418 Roo.bootstrap.Radio = function(config){
12419 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12423 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12425 inputType: 'radio',
12427 getAutoCreate : function()
12429 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12435 cfg.cls = 'form-group' //input-group
12440 type : this.inputType,
12441 value : (!this.checked) ? this.valueOff : this.value,
12443 placeholder : this.placeholder || ''
12447 if (this.disabled) {
12448 input.disabled=true;
12452 input.checked = this.checked;
12456 input.name = this.name;
12460 input.cls += ' input-' + this.size;
12464 ['xs','sm','md','lg'].map(function(size){
12465 if (settings[size]) {
12466 cfg.cls += ' col-' + size + '-' + settings[size];
12470 var inputblock = input;
12472 if (this.before || this.after) {
12475 cls : 'input-group',
12479 inputblock.cn.push({
12481 cls : 'input-group-addon',
12485 inputblock.cn.push(input);
12487 inputblock.cn.push({
12489 cls : 'input-group-addon',
12496 if (align ==='left' && this.fieldLabel.length) {
12497 Roo.log("left and has label");
12503 cls : 'control-label col-md-' + this.labelWidth,
12504 html : this.fieldLabel
12508 cls : "col-md-" + (12 - this.labelWidth),
12515 } else if ( this.fieldLabel.length) {
12522 cls: 'control-label box-input-label',
12523 //cls : 'input-group-addon',
12524 html : this.fieldLabel
12534 Roo.log(" no label && no align");
12549 html: this.boxLabel
12557 onClick : function()
12559 this.setChecked(true);
12562 setChecked : function(state,suppressEvent)
12564 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12568 this.checked = state;
12570 if(suppressEvent !== true){
12571 this.fireEvent('check', this, state);
12574 this.inputEl().dom.value = state ? this.value : this.valueOff;
12578 getGroupValue : function()
12580 if(typeof(this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true)) == 'undefined'){
12584 return this.inputEl().up('form').child('input[name='+this.inputEl().dom.name+']:checked', true).value;
12588 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12589 * @return {Mixed} value The field value
12591 getValue : function(){
12592 return this.getGroupValue();
12606 * @class Roo.bootstrap.HtmlEditor
12607 * @extends Roo.bootstrap.Component
12608 * Bootstrap HtmlEditor class
12611 * Create a new HtmlEditor
12612 * @param {Object} config The config object
12615 Roo.bootstrap.HtmlEditor = function(config){
12616 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
12617 if (!this.toolbars) {
12618 this.toolbars = [];
12620 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
12623 * @event initialize
12624 * Fires when the editor is fully initialized (including the iframe)
12625 * @param {HtmlEditor} this
12630 * Fires when the editor is first receives the focus. Any insertion must wait
12631 * until after this event.
12632 * @param {HtmlEditor} this
12636 * @event beforesync
12637 * Fires before the textarea is updated with content from the editor iframe. Return false
12638 * to cancel the sync.
12639 * @param {HtmlEditor} this
12640 * @param {String} html
12644 * @event beforepush
12645 * Fires before the iframe editor is updated with content from the textarea. Return false
12646 * to cancel the push.
12647 * @param {HtmlEditor} this
12648 * @param {String} html
12653 * Fires when the textarea is updated with content from the editor iframe.
12654 * @param {HtmlEditor} this
12655 * @param {String} html
12660 * Fires when the iframe editor is updated with content from the textarea.
12661 * @param {HtmlEditor} this
12662 * @param {String} html
12666 * @event editmodechange
12667 * Fires when the editor switches edit modes
12668 * @param {HtmlEditor} this
12669 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
12671 editmodechange: true,
12673 * @event editorevent
12674 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12675 * @param {HtmlEditor} this
12679 * @event firstfocus
12680 * Fires when on first focus - needed by toolbars..
12681 * @param {HtmlEditor} this
12686 * Auto save the htmlEditor value as a file into Events
12687 * @param {HtmlEditor} this
12691 * @event savedpreview
12692 * preview the saved version of htmlEditor
12693 * @param {HtmlEditor} this
12700 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
12704 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
12709 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12714 * @cfg {Number} height (in pixels)
12718 * @cfg {Number} width (in pixels)
12723 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12726 stylesheets: false,
12731 // private properties
12732 validationEvent : false,
12734 initialized : false,
12737 onFocus : Roo.emptyFn,
12739 hideMode:'offsets',
12742 tbContainer : false,
12744 toolbarContainer :function() {
12745 return this.wrap.select('.x-html-editor-tb',true).first();
12749 * Protected method that will not generally be called directly. It
12750 * is called when the editor creates its toolbar. Override this method if you need to
12751 * add custom toolbar buttons.
12752 * @param {HtmlEditor} editor
12754 createToolbar : function(){
12756 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
12757 this.toolbars[0].render(this.toolbarContainer());
12759 Roo.log("create toolbars");
12761 if (!editor.toolbars || !editor.toolbars.length) {
12762 editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
12765 for (var i =0 ; i < editor.toolbars.length;i++) {
12766 editor.toolbars[i] = Roo.factory(
12767 typeof(editor.toolbars[i]) == 'string' ?
12768 { xtype: editor.toolbars[i]} : editor.toolbars[i],
12769 Roo.bootstrap.HtmlEditor);
12770 editor.toolbars[i].init(editor);
12778 onRender : function(ct, position)
12780 // Roo.log("Call onRender: " + this.xtype);
12782 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
12784 this.wrap = this.inputEl().wrap({
12785 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
12788 this.editorcore.onRender(ct, position);
12790 if (this.resizable) {
12791 this.resizeEl = new Roo.Resizable(this.wrap, {
12795 minHeight : this.height,
12796 height: this.height,
12797 handles : this.resizable,
12800 resize : function(r, w, h) {
12801 _t.onResize(w,h); // -something
12807 this.createToolbar(this);
12811 this.setSize(this.wrap.getSize());
12813 if (this.resizeEl) {
12814 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
12815 // should trigger onReize..
12818 // if(this.autosave && this.w){
12819 // this.autoSaveFn = setInterval(this.autosave, 1000);
12824 onResize : function(w, h)
12826 Roo.log('resize: ' +w + ',' + h );
12827 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
12831 if(this.inputEl() ){
12832 if(typeof w == 'number'){
12833 var aw = w - this.wrap.getFrameWidth('lr');
12834 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
12837 if(typeof h == 'number'){
12838 var tbh = -11; // fixme it needs to tool bar size!
12839 for (var i =0; i < this.toolbars.length;i++) {
12840 // fixme - ask toolbars for heights?
12841 tbh += this.toolbars[i].el.getHeight();
12842 //if (this.toolbars[i].footer) {
12843 // tbh += this.toolbars[i].footer.el.getHeight();
12851 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
12852 ah -= 5; // knock a few pixes off for look..
12853 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
12857 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
12858 this.editorcore.onResize(ew,eh);
12863 * Toggles the editor between standard and source edit mode.
12864 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
12866 toggleSourceEdit : function(sourceEditMode)
12868 this.editorcore.toggleSourceEdit(sourceEditMode);
12870 if(this.editorcore.sourceEditMode){
12871 Roo.log('editor - showing textarea');
12874 // Roo.log(this.syncValue());
12875 this.editorcore.syncValue();
12876 this.inputEl().removeClass('hide');
12877 this.inputEl().dom.removeAttribute('tabIndex');
12878 this.inputEl().focus();
12880 Roo.log('editor - hiding textarea');
12882 // Roo.log(this.pushValue());
12883 this.editorcore.pushValue();
12885 this.inputEl().addClass('hide');
12886 this.inputEl().dom.setAttribute('tabIndex', -1);
12887 //this.deferFocus();
12890 this.setSize(this.wrap.getSize());
12891 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
12894 // private (for BoxComponent)
12895 adjustSize : Roo.BoxComponent.prototype.adjustSize,
12897 // private (for BoxComponent)
12898 getResizeEl : function(){
12902 // private (for BoxComponent)
12903 getPositionEl : function(){
12908 initEvents : function(){
12909 this.originalValue = this.getValue();
12913 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
12916 markInvalid : Roo.emptyFn,
12918 * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
12921 clearInvalid : Roo.emptyFn,
12923 setValue : function(v){
12924 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
12925 this.editorcore.pushValue();
12930 deferFocus : function(){
12931 this.focus.defer(10, this);
12935 focus : function(){
12936 this.editorcore.focus();
12942 onDestroy : function(){
12948 for (var i =0; i < this.toolbars.length;i++) {
12949 // fixme - ask toolbars for heights?
12950 this.toolbars[i].onDestroy();
12953 this.wrap.dom.innerHTML = '';
12954 this.wrap.remove();
12959 onFirstFocus : function(){
12960 //Roo.log("onFirstFocus");
12961 this.editorcore.onFirstFocus();
12962 for (var i =0; i < this.toolbars.length;i++) {
12963 this.toolbars[i].onFirstFocus();
12969 syncValue : function()
12971 this.editorcore.syncValue();
12975 // hide stuff that is not compatible
12989 * @event specialkey
12993 * @cfg {String} fieldClass @hide
12996 * @cfg {String} focusClass @hide
12999 * @cfg {String} autoCreate @hide
13002 * @cfg {String} inputType @hide
13005 * @cfg {String} invalidClass @hide
13008 * @cfg {String} invalidText @hide
13011 * @cfg {String} msgFx @hide
13014 * @cfg {String} validateOnBlur @hide
13022 //<script type="text/javascript">
13025 * Based Ext JS Library 1.1.1
13026 * Copyright(c) 2006-2007, Ext JS, LLC.
13032 * @class Roo.HtmlEditorCore
13033 * @extends Roo.Component
13034 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
13036 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
13039 Roo.HtmlEditorCore = function(config){
13042 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
13045 * @event initialize
13046 * Fires when the editor is fully initialized (including the iframe)
13047 * @param {Roo.HtmlEditorCore} this
13052 * Fires when the editor is first receives the focus. Any insertion must wait
13053 * until after this event.
13054 * @param {Roo.HtmlEditorCore} this
13058 * @event beforesync
13059 * Fires before the textarea is updated with content from the editor iframe. Return false
13060 * to cancel the sync.
13061 * @param {Roo.HtmlEditorCore} this
13062 * @param {String} html
13066 * @event beforepush
13067 * Fires before the iframe editor is updated with content from the textarea. Return false
13068 * to cancel the push.
13069 * @param {Roo.HtmlEditorCore} this
13070 * @param {String} html
13075 * Fires when the textarea is updated with content from the editor iframe.
13076 * @param {Roo.HtmlEditorCore} this
13077 * @param {String} html
13082 * Fires when the iframe editor is updated with content from the textarea.
13083 * @param {Roo.HtmlEditorCore} this
13084 * @param {String} html
13089 * @event editorevent
13090 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
13091 * @param {Roo.HtmlEditorCore} this
13099 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
13103 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
13109 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
13114 * @cfg {Number} height (in pixels)
13118 * @cfg {Number} width (in pixels)
13123 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
13126 stylesheets: false,
13131 // private properties
13132 validationEvent : false,
13134 initialized : false,
13136 sourceEditMode : false,
13137 onFocus : Roo.emptyFn,
13139 hideMode:'offsets',
13145 * Protected method that will not generally be called directly. It
13146 * is called when the editor initializes the iframe with HTML contents. Override this method if you
13147 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
13149 getDocMarkup : function(){
13152 Roo.log(this.stylesheets);
13154 // inherit styels from page...??
13155 if (this.stylesheets === false) {
13157 Roo.get(document.head).select('style').each(function(node) {
13158 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13161 Roo.get(document.head).select('link').each(function(node) {
13162 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
13165 } else if (!this.stylesheets.length) {
13167 st = '<style type="text/css">' +
13168 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13171 Roo.each(this.stylesheets, function(s) {
13172 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
13177 st += '<style type="text/css">' +
13178 'IMG { cursor: pointer } ' +
13182 return '<html><head>' + st +
13183 //<style type="text/css">' +
13184 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
13186 ' </head><body class="roo-htmleditor-body"></body></html>';
13190 onRender : function(ct, position)
13193 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
13194 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13197 this.el.dom.style.border = '0 none';
13198 this.el.dom.setAttribute('tabIndex', -1);
13199 this.el.addClass('x-hidden hide');
13203 if(Roo.isIE){ // fix IE 1px bogus margin
13204 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13208 this.frameId = Roo.id();
13212 var iframe = this.owner.wrap.createChild({
13214 cls: 'form-control', // bootstrap..
13216 name: this.frameId,
13217 frameBorder : 'no',
13218 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13223 this.iframe = iframe.dom;
13225 this.assignDocWin();
13227 this.doc.designMode = 'on';
13230 this.doc.write(this.getDocMarkup());
13234 var task = { // must defer to wait for browser to be ready
13236 //console.log("run task?" + this.doc.readyState);
13237 this.assignDocWin();
13238 if(this.doc.body || this.doc.readyState == 'complete'){
13240 this.doc.designMode="on";
13244 Roo.TaskMgr.stop(task);
13245 this.initEditor.defer(10, this);
13252 Roo.TaskMgr.start(task);
13259 onResize : function(w, h)
13261 Roo.log('resize: ' +w + ',' + h );
13262 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13266 if(typeof w == 'number'){
13268 this.iframe.style.width = w + 'px';
13270 if(typeof h == 'number'){
13272 this.iframe.style.height = h + 'px';
13274 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13281 * Toggles the editor between standard and source edit mode.
13282 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13284 toggleSourceEdit : function(sourceEditMode){
13286 this.sourceEditMode = sourceEditMode === true;
13288 if(this.sourceEditMode){
13290 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13293 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13294 //this.iframe.className = '';
13297 //this.setSize(this.owner.wrap.getSize());
13298 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13305 * Protected method that will not generally be called directly. If you need/want
13306 * custom HTML cleanup, this is the method you should override.
13307 * @param {String} html The HTML to be cleaned
13308 * return {String} The cleaned HTML
13310 cleanHtml : function(html){
13311 html = String(html);
13312 if(html.length > 5){
13313 if(Roo.isSafari){ // strip safari nonsense
13314 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13317 if(html == ' '){
13324 * HTML Editor -> Textarea
13325 * Protected method that will not generally be called directly. Syncs the contents
13326 * of the editor iframe with the textarea.
13328 syncValue : function(){
13329 if(this.initialized){
13330 var bd = (this.doc.body || this.doc.documentElement);
13331 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13332 var html = bd.innerHTML;
13334 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13335 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13337 html = '<div style="'+m[0]+'">' + html + '</div>';
13340 html = this.cleanHtml(html);
13341 // fix up the special chars.. normaly like back quotes in word...
13342 // however we do not want to do this with chinese..
13343 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13344 var cc = b.charCodeAt();
13346 (cc >= 0x4E00 && cc < 0xA000 ) ||
13347 (cc >= 0x3400 && cc < 0x4E00 ) ||
13348 (cc >= 0xf900 && cc < 0xfb00 )
13354 if(this.owner.fireEvent('beforesync', this, html) !== false){
13355 this.el.dom.value = html;
13356 this.owner.fireEvent('sync', this, html);
13362 * Protected method that will not generally be called directly. Pushes the value of the textarea
13363 * into the iframe editor.
13365 pushValue : function(){
13366 if(this.initialized){
13367 var v = this.el.dom.value;
13373 if(this.owner.fireEvent('beforepush', this, v) !== false){
13374 var d = (this.doc.body || this.doc.documentElement);
13376 this.cleanUpPaste();
13377 this.el.dom.value = d.innerHTML;
13378 this.owner.fireEvent('push', this, v);
13384 deferFocus : function(){
13385 this.focus.defer(10, this);
13389 focus : function(){
13390 if(this.win && !this.sourceEditMode){
13397 assignDocWin: function()
13399 var iframe = this.iframe;
13402 this.doc = iframe.contentWindow.document;
13403 this.win = iframe.contentWindow;
13405 if (!Roo.get(this.frameId)) {
13408 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13409 this.win = Roo.get(this.frameId).dom.contentWindow;
13414 initEditor : function(){
13415 //console.log("INIT EDITOR");
13416 this.assignDocWin();
13420 this.doc.designMode="on";
13422 this.doc.write(this.getDocMarkup());
13425 var dbody = (this.doc.body || this.doc.documentElement);
13426 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13427 // this copies styles from the containing element into thsi one..
13428 // not sure why we need all of this..
13429 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13430 ss['background-attachment'] = 'fixed'; // w3c
13431 dbody.bgProperties = 'fixed'; // ie
13432 Roo.DomHelper.applyStyles(dbody, ss);
13433 Roo.EventManager.on(this.doc, {
13434 //'mousedown': this.onEditorEvent,
13435 'mouseup': this.onEditorEvent,
13436 'dblclick': this.onEditorEvent,
13437 'click': this.onEditorEvent,
13438 'keyup': this.onEditorEvent,
13443 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13445 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13446 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13448 this.initialized = true;
13450 this.owner.fireEvent('initialize', this);
13455 onDestroy : function(){
13461 //for (var i =0; i < this.toolbars.length;i++) {
13462 // // fixme - ask toolbars for heights?
13463 // this.toolbars[i].onDestroy();
13466 //this.wrap.dom.innerHTML = '';
13467 //this.wrap.remove();
13472 onFirstFocus : function(){
13474 this.assignDocWin();
13477 this.activated = true;
13480 if(Roo.isGecko){ // prevent silly gecko errors
13482 var s = this.win.getSelection();
13483 if(!s.focusNode || s.focusNode.nodeType != 3){
13484 var r = s.getRangeAt(0);
13485 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13490 this.execCmd('useCSS', true);
13491 this.execCmd('styleWithCSS', false);
13494 this.owner.fireEvent('activate', this);
13498 adjustFont: function(btn){
13499 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13500 //if(Roo.isSafari){ // safari
13503 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13504 if(Roo.isSafari){ // safari
13505 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13506 v = (v < 10) ? 10 : v;
13507 v = (v > 48) ? 48 : v;
13508 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13513 v = Math.max(1, v+adjust);
13515 this.execCmd('FontSize', v );
13518 onEditorEvent : function(e){
13519 this.owner.fireEvent('editorevent', this, e);
13520 // this.updateToolbar();
13521 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13524 insertTag : function(tg)
13526 // could be a bit smarter... -> wrap the current selected tRoo..
13527 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13529 range = this.createRange(this.getSelection());
13530 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13531 wrappingNode.appendChild(range.extractContents());
13532 range.insertNode(wrappingNode);
13539 this.execCmd("formatblock", tg);
13543 insertText : function(txt)
13547 var range = this.createRange();
13548 range.deleteContents();
13549 //alert(Sender.getAttribute('label'));
13551 range.insertNode(this.doc.createTextNode(txt));
13557 * Executes a Midas editor command on the editor document and performs necessary focus and
13558 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13559 * @param {String} cmd The Midas command
13560 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13562 relayCmd : function(cmd, value){
13564 this.execCmd(cmd, value);
13565 this.owner.fireEvent('editorevent', this);
13566 //this.updateToolbar();
13567 this.owner.deferFocus();
13571 * Executes a Midas editor command directly on the editor document.
13572 * For visual commands, you should use {@link #relayCmd} instead.
13573 * <b>This should only be called after the editor is initialized.</b>
13574 * @param {String} cmd The Midas command
13575 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13577 execCmd : function(cmd, value){
13578 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13585 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13587 * @param {String} text | dom node..
13589 insertAtCursor : function(text)
13594 if(!this.activated){
13600 var r = this.doc.selection.createRange();
13611 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13615 // from jquery ui (MIT licenced)
13617 var win = this.win;
13619 if (win.getSelection && win.getSelection().getRangeAt) {
13620 range = win.getSelection().getRangeAt(0);
13621 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13622 range.insertNode(node);
13623 } else if (win.document.selection && win.document.selection.createRange) {
13624 // no firefox support
13625 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13626 win.document.selection.createRange().pasteHTML(txt);
13628 // no firefox support
13629 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13630 this.execCmd('InsertHTML', txt);
13639 mozKeyPress : function(e){
13641 var c = e.getCharCode(), cmd;
13644 c = String.fromCharCode(c).toLowerCase();
13658 this.cleanUpPaste.defer(100, this);
13666 e.preventDefault();
13674 fixKeys : function(){ // load time branching for fastest keydown performance
13676 return function(e){
13677 var k = e.getKey(), r;
13680 r = this.doc.selection.createRange();
13683 r.pasteHTML('    ');
13690 r = this.doc.selection.createRange();
13692 var target = r.parentElement();
13693 if(!target || target.tagName.toLowerCase() != 'li'){
13695 r.pasteHTML('<br />');
13701 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13702 this.cleanUpPaste.defer(100, this);
13708 }else if(Roo.isOpera){
13709 return function(e){
13710 var k = e.getKey();
13714 this.execCmd('InsertHTML','    ');
13717 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13718 this.cleanUpPaste.defer(100, this);
13723 }else if(Roo.isSafari){
13724 return function(e){
13725 var k = e.getKey();
13729 this.execCmd('InsertText','\t');
13733 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13734 this.cleanUpPaste.defer(100, this);
13742 getAllAncestors: function()
13744 var p = this.getSelectedNode();
13747 a.push(p); // push blank onto stack..
13748 p = this.getParentElement();
13752 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13756 a.push(this.doc.body);
13760 lastSelNode : false,
13763 getSelection : function()
13765 this.assignDocWin();
13766 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13769 getSelectedNode: function()
13771 // this may only work on Gecko!!!
13773 // should we cache this!!!!
13778 var range = this.createRange(this.getSelection()).cloneRange();
13781 var parent = range.parentElement();
13783 var testRange = range.duplicate();
13784 testRange.moveToElementText(parent);
13785 if (testRange.inRange(range)) {
13788 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13791 parent = parent.parentElement;
13796 // is ancestor a text element.
13797 var ac = range.commonAncestorContainer;
13798 if (ac.nodeType == 3) {
13799 ac = ac.parentNode;
13802 var ar = ac.childNodes;
13805 var other_nodes = [];
13806 var has_other_nodes = false;
13807 for (var i=0;i<ar.length;i++) {
13808 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13811 // fullly contained node.
13813 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13818 // probably selected..
13819 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13820 other_nodes.push(ar[i]);
13824 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13829 has_other_nodes = true;
13831 if (!nodes.length && other_nodes.length) {
13832 nodes= other_nodes;
13834 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13840 createRange: function(sel)
13842 // this has strange effects when using with
13843 // top toolbar - not sure if it's a great idea.
13844 //this.editor.contentWindow.focus();
13845 if (typeof sel != "undefined") {
13847 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13849 return this.doc.createRange();
13852 return this.doc.createRange();
13855 getParentElement: function()
13858 this.assignDocWin();
13859 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13861 var range = this.createRange(sel);
13864 var p = range.commonAncestorContainer;
13865 while (p.nodeType == 3) { // text node
13876 * Range intersection.. the hard stuff...
13880 * [ -- selected range --- ]
13884 * if end is before start or hits it. fail.
13885 * if start is after end or hits it fail.
13887 * if either hits (but other is outside. - then it's not
13893 // @see http://www.thismuchiknow.co.uk/?p=64.
13894 rangeIntersectsNode : function(range, node)
13896 var nodeRange = node.ownerDocument.createRange();
13898 nodeRange.selectNode(node);
13900 nodeRange.selectNodeContents(node);
13903 var rangeStartRange = range.cloneRange();
13904 rangeStartRange.collapse(true);
13906 var rangeEndRange = range.cloneRange();
13907 rangeEndRange.collapse(false);
13909 var nodeStartRange = nodeRange.cloneRange();
13910 nodeStartRange.collapse(true);
13912 var nodeEndRange = nodeRange.cloneRange();
13913 nodeEndRange.collapse(false);
13915 return rangeStartRange.compareBoundaryPoints(
13916 Range.START_TO_START, nodeEndRange) == -1 &&
13917 rangeEndRange.compareBoundaryPoints(
13918 Range.START_TO_START, nodeStartRange) == 1;
13922 rangeCompareNode : function(range, node)
13924 var nodeRange = node.ownerDocument.createRange();
13926 nodeRange.selectNode(node);
13928 nodeRange.selectNodeContents(node);
13932 range.collapse(true);
13934 nodeRange.collapse(true);
13936 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13937 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13939 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13941 var nodeIsBefore = ss == 1;
13942 var nodeIsAfter = ee == -1;
13944 if (nodeIsBefore && nodeIsAfter)
13946 if (!nodeIsBefore && nodeIsAfter)
13947 return 1; //right trailed.
13949 if (nodeIsBefore && !nodeIsAfter)
13950 return 2; // left trailed.
13955 // private? - in a new class?
13956 cleanUpPaste : function()
13958 // cleans up the whole document..
13959 Roo.log('cleanuppaste');
13960 this.cleanUpChildren(this.doc.body);
13961 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13962 if (clean != this.doc.body.innerHTML) {
13963 this.doc.body.innerHTML = clean;
13968 cleanWordChars : function(input) {// change the chars to hex code
13969 var he = Roo.HtmlEditorCore;
13971 var output = input;
13972 Roo.each(he.swapCodes, function(sw) {
13973 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13975 output = output.replace(swapper, sw[1]);
13982 cleanUpChildren : function (n)
13984 if (!n.childNodes.length) {
13987 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13988 this.cleanUpChild(n.childNodes[i]);
13995 cleanUpChild : function (node)
13998 //console.log(node);
13999 if (node.nodeName == "#text") {
14000 // clean up silly Windows -- stuff?
14003 if (node.nodeName == "#comment") {
14004 node.parentNode.removeChild(node);
14005 // clean up silly Windows -- stuff?
14009 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
14011 node.parentNode.removeChild(node);
14016 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
14018 // remove <a name=....> as rendering on yahoo mailer is borked with this.
14019 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
14021 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
14022 // remove_keep_children = true;
14025 if (remove_keep_children) {
14026 this.cleanUpChildren(node);
14027 // inserts everything just before this node...
14028 while (node.childNodes.length) {
14029 var cn = node.childNodes[0];
14030 node.removeChild(cn);
14031 node.parentNode.insertBefore(cn, node);
14033 node.parentNode.removeChild(node);
14037 if (!node.attributes || !node.attributes.length) {
14038 this.cleanUpChildren(node);
14042 function cleanAttr(n,v)
14045 if (v.match(/^\./) || v.match(/^\//)) {
14048 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
14051 if (v.match(/^#/)) {
14054 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
14055 node.removeAttribute(n);
14059 function cleanStyle(n,v)
14061 if (v.match(/expression/)) { //XSS?? should we even bother..
14062 node.removeAttribute(n);
14065 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
14066 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
14069 var parts = v.split(/;/);
14072 Roo.each(parts, function(p) {
14073 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
14077 var l = p.split(':').shift().replace(/\s+/g,'');
14078 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
14081 if ( cblack.indexOf(l) > -1) {
14082 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14083 //node.removeAttribute(n);
14087 // only allow 'c whitelisted system attributes'
14088 if ( cwhite.length && cwhite.indexOf(l) < 0) {
14089 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
14090 //node.removeAttribute(n);
14100 if (clean.length) {
14101 node.setAttribute(n, clean.join(';'));
14103 node.removeAttribute(n);
14109 for (var i = node.attributes.length-1; i > -1 ; i--) {
14110 var a = node.attributes[i];
14113 if (a.name.toLowerCase().substr(0,2)=='on') {
14114 node.removeAttribute(a.name);
14117 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
14118 node.removeAttribute(a.name);
14121 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
14122 cleanAttr(a.name,a.value); // fixme..
14125 if (a.name == 'style') {
14126 cleanStyle(a.name,a.value);
14129 /// clean up MS crap..
14130 // tecnically this should be a list of valid class'es..
14133 if (a.name == 'class') {
14134 if (a.value.match(/^Mso/)) {
14135 node.className = '';
14138 if (a.value.match(/body/)) {
14139 node.className = '';
14150 this.cleanUpChildren(node);
14156 // hide stuff that is not compatible
14170 * @event specialkey
14174 * @cfg {String} fieldClass @hide
14177 * @cfg {String} focusClass @hide
14180 * @cfg {String} autoCreate @hide
14183 * @cfg {String} inputType @hide
14186 * @cfg {String} invalidClass @hide
14189 * @cfg {String} invalidText @hide
14192 * @cfg {String} msgFx @hide
14195 * @cfg {String} validateOnBlur @hide
14199 Roo.HtmlEditorCore.white = [
14200 'area', 'br', 'img', 'input', 'hr', 'wbr',
14202 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14203 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14204 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14205 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14206 'table', 'ul', 'xmp',
14208 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14211 'dir', 'menu', 'ol', 'ul', 'dl',
14217 Roo.HtmlEditorCore.black = [
14218 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14220 'base', 'basefont', 'bgsound', 'blink', 'body',
14221 'frame', 'frameset', 'head', 'html', 'ilayer',
14222 'iframe', 'layer', 'link', 'meta', 'object',
14223 'script', 'style' ,'title', 'xml' // clean later..
14225 Roo.HtmlEditorCore.clean = [
14226 'script', 'style', 'title', 'xml'
14228 Roo.HtmlEditorCore.remove = [
14233 Roo.HtmlEditorCore.ablack = [
14237 Roo.HtmlEditorCore.aclean = [
14238 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14242 Roo.HtmlEditorCore.pwhite= [
14243 'http', 'https', 'mailto'
14246 // white listed style attributes.
14247 Roo.HtmlEditorCore.cwhite= [
14248 // 'text-align', /// default is to allow most things..
14254 // black listed style attributes.
14255 Roo.HtmlEditorCore.cblack= [
14256 // 'font-size' -- this can be set by the project
14260 Roo.HtmlEditorCore.swapCodes =[
14273 * @class Roo.bootstrap.Table.AbstractSelectionModel
14274 * @extends Roo.util.Observable
14275 * Abstract base class for grid SelectionModels. It provides the interface that should be
14276 * implemented by descendant classes. This class should not be directly instantiated.
14279 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14280 this.locked = false;
14281 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14285 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14286 /** @ignore Called by the grid automatically. Do not call directly. */
14287 init : function(grid){
14293 * Locks the selections.
14296 this.locked = true;
14300 * Unlocks the selections.
14302 unlock : function(){
14303 this.locked = false;
14307 * Returns true if the selections are locked.
14308 * @return {Boolean}
14310 isLocked : function(){
14311 return this.locked;
14315 * @class Roo.bootstrap.Table.ColumnModel
14316 * @extends Roo.util.Observable
14317 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14318 * the columns in the table.
14321 * @param {Object} config An Array of column config objects. See this class's
14322 * config objects for details.
14324 Roo.bootstrap.Table.ColumnModel = function(config){
14326 * The config passed into the constructor
14328 this.config = config;
14331 // if no id, create one
14332 // if the column does not have a dataIndex mapping,
14333 // map it to the order it is in the config
14334 for(var i = 0, len = config.length; i < len; i++){
14336 if(typeof c.dataIndex == "undefined"){
14339 if(typeof c.renderer == "string"){
14340 c.renderer = Roo.util.Format[c.renderer];
14342 if(typeof c.id == "undefined"){
14345 // if(c.editor && c.editor.xtype){
14346 // c.editor = Roo.factory(c.editor, Roo.grid);
14348 // if(c.editor && c.editor.isFormField){
14349 // c.editor = new Roo.grid.GridEditor(c.editor);
14352 this.lookup[c.id] = c;
14356 * The width of columns which have no width specified (defaults to 100)
14359 this.defaultWidth = 100;
14362 * Default sortable of columns which have no sortable specified (defaults to false)
14365 this.defaultSortable = false;
14369 * @event widthchange
14370 * Fires when the width of a column changes.
14371 * @param {ColumnModel} this
14372 * @param {Number} columnIndex The column index
14373 * @param {Number} newWidth The new width
14375 "widthchange": true,
14377 * @event headerchange
14378 * Fires when the text of a header changes.
14379 * @param {ColumnModel} this
14380 * @param {Number} columnIndex The column index
14381 * @param {Number} newText The new header text
14383 "headerchange": true,
14385 * @event hiddenchange
14386 * Fires when a column is hidden or "unhidden".
14387 * @param {ColumnModel} this
14388 * @param {Number} columnIndex The column index
14389 * @param {Boolean} hidden true if hidden, false otherwise
14391 "hiddenchange": true,
14393 * @event columnmoved
14394 * Fires when a column is moved.
14395 * @param {ColumnModel} this
14396 * @param {Number} oldIndex
14397 * @param {Number} newIndex
14399 "columnmoved" : true,
14401 * @event columlockchange
14402 * Fires when a column's locked state is changed
14403 * @param {ColumnModel} this
14404 * @param {Number} colIndex
14405 * @param {Boolean} locked true if locked
14407 "columnlockchange" : true
14409 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14411 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14413 * @cfg {String} header The header text to display in the Grid view.
14416 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14417 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14418 * specified, the column's index is used as an index into the Record's data Array.
14421 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14422 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14425 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14426 * Defaults to the value of the {@link #defaultSortable} property.
14427 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14430 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14433 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14436 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14439 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14442 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14443 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14444 * default renderer uses the raw data value.
14447 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14451 * Returns the id of the column at the specified index.
14452 * @param {Number} index The column index
14453 * @return {String} the id
14455 getColumnId : function(index){
14456 return this.config[index].id;
14460 * Returns the column for a specified id.
14461 * @param {String} id The column id
14462 * @return {Object} the column
14464 getColumnById : function(id){
14465 return this.lookup[id];
14470 * Returns the column for a specified dataIndex.
14471 * @param {String} dataIndex The column dataIndex
14472 * @return {Object|Boolean} the column or false if not found
14474 getColumnByDataIndex: function(dataIndex){
14475 var index = this.findColumnIndex(dataIndex);
14476 return index > -1 ? this.config[index] : false;
14480 * Returns the index for a specified column id.
14481 * @param {String} id The column id
14482 * @return {Number} the index, or -1 if not found
14484 getIndexById : function(id){
14485 for(var i = 0, len = this.config.length; i < len; i++){
14486 if(this.config[i].id == id){
14494 * Returns the index for a specified column dataIndex.
14495 * @param {String} dataIndex The column dataIndex
14496 * @return {Number} the index, or -1 if not found
14499 findColumnIndex : function(dataIndex){
14500 for(var i = 0, len = this.config.length; i < len; i++){
14501 if(this.config[i].dataIndex == dataIndex){
14509 moveColumn : function(oldIndex, newIndex){
14510 var c = this.config[oldIndex];
14511 this.config.splice(oldIndex, 1);
14512 this.config.splice(newIndex, 0, c);
14513 this.dataMap = null;
14514 this.fireEvent("columnmoved", this, oldIndex, newIndex);
14517 isLocked : function(colIndex){
14518 return this.config[colIndex].locked === true;
14521 setLocked : function(colIndex, value, suppressEvent){
14522 if(this.isLocked(colIndex) == value){
14525 this.config[colIndex].locked = value;
14526 if(!suppressEvent){
14527 this.fireEvent("columnlockchange", this, colIndex, value);
14531 getTotalLockedWidth : function(){
14532 var totalWidth = 0;
14533 for(var i = 0; i < this.config.length; i++){
14534 if(this.isLocked(i) && !this.isHidden(i)){
14535 this.totalWidth += this.getColumnWidth(i);
14541 getLockedCount : function(){
14542 for(var i = 0, len = this.config.length; i < len; i++){
14543 if(!this.isLocked(i)){
14550 * Returns the number of columns.
14553 getColumnCount : function(visibleOnly){
14554 if(visibleOnly === true){
14556 for(var i = 0, len = this.config.length; i < len; i++){
14557 if(!this.isHidden(i)){
14563 return this.config.length;
14567 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
14568 * @param {Function} fn
14569 * @param {Object} scope (optional)
14570 * @return {Array} result
14572 getColumnsBy : function(fn, scope){
14574 for(var i = 0, len = this.config.length; i < len; i++){
14575 var c = this.config[i];
14576 if(fn.call(scope||this, c, i) === true){
14584 * Returns true if the specified column is sortable.
14585 * @param {Number} col The column index
14586 * @return {Boolean}
14588 isSortable : function(col){
14589 if(typeof this.config[col].sortable == "undefined"){
14590 return this.defaultSortable;
14592 return this.config[col].sortable;
14596 * Returns the rendering (formatting) function defined for the column.
14597 * @param {Number} col The column index.
14598 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
14600 getRenderer : function(col){
14601 if(!this.config[col].renderer){
14602 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
14604 return this.config[col].renderer;
14608 * Sets the rendering (formatting) function for a column.
14609 * @param {Number} col The column index
14610 * @param {Function} fn The function to use to process the cell's raw data
14611 * to return HTML markup for the grid view. The render function is called with
14612 * the following parameters:<ul>
14613 * <li>Data value.</li>
14614 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
14615 * <li>css A CSS style string to apply to the table cell.</li>
14616 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
14617 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
14618 * <li>Row index</li>
14619 * <li>Column index</li>
14620 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
14622 setRenderer : function(col, fn){
14623 this.config[col].renderer = fn;
14627 * Returns the width for the specified column.
14628 * @param {Number} col The column index
14631 getColumnWidth : function(col){
14632 return this.config[col].width * 1 || this.defaultWidth;
14636 * Sets the width for a column.
14637 * @param {Number} col The column index
14638 * @param {Number} width The new width
14640 setColumnWidth : function(col, width, suppressEvent){
14641 this.config[col].width = width;
14642 this.totalWidth = null;
14643 if(!suppressEvent){
14644 this.fireEvent("widthchange", this, col, width);
14649 * Returns the total width of all columns.
14650 * @param {Boolean} includeHidden True to include hidden column widths
14653 getTotalWidth : function(includeHidden){
14654 if(!this.totalWidth){
14655 this.totalWidth = 0;
14656 for(var i = 0, len = this.config.length; i < len; i++){
14657 if(includeHidden || !this.isHidden(i)){
14658 this.totalWidth += this.getColumnWidth(i);
14662 return this.totalWidth;
14666 * Returns the header for the specified column.
14667 * @param {Number} col The column index
14670 getColumnHeader : function(col){
14671 return this.config[col].header;
14675 * Sets the header for a column.
14676 * @param {Number} col The column index
14677 * @param {String} header The new header
14679 setColumnHeader : function(col, header){
14680 this.config[col].header = header;
14681 this.fireEvent("headerchange", this, col, header);
14685 * Returns the tooltip for the specified column.
14686 * @param {Number} col The column index
14689 getColumnTooltip : function(col){
14690 return this.config[col].tooltip;
14693 * Sets the tooltip for a column.
14694 * @param {Number} col The column index
14695 * @param {String} tooltip The new tooltip
14697 setColumnTooltip : function(col, tooltip){
14698 this.config[col].tooltip = tooltip;
14702 * Returns the dataIndex for the specified column.
14703 * @param {Number} col The column index
14706 getDataIndex : function(col){
14707 return this.config[col].dataIndex;
14711 * Sets the dataIndex for a column.
14712 * @param {Number} col The column index
14713 * @param {Number} dataIndex The new dataIndex
14715 setDataIndex : function(col, dataIndex){
14716 this.config[col].dataIndex = dataIndex;
14722 * Returns true if the cell is editable.
14723 * @param {Number} colIndex The column index
14724 * @param {Number} rowIndex The row index
14725 * @return {Boolean}
14727 isCellEditable : function(colIndex, rowIndex){
14728 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
14732 * Returns the editor defined for the cell/column.
14733 * return false or null to disable editing.
14734 * @param {Number} colIndex The column index
14735 * @param {Number} rowIndex The row index
14738 getCellEditor : function(colIndex, rowIndex){
14739 return this.config[colIndex].editor;
14743 * Sets if a column is editable.
14744 * @param {Number} col The column index
14745 * @param {Boolean} editable True if the column is editable
14747 setEditable : function(col, editable){
14748 this.config[col].editable = editable;
14753 * Returns true if the column is hidden.
14754 * @param {Number} colIndex The column index
14755 * @return {Boolean}
14757 isHidden : function(colIndex){
14758 return this.config[colIndex].hidden;
14763 * Returns true if the column width cannot be changed
14765 isFixed : function(colIndex){
14766 return this.config[colIndex].fixed;
14770 * Returns true if the column can be resized
14771 * @return {Boolean}
14773 isResizable : function(colIndex){
14774 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
14777 * Sets if a column is hidden.
14778 * @param {Number} colIndex The column index
14779 * @param {Boolean} hidden True if the column is hidden
14781 setHidden : function(colIndex, hidden){
14782 this.config[colIndex].hidden = hidden;
14783 this.totalWidth = null;
14784 this.fireEvent("hiddenchange", this, colIndex, hidden);
14788 * Sets the editor for a column.
14789 * @param {Number} col The column index
14790 * @param {Object} editor The editor object
14792 setEditor : function(col, editor){
14793 this.config[col].editor = editor;
14797 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
14798 if(typeof value == "string" && value.length < 1){
14804 // Alias for backwards compatibility
14805 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
14808 * @extends Roo.bootstrap.Table.AbstractSelectionModel
14809 * @class Roo.bootstrap.Table.RowSelectionModel
14810 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
14811 * It supports multiple selections and keyboard selection/navigation.
14813 * @param {Object} config
14816 Roo.bootstrap.Table.RowSelectionModel = function(config){
14817 Roo.apply(this, config);
14818 this.selections = new Roo.util.MixedCollection(false, function(o){
14823 this.lastActive = false;
14827 * @event selectionchange
14828 * Fires when the selection changes
14829 * @param {SelectionModel} this
14831 "selectionchange" : true,
14833 * @event afterselectionchange
14834 * Fires after the selection changes (eg. by key press or clicking)
14835 * @param {SelectionModel} this
14837 "afterselectionchange" : true,
14839 * @event beforerowselect
14840 * Fires when a row is selected being selected, return false to cancel.
14841 * @param {SelectionModel} this
14842 * @param {Number} rowIndex The selected index
14843 * @param {Boolean} keepExisting False if other selections will be cleared
14845 "beforerowselect" : true,
14848 * Fires when a row is selected.
14849 * @param {SelectionModel} this
14850 * @param {Number} rowIndex The selected index
14851 * @param {Roo.data.Record} r The record
14853 "rowselect" : true,
14855 * @event rowdeselect
14856 * Fires when a row is deselected.
14857 * @param {SelectionModel} this
14858 * @param {Number} rowIndex The selected index
14860 "rowdeselect" : true
14862 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
14863 this.locked = false;
14866 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
14868 * @cfg {Boolean} singleSelect
14869 * True to allow selection of only one row at a time (defaults to false)
14871 singleSelect : false,
14874 initEvents : function(){
14876 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
14877 this.grid.on("mousedown", this.handleMouseDown, this);
14878 }else{ // allow click to work like normal
14879 this.grid.on("rowclick", this.handleDragableRowClick, this);
14882 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
14883 "up" : function(e){
14885 this.selectPrevious(e.shiftKey);
14886 }else if(this.last !== false && this.lastActive !== false){
14887 var last = this.last;
14888 this.selectRange(this.last, this.lastActive-1);
14889 this.grid.getView().focusRow(this.lastActive);
14890 if(last !== false){
14894 this.selectFirstRow();
14896 this.fireEvent("afterselectionchange", this);
14898 "down" : function(e){
14900 this.selectNext(e.shiftKey);
14901 }else if(this.last !== false && this.lastActive !== false){
14902 var last = this.last;
14903 this.selectRange(this.last, this.lastActive+1);
14904 this.grid.getView().focusRow(this.lastActive);
14905 if(last !== false){
14909 this.selectFirstRow();
14911 this.fireEvent("afterselectionchange", this);
14916 var view = this.grid.view;
14917 view.on("refresh", this.onRefresh, this);
14918 view.on("rowupdated", this.onRowUpdated, this);
14919 view.on("rowremoved", this.onRemove, this);
14923 onRefresh : function(){
14924 var ds = this.grid.dataSource, i, v = this.grid.view;
14925 var s = this.selections;
14926 s.each(function(r){
14927 if((i = ds.indexOfId(r.id)) != -1){
14936 onRemove : function(v, index, r){
14937 this.selections.remove(r);
14941 onRowUpdated : function(v, index, r){
14942 if(this.isSelected(r)){
14943 v.onRowSelect(index);
14949 * @param {Array} records The records to select
14950 * @param {Boolean} keepExisting (optional) True to keep existing selections
14952 selectRecords : function(records, keepExisting){
14954 this.clearSelections();
14956 var ds = this.grid.dataSource;
14957 for(var i = 0, len = records.length; i < len; i++){
14958 this.selectRow(ds.indexOf(records[i]), true);
14963 * Gets the number of selected rows.
14966 getCount : function(){
14967 return this.selections.length;
14971 * Selects the first row in the grid.
14973 selectFirstRow : function(){
14978 * Select the last row.
14979 * @param {Boolean} keepExisting (optional) True to keep existing selections
14981 selectLastRow : function(keepExisting){
14982 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
14986 * Selects the row immediately following the last selected row.
14987 * @param {Boolean} keepExisting (optional) True to keep existing selections
14989 selectNext : function(keepExisting){
14990 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
14991 this.selectRow(this.last+1, keepExisting);
14992 this.grid.getView().focusRow(this.last);
14997 * Selects the row that precedes the last selected row.
14998 * @param {Boolean} keepExisting (optional) True to keep existing selections
15000 selectPrevious : function(keepExisting){
15002 this.selectRow(this.last-1, keepExisting);
15003 this.grid.getView().focusRow(this.last);
15008 * Returns the selected records
15009 * @return {Array} Array of selected records
15011 getSelections : function(){
15012 return [].concat(this.selections.items);
15016 * Returns the first selected record.
15019 getSelected : function(){
15020 return this.selections.itemAt(0);
15025 * Clears all selections.
15027 clearSelections : function(fast){
15028 if(this.locked) return;
15030 var ds = this.grid.dataSource;
15031 var s = this.selections;
15032 s.each(function(r){
15033 this.deselectRow(ds.indexOfId(r.id));
15037 this.selections.clear();
15044 * Selects all rows.
15046 selectAll : function(){
15047 if(this.locked) return;
15048 this.selections.clear();
15049 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15050 this.selectRow(i, true);
15055 * Returns True if there is a selection.
15056 * @return {Boolean}
15058 hasSelection : function(){
15059 return this.selections.length > 0;
15063 * Returns True if the specified row is selected.
15064 * @param {Number/Record} record The record or index of the record to check
15065 * @return {Boolean}
15067 isSelected : function(index){
15068 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15069 return (r && this.selections.key(r.id) ? true : false);
15073 * Returns True if the specified record id is selected.
15074 * @param {String} id The id of record to check
15075 * @return {Boolean}
15077 isIdSelected : function(id){
15078 return (this.selections.key(id) ? true : false);
15082 handleMouseDown : function(e, t){
15083 var view = this.grid.getView(), rowIndex;
15084 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15087 if(e.shiftKey && this.last !== false){
15088 var last = this.last;
15089 this.selectRange(last, rowIndex, e.ctrlKey);
15090 this.last = last; // reset the last
15091 view.focusRow(rowIndex);
15093 var isSelected = this.isSelected(rowIndex);
15094 if(e.button !== 0 && isSelected){
15095 view.focusRow(rowIndex);
15096 }else if(e.ctrlKey && isSelected){
15097 this.deselectRow(rowIndex);
15098 }else if(!isSelected){
15099 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15100 view.focusRow(rowIndex);
15103 this.fireEvent("afterselectionchange", this);
15106 handleDragableRowClick : function(grid, rowIndex, e)
15108 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15109 this.selectRow(rowIndex, false);
15110 grid.view.focusRow(rowIndex);
15111 this.fireEvent("afterselectionchange", this);
15116 * Selects multiple rows.
15117 * @param {Array} rows Array of the indexes of the row to select
15118 * @param {Boolean} keepExisting (optional) True to keep existing selections
15120 selectRows : function(rows, keepExisting){
15122 this.clearSelections();
15124 for(var i = 0, len = rows.length; i < len; i++){
15125 this.selectRow(rows[i], true);
15130 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15131 * @param {Number} startRow The index of the first row in the range
15132 * @param {Number} endRow The index of the last row in the range
15133 * @param {Boolean} keepExisting (optional) True to retain existing selections
15135 selectRange : function(startRow, endRow, keepExisting){
15136 if(this.locked) return;
15138 this.clearSelections();
15140 if(startRow <= endRow){
15141 for(var i = startRow; i <= endRow; i++){
15142 this.selectRow(i, true);
15145 for(var i = startRow; i >= endRow; i--){
15146 this.selectRow(i, true);
15152 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15153 * @param {Number} startRow The index of the first row in the range
15154 * @param {Number} endRow The index of the last row in the range
15156 deselectRange : function(startRow, endRow, preventViewNotify){
15157 if(this.locked) return;
15158 for(var i = startRow; i <= endRow; i++){
15159 this.deselectRow(i, preventViewNotify);
15165 * @param {Number} row The index of the row to select
15166 * @param {Boolean} keepExisting (optional) True to keep existing selections
15168 selectRow : function(index, keepExisting, preventViewNotify){
15169 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15170 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15171 if(!keepExisting || this.singleSelect){
15172 this.clearSelections();
15174 var r = this.grid.dataSource.getAt(index);
15175 this.selections.add(r);
15176 this.last = this.lastActive = index;
15177 if(!preventViewNotify){
15178 this.grid.getView().onRowSelect(index);
15180 this.fireEvent("rowselect", this, index, r);
15181 this.fireEvent("selectionchange", this);
15187 * @param {Number} row The index of the row to deselect
15189 deselectRow : function(index, preventViewNotify){
15190 if(this.locked) return;
15191 if(this.last == index){
15194 if(this.lastActive == index){
15195 this.lastActive = false;
15197 var r = this.grid.dataSource.getAt(index);
15198 this.selections.remove(r);
15199 if(!preventViewNotify){
15200 this.grid.getView().onRowDeselect(index);
15202 this.fireEvent("rowdeselect", this, index);
15203 this.fireEvent("selectionchange", this);
15207 restoreLast : function(){
15209 this.last = this._last;
15214 acceptsNav : function(row, col, cm){
15215 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15219 onEditorKey : function(field, e){
15220 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15225 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15227 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15229 }else if(k == e.ENTER && !e.ctrlKey){
15233 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15235 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15237 }else if(k == e.ESC){
15241 g.startEditing(newCell[0], newCell[1]);