4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
21 * Do not use directly - it does not do anything..
22 * @param {Object} config The config object
27 Roo.bootstrap.Component = function(config){
28 Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
34 allowDomMove : false, // to stop relocations in parent onRender...
42 initEvents : function() { },
48 can_build_overlaid : true,
55 // returns the parent component..
56 return Roo.ComponentMgr.get(this.parentId)
62 onRender : function(ct, position)
64 // Roo.log("Call onRender: " + this.xtype);
66 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
69 if (this.el.attr('xtype')) {
70 this.el.attr('xtypex', this.el.attr('xtype'));
71 this.el.dom.removeAttribute('xtype');
81 var cfg = Roo.apply({}, this.getAutoCreate());
84 // fill in the extra attributes
85 if (this.xattr && typeof(this.xattr) =='object') {
86 for (var i in this.xattr) {
87 cfg[i] = this.xattr[i];
92 cfg.dataId = this.dataId;
96 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
99 if (this.style) { // fixme needs to support more complex style data.
100 cfg.style = this.style;
104 cfg.name = this.name;
107 this.el = ct.createChild(cfg, position);
109 if(this.tabIndex !== undefined){
110 this.el.dom.setAttribute('tabIndex', this.tabIndex);
117 getChildContainer : function()
123 addxtype : function(tree,cntr)
127 cn = Roo.factory(tree);
129 cn.parentType = this.xtype; //??
130 cn.parentId = this.id;
132 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
134 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
136 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
138 var build_from_html = Roo.XComponent.build_from_html;
140 var is_body = (tree.xtype == 'Body') ;
142 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
144 var self_cntr_el = Roo.get(this[cntr]());
146 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148 return this.addxtypeChild(tree,cntr);
151 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
154 return this.addxtypeChild(Roo.apply({}, tree),cntr);
157 Roo.log('skipping render');
165 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
171 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
175 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
180 addxtypeChild : function (tree, cntr)
182 Roo.log('addxtypeChild:' + cntr);
184 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
187 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188 (typeof(tree['flexy:foreach']) != 'undefined');
193 // render the element if it's not BODY.
194 if (tree.xtype != 'Body') {
196 cn = Roo.factory(tree);
198 cn.parentType = this.xtype; //??
199 cn.parentId = this.id;
201 var build_from_html = Roo.XComponent.build_from_html;
204 // does the container contain child eleemnts with 'xtype' attributes.
205 // that match this xtype..
206 // note - when we render we create these as well..
207 // so we should check to see if body has xtype set.
208 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
210 var self_cntr_el = Roo.get(this[cntr]());
211 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
213 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
220 //echild.dom.removeAttribute('xtype');
222 Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229 // if object has flexy:if - then it may or may not be rendered.
230 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
231 // skip a flexy if element.
232 Roo.log('skipping render');
235 // actually if flexy:foreach is found, we really want to create
236 // multiple copies here...
238 //Roo.log(this[cntr]());
239 cn.render(this[cntr]());
241 // then add the element..
248 if (typeof (tree.menu) != 'undefined') {
249 tree.menu.parentType = cn.xtype;
250 tree.menu.triggerEl = cn.el;
251 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
255 if (!tree.items || !tree.items.length) {
259 var items = tree.items;
262 //Roo.log(items.length);
264 for(var i =0;i < items.length;i++) {
265 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
286 * @class Roo.bootstrap.Body
287 * @extends Roo.bootstrap.Component
288 * Bootstrap Body class
292 * @param {Object} config The config object
295 Roo.bootstrap.Body = function(config){
296 Roo.bootstrap.Body.superclass.constructor.call(this, config);
297 this.el = Roo.get(document.body);
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
305 onRender : function(ct, position){
308 //this.el.addClass([this.fieldClass, this.cls]);
326 * @class Roo.bootstrap.ButtonGroup
327 * @extends Roo.bootstrap.Component
328 * Bootstrap ButtonGroup class
329 * @cfg {String} size lg | sm | xs (default empty normal)
330 * @cfg {String} align vertical | justified (default none)
331 * @cfg {String} direction up | down (default down)
332 * @cfg {Boolean} toolbar false | true
333 * @cfg {Boolean} btn true | false
338 * @param {Object} config The config object
341 Roo.bootstrap.ButtonGroup = function(config){
342 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
353 getAutoCreate : function(){
359 cfg.html = this.html || cfg.html;
370 if (['vertical','justified'].indexOf(this.align)!==-1) {
371 cfg.cls = 'btn-group-' + this.align;
373 if (this.align == 'justified') {
374 console.log(this.items);
378 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379 cfg.cls += ' btn-group-' + this.size;
382 if (this.direction == 'up') {
383 cfg.cls += ' dropup' ;
399 * @class Roo.bootstrap.Button
400 * @extends Roo.bootstrap.Component
401 * Bootstrap Button class
402 * @cfg {String} html The button content
403 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404 * @cfg {String} size empty | lg | sm | xs
405 * @cfg {String} tag empty | a | input | submit
406 * @cfg {String} href empty or href
407 * @cfg {Boolean} disabled false | true
408 * @cfg {Boolean} isClose false | true
409 * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410 * @cfg {String} badge text for badge
411 * @cfg {String} theme default (or empty) | glow
412 * @cfg {Boolean} inverse false | true
413 * @cfg {Boolean} toggle false | true
414 * @cfg {String} ontext text for on toggle state
415 * @cfg {String} offtext text for off toggle state
416 * @cfg {Boolean} defaulton true | false
417 * @cfg {Boolean} preventDefault (true | false) default true
418 * @cfg {Boolean} removeClass true | false remove the standard class..
419 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
422 * Create a new button
423 * @param {Object} config The config object
427 Roo.bootstrap.Button = function(config){
428 Roo.bootstrap.Button.superclass.constructor.call(this, config);
433 * When a butotn is pressed
434 * @param {Roo.EventObject} e
439 * After the button has been toggles
440 * @param {Roo.EventObject} e
441 * @param {boolean} pressed (also available as button.pressed)
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
465 preventDefault: true,
474 getAutoCreate : function(){
482 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
488 cfg.html = this.html || cfg.html;
490 if (this.toggle == true) {
493 cls: 'slider-frame roo-button',
498 'data-off-text':'OFF',
499 cls: 'slider-button',
505 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506 cfg.cls += ' '+this.weight;
515 cfg["aria-hidden"] = true;
517 cfg.html = "×";
523 if (this.theme==='default') {
524 cfg.cls = 'btn roo-button';
526 //if (this.parentType != 'Navbar') {
527 this.weight = this.weight.length ? this.weight : 'default';
529 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
531 cfg.cls += ' btn-' + this.weight;
533 } else if (this.theme==='glow') {
536 cfg.cls = 'btn-glow roo-button';
538 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
540 cfg.cls += ' ' + this.weight;
546 this.cls += ' inverse';
551 cfg.cls += ' active';
555 cfg.disabled = 'disabled';
559 Roo.log('changing to ul' );
561 this.glyphicon = 'caret';
564 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
566 //gsRoo.log(this.parentType);
567 if (this.parentType === 'Navbar' && !this.parent().bar) {
568 Roo.log('changing to li?');
577 href : this.href || '#'
580 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
581 cfg.cls += ' dropdown';
588 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
590 if (this.glyphicon) {
591 cfg.html = ' ' + cfg.html;
596 cls: 'glyphicon glyphicon-' + this.glyphicon
606 // cfg.cls='btn roo-button';
610 var value = cfg.html;
615 cls: 'glyphicon glyphicon-' + this.glyphicon,
634 cfg.cls += ' dropdown';
635 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
638 if (cfg.tag !== 'a' && this.href !== '') {
639 throw "Tag must be a to set href.";
640 } else if (this.href.length > 0) {
641 cfg.href = this.href;
644 if(this.removeClass){
649 cfg.target = this.target;
654 initEvents: function() {
655 // Roo.log('init events?');
656 // Roo.log(this.el.dom);
657 if (this.el.hasClass('roo-button')) {
658 this.el.on('click', this.onClick, this);
660 this.el.select('.roo-button').on('click', this.onClick, this);
666 onClick : function(e)
672 Roo.log('button on click ');
673 if(this.preventDefault){
676 if (this.pressed === true || this.pressed === false) {
677 this.pressed = !this.pressed;
678 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679 this.fireEvent('toggle', this, e, this.pressed);
683 this.fireEvent('click', this, e);
687 * Enables this button
691 this.disabled = false;
692 this.el.removeClass('disabled');
696 * Disable this button
700 this.disabled = true;
701 this.el.addClass('disabled');
704 * sets the active state on/off,
705 * @param {Boolean} state (optional) Force a particular state
707 setActive : function(v) {
709 this.el[v ? 'addClass' : 'removeClass']('active');
712 * toggles the current active state
714 toggleActive : function()
716 var active = this.el.hasClass('active');
717 this.setActive(!active);
734 * @class Roo.bootstrap.Column
735 * @extends Roo.bootstrap.Component
736 * Bootstrap Column class
737 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739 * @cfg {Number} md colspan out of 12 for computer-sized screens
740 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741 * @cfg {String} html content of column.
744 * Create a new Column
745 * @param {Object} config The config object
748 Roo.bootstrap.Column = function(config){
749 Roo.bootstrap.Column.superclass.constructor.call(this, config);
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
761 getAutoCreate : function(){
762 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
770 ['xs','sm','md','lg'].map(function(size){
771 if (settings[size]) {
772 cfg.cls += ' col-' + size + '-' + settings[size];
775 if (this.html.length) {
776 cfg.html = this.html;
795 * @class Roo.bootstrap.Container
796 * @extends Roo.bootstrap.Component
797 * Bootstrap Container class
798 * @cfg {Boolean} jumbotron is it a jumbotron element
799 * @cfg {String} html content of element
800 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802 * @cfg {String} header content of header (for panel)
803 * @cfg {String} footer content of footer (for panel)
804 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
807 * Create a new Container
808 * @param {Object} config The config object
811 Roo.bootstrap.Container = function(config){
812 Roo.bootstrap.Container.superclass.constructor.call(this, config);
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
825 getChildContainer : function() {
831 if (this.panel.length) {
832 return this.el.select('.panel-body',true).first();
839 getAutoCreate : function(){
845 if (this.jumbotron) {
846 cfg.cls = 'jumbotron';
849 cfg.cls = this.cls + '';
852 if (this.sticky.length) {
854 var bd = Roo.get(document.body);
855 if (!bd.hasClass('bootstrap-sticky')) {
856 bd.addClass('bootstrap-sticky');
857 Roo.select('html',true).setStyle('height', '100%');
860 cfg.cls += 'bootstrap-sticky-' + this.sticky;
864 if (this.well.length) {
868 cfg.cls +=' well well-' +this.well;
878 if (this.panel.length) {
879 cfg.cls += ' panel panel-' + this.panel;
881 if (this.header.length) {
884 cls : 'panel-heading',
900 if (this.footer.length) {
902 cls : 'panel-footer',
910 body.html = this.html || cfg.html;
912 if (!cfg.cls.length) {
913 cfg.cls = 'container';
930 * @class Roo.bootstrap.Img
931 * @extends Roo.bootstrap.Component
932 * Bootstrap Img class
933 * @cfg {Boolean} imgResponsive false | true
934 * @cfg {String} border rounded | circle | thumbnail
935 * @cfg {String} src image source
936 * @cfg {String} alt image alternative text
937 * @cfg {String} href a tag href
938 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
942 * @param {Object} config The config object
945 Roo.bootstrap.Img = function(config){
946 Roo.bootstrap.Img.superclass.constructor.call(this, config);
952 * The img click event for the img.
953 * @param {Roo.EventObject} e
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
967 getAutoCreate : function(){
971 cls: 'img-responsive',
975 cfg.html = this.html || cfg.html;
977 cfg.src = this.src || cfg.src;
979 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980 cfg.cls += ' img-' + this.border;
997 a.target = this.target;
1003 return (this.href) ? a : cfg;
1006 initEvents: function() {
1009 this.el.on('click', this.onClick, this);
1013 onClick : function(e)
1015 Roo.log('img onclick');
1016 this.fireEvent('click', this, e);
1029 * @class Roo.bootstrap.Header
1030 * @extends Roo.bootstrap.Component
1031 * Bootstrap Header class
1032 * @cfg {String} html content of header
1033 * @cfg {Number} level (1|2|3|4|5|6) default 1
1036 * Create a new Header
1037 * @param {Object} config The config object
1041 Roo.bootstrap.Header = function(config){
1042 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1053 getAutoCreate : function(){
1056 tag: 'h' + (1 *this.level),
1057 html: this.html || 'fill in html'
1069 * Ext JS Library 1.1.1
1070 * Copyright(c) 2006-2007, Ext JS, LLC.
1072 * Originally Released Under LGPL - original licence link has changed is not relivant.
1075 * <script type="text/javascript">
1079 * @class Roo.bootstrap.MenuMgr
1080 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1083 Roo.bootstrap.MenuMgr = function(){
1084 var menus, active, groups = {}, attached = false, lastShow = new Date();
1086 // private - called when first menu is created
1089 active = new Roo.util.MixedCollection();
1090 Roo.get(document).addKeyListener(27, function(){
1091 if(active.length > 0){
1099 if(active && active.length > 0){
1100 var c = active.clone();
1110 if(active.length < 1){
1111 Roo.get(document).un("mouseup", onMouseDown);
1119 var last = active.last();
1120 lastShow = new Date();
1123 Roo.get(document).on("mouseup", onMouseDown);
1128 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129 m.parentMenu.activeChild = m;
1130 }else if(last && last.isVisible()){
1131 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1136 function onBeforeHide(m){
1138 m.activeChild.hide();
1140 if(m.autoHideTimer){
1141 clearTimeout(m.autoHideTimer);
1142 delete m.autoHideTimer;
1147 function onBeforeShow(m){
1148 var pm = m.parentMenu;
1149 if(!pm && !m.allowOtherMenus){
1151 }else if(pm && pm.activeChild && active != m){
1152 pm.activeChild.hide();
1157 function onMouseDown(e){
1158 Roo.log("on MouseDown");
1159 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1167 function onBeforeCheck(mi, state){
1169 var g = groups[mi.group];
1170 for(var i = 0, l = g.length; i < l; i++){
1172 g[i].setChecked(false);
1181 * Hides all menus that are currently visible
1183 hideAll : function(){
1188 register : function(menu){
1192 menus[menu.id] = menu;
1193 menu.on("beforehide", onBeforeHide);
1194 menu.on("hide", onHide);
1195 menu.on("beforeshow", onBeforeShow);
1196 menu.on("show", onShow);
1198 if(g && menu.events["checkchange"]){
1202 groups[g].push(menu);
1203 menu.on("checkchange", onCheck);
1208 * Returns a {@link Roo.menu.Menu} object
1209 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210 * be used to generate and return a new Menu instance.
1212 get : function(menu){
1213 if(typeof menu == "string"){ // menu id
1215 }else if(menu.events){ // menu instance
1218 /*else if(typeof menu.length == 'number'){ // array of menu items?
1219 return new Roo.bootstrap.Menu({items:menu});
1220 }else{ // otherwise, must be a config
1221 return new Roo.bootstrap.Menu(menu);
1228 unregister : function(menu){
1229 delete menus[menu.id];
1230 menu.un("beforehide", onBeforeHide);
1231 menu.un("hide", onHide);
1232 menu.un("beforeshow", onBeforeShow);
1233 menu.un("show", onShow);
1235 if(g && menu.events["checkchange"]){
1236 groups[g].remove(menu);
1237 menu.un("checkchange", onCheck);
1242 registerCheckable : function(menuItem){
1243 var g = menuItem.group;
1248 groups[g].push(menuItem);
1249 menuItem.on("beforecheckchange", onBeforeCheck);
1254 unregisterCheckable : function(menuItem){
1255 var g = menuItem.group;
1257 groups[g].remove(menuItem);
1258 menuItem.un("beforecheckchange", onBeforeCheck);
1270 * @class Roo.bootstrap.Menu
1271 * @extends Roo.bootstrap.Component
1272 * Bootstrap Menu class - container for MenuItems
1273 * @cfg {String} type type of menu
1277 * @param {Object} config The config object
1281 Roo.bootstrap.Menu = function(config){
1282 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283 if (this.registerMenu) {
1284 Roo.bootstrap.MenuMgr.register(this);
1289 * Fires before this menu is displayed
1290 * @param {Roo.menu.Menu} this
1295 * Fires before this menu is hidden
1296 * @param {Roo.menu.Menu} this
1301 * Fires after this menu is displayed
1302 * @param {Roo.menu.Menu} this
1307 * Fires after this menu is hidden
1308 * @param {Roo.menu.Menu} this
1313 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314 * @param {Roo.menu.Menu} this
1315 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316 * @param {Roo.EventObject} e
1321 * Fires when the mouse is hovering over this menu
1322 * @param {Roo.menu.Menu} this
1323 * @param {Roo.EventObject} e
1324 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1329 * Fires when the mouse exits this menu
1330 * @param {Roo.menu.Menu} this
1331 * @param {Roo.EventObject} e
1332 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1337 * Fires when a menu item contained in this menu is clicked
1338 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339 * @param {Roo.EventObject} e
1343 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1350 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1353 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1355 registerMenu : true,
1357 menuItems :false, // stores the menu items..
1363 getChildContainer : function() {
1367 getAutoCreate : function(){
1369 //if (['right'].indexOf(this.align)!==-1) {
1370 // cfg.cn[1].cls += ' pull-right'
1374 cls : 'dropdown-menu' ,
1375 style : 'z-index:1000'
1379 if (this.type === 'submenu') {
1380 cfg.cls = 'submenu active'
1385 initEvents : function() {
1387 // Roo.log("ADD event");
1388 // Roo.log(this.triggerEl.dom);
1389 this.triggerEl.on('click', this.onTriggerPress, this);
1390 this.triggerEl.addClass('dropdown-toggle');
1391 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1393 this.el.on("mouseover", this.onMouseOver, this);
1394 this.el.on("mouseout", this.onMouseOut, this);
1398 findTargetItem : function(e){
1399 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1403 //Roo.log(t); Roo.log(t.id);
1405 //Roo.log(this.menuitems);
1406 return this.menuitems.get(t.id);
1408 //return this.items.get(t.menuItemId);
1413 onClick : function(e){
1414 Roo.log("menu.onClick");
1415 var t = this.findTargetItem(e);
1421 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1422 if(t == this.activeItem && t.shouldDeactivate(e)){
1423 this.activeItem.deactivate();
1424 delete this.activeItem;
1428 this.setActiveItem(t, true);
1435 Roo.log('pass click event');
1439 this.fireEvent("click", this, t, e);
1443 onMouseOver : function(e){
1444 var t = this.findTargetItem(e);
1447 // if(t.canActivate && !t.disabled){
1448 // this.setActiveItem(t, true);
1452 this.fireEvent("mouseover", this, e, t);
1454 isVisible : function(){
1455 return !this.hidden;
1457 onMouseOut : function(e){
1458 var t = this.findTargetItem(e);
1461 // if(t == this.activeItem && t.shouldDeactivate(e)){
1462 // this.activeItem.deactivate();
1463 // delete this.activeItem;
1466 this.fireEvent("mouseout", this, e, t);
1471 * Displays this menu relative to another element
1472 * @param {String/HTMLElement/Roo.Element} element The element to align to
1473 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474 * the element (defaults to this.defaultAlign)
1475 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1477 show : function(el, pos, parentMenu){
1478 this.parentMenu = parentMenu;
1482 this.fireEvent("beforeshow", this);
1483 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1486 * Displays this menu at a specific xy position
1487 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1490 showAt : function(xy, parentMenu, /* private: */_e){
1491 this.parentMenu = parentMenu;
1496 this.fireEvent("beforeshow", this);
1498 //xy = this.el.adjustForConstraints(xy);
1500 //this.el.setXY(xy);
1502 this.hideMenuItems();
1503 this.hidden = false;
1504 this.triggerEl.addClass('open');
1506 this.fireEvent("show", this);
1512 this.doFocus.defer(50, this);
1516 doFocus : function(){
1518 this.focusEl.focus();
1523 * Hides this menu and optionally all parent menus
1524 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1526 hide : function(deep){
1528 this.hideMenuItems();
1529 if(this.el && this.isVisible()){
1530 this.fireEvent("beforehide", this);
1531 if(this.activeItem){
1532 this.activeItem.deactivate();
1533 this.activeItem = null;
1535 this.triggerEl.removeClass('open');;
1537 this.fireEvent("hide", this);
1539 if(deep === true && this.parentMenu){
1540 this.parentMenu.hide(true);
1544 onTriggerPress : function(e)
1547 Roo.log('trigger press');
1548 //Roo.log(e.getTarget());
1549 // Roo.log(this.triggerEl.dom);
1550 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1553 if (this.isVisible()) {
1557 this.show(this.triggerEl, false, false);
1566 hideMenuItems : function()
1568 //$(backdrop).remove()
1569 Roo.select('.open',true).each(function(aa) {
1571 aa.removeClass('open');
1572 //var parent = getParent($(this))
1573 //var relatedTarget = { relatedTarget: this }
1575 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576 //if (e.isDefaultPrevented()) return
1577 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1580 addxtypeChild : function (tree, cntr) {
1581 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1583 this.menuitems.add(comp);
1604 * @class Roo.bootstrap.MenuItem
1605 * @extends Roo.bootstrap.Component
1606 * Bootstrap MenuItem class
1607 * @cfg {String} html the menu label
1608 * @cfg {String} href the link
1609 * @cfg {Boolean} preventDefault (true | false) default true
1613 * Create a new MenuItem
1614 * @param {Object} config The config object
1618 Roo.bootstrap.MenuItem = function(config){
1619 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1624 * The raw click event for the entire grid.
1625 * @param {Roo.EventObject} e
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1635 preventDefault: true,
1637 getAutoCreate : function(){
1640 cls: 'dropdown-menu-item',
1650 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1655 initEvents: function() {
1657 //this.el.select('a').on('click', this.onClick, this);
1660 onClick : function(e)
1662 Roo.log('item on click ');
1663 //if(this.preventDefault){
1664 // e.preventDefault();
1666 //this.parent().hideMenuItems();
1668 this.fireEvent('click', this, e);
1687 * @class Roo.bootstrap.MenuSeparator
1688 * @extends Roo.bootstrap.Component
1689 * Bootstrap MenuSeparator class
1692 * Create a new MenuItem
1693 * @param {Object} config The config object
1697 Roo.bootstrap.MenuSeparator = function(config){
1698 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1703 getAutoCreate : function(){
1718 <div class="modal fade">
1719 <div class="modal-dialog">
1720 <div class="modal-content">
1721 <div class="modal-header">
1722 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1723 <h4 class="modal-title">Modal title</h4>
1725 <div class="modal-body">
1726 <p>One fine body…</p>
1728 <div class="modal-footer">
1729 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730 <button type="button" class="btn btn-primary">Save changes</button>
1732 </div><!-- /.modal-content -->
1733 </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1744 * @class Roo.bootstrap.Modal
1745 * @extends Roo.bootstrap.Component
1746 * Bootstrap Modal class
1747 * @cfg {String} title Title of dialog
1748 * @cfg {Array} buttons Array of buttons or standard button set..
1751 * Create a new Modal Dialog
1752 * @param {Object} config The config object
1755 Roo.bootstrap.Modal = function(config){
1756 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1761 * The raw btnclick event for the button
1762 * @param {Roo.EventObject} e
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1770 title : 'test dialog',
1774 onRender : function(ct, position)
1776 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1779 var cfg = Roo.apply({}, this.getAutoCreate());
1782 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1784 //if (!cfg.name.length) {
1788 cfg.cls += ' ' + this.cls;
1791 cfg.style = this.style;
1793 this.el = Roo.get(document.body).createChild(cfg, position);
1795 //var type = this.el.dom.type;
1797 if(this.tabIndex !== undefined){
1798 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1803 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804 this.maskEl.enableDisplayMode("block");
1806 //this.el.addClass("x-dlg-modal");
1809 Roo.each(this.buttons, function(bb) {
1810 b = Roo.apply({}, bb);
1811 b.xns = b.xns || Roo.bootstrap;
1812 b.xtype = b.xtype || 'Button';
1813 if (typeof(b.listeners) == 'undefined') {
1814 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1817 var btn = Roo.factory(b);
1819 btn.onRender(this.el.select('.modal-footer').first());
1823 // render the children.
1826 if(typeof(this.items) != 'undefined'){
1827 var items = this.items;
1830 for(var i =0;i < items.length;i++) {
1831 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1835 this.items = nitems;
1837 //this.el.addClass([this.fieldClass, this.cls]);
1840 getAutoCreate : function(){
1845 html : this.html || ''
1853 cls: "modal-dialog",
1856 cls : "modal-content",
1859 cls : 'modal-header',
1868 cls : 'modal-title',
1876 cls : 'modal-footer'
1892 getChildContainer : function() {
1894 return this.el.select('.modal-body',true).first();
1897 getButtonContainer : function() {
1898 return this.el.select('.modal-footer',true).first();
1901 initEvents : function()
1903 this.el.select('.modal-header .close').on('click', this.hide, this);
1905 // this.addxtype(this);
1909 if (!this.rendered) {
1913 this.el.addClass('on');
1914 this.el.removeClass('fade');
1915 this.el.setStyle('display', 'block');
1916 Roo.get(document.body).addClass("x-body-masked");
1917 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1919 this.el.setStyle('zIndex', '10001');
1920 this.fireEvent('show', this);
1926 Roo.log('Modal hide?!');
1928 Roo.get(document.body).removeClass("x-body-masked");
1929 this.el.removeClass('on');
1930 this.el.addClass('fade');
1931 this.el.setStyle('display', 'none');
1932 this.fireEvent('hide', this);
1934 onButtonClick: function(btn,e)
1937 this.fireEvent('btnclick', btn.name, e);
1942 Roo.apply(Roo.bootstrap.Modal, {
1944 * Button config that displays a single OK button
1953 * Button config that displays Yes and No buttons
1969 * Button config that displays OK and Cancel buttons
1984 * Button config that displays Yes, No and Cancel buttons
2011 * @class Roo.bootstrap.Navbar
2012 * @extends Roo.bootstrap.Component
2013 * Bootstrap Navbar class
2014 * @cfg {Boolean} sidebar has side bar
2015 * @cfg {Boolean} bar is a bar?
2016 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017 * @cfg {String} brand what is brand
2018 * @cfg {Boolean} inverse is inverted color
2019 * @cfg {String} type (nav | pills | tabs)
2020 * @cfg {Boolean} arrangement stacked | justified
2021 * @cfg {String} align (left | right) alignment
2022 * @cfg {String} brand_href href of the brand
2023 * @cfg {Boolean} main (true|false) main nav bar? default false
2024 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2028 * Create a new Navbar
2029 * @param {Object} config The config object
2033 Roo.bootstrap.Navbar = function(config){
2034 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2052 getAutoCreate : function(){
2057 if (this.sidebar === true) {
2065 if (this.bar === true) {
2073 cls: 'navbar-header',
2078 cls: 'navbar-toggle',
2079 'data-toggle': 'collapse',
2084 html: 'Toggle navigation'
2104 cls: 'collapse navbar-collapse'
2109 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2111 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112 cfg.cls += ' navbar-' + this.position;
2113 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2116 if (this.brand !== '') {
2119 href: this.brand_href ? this.brand_href : '#',
2120 cls: 'navbar-brand',
2128 cfg.cls += ' main-nav';
2134 } else if (this.bar === false) {
2137 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2147 if (['tabs','pills'].indexOf(this.type)!==-1) {
2148 cfg.cn[0].cls += ' nav-' + this.type
2150 if (this.type!=='nav') {
2151 Roo.log('nav type must be nav/tabs/pills')
2153 cfg.cn[0].cls += ' navbar-nav'
2156 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157 cfg.cn[0].cls += ' nav-' + this.arrangement;
2160 if (this.align === 'right') {
2161 cfg.cn[0].cls += ' navbar-right';
2164 cfg.cls += ' navbar-inverse';
2172 initEvents :function ()
2174 //Roo.log(this.el.select('.navbar-toggle',true));
2175 this.el.select('.navbar-toggle',true).on('click', function() {
2176 // Roo.log('click');
2177 this.el.select('.navbar-collapse',true).toggleClass('in');
2185 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2187 var size = this.el.getSize();
2188 this.maskEl.setSize(size.width, size.height);
2189 this.maskEl.enableDisplayMode("block");
2198 getChildContainer : function()
2200 if (this.bar === true) {
2201 return this.el.select('.collapse',true).first();
2229 * @class Roo.bootstrap.NavGroup
2230 * @extends Roo.bootstrap.Component
2231 * Bootstrap NavGroup class
2232 * @cfg {String} align left | right
2233 * @cfg {Boolean} inverse false | true
2234 * @cfg {String} type (nav|pills|tab) default nav
2237 * Create a new nav group
2238 * @param {Object} config The config object
2241 Roo.bootstrap.NavGroup = function(config){
2242 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2252 getAutoCreate : function(){
2253 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2260 if (['tabs','pills'].indexOf(this.type)!==-1) {
2261 cfg.cls += ' nav-' + this.type
2263 if (this.type!=='nav') {
2264 Roo.log('nav type must be nav/tabs/pills')
2266 cfg.cls += ' navbar-nav'
2269 if (this.parent().sidebar === true) {
2272 cls: 'dashboard-menu'
2278 if (this.form === true) {
2284 if (this.align === 'right') {
2285 cfg.cls += ' navbar-right';
2287 cfg.cls += ' navbar-left';
2291 if (this.align === 'right') {
2292 cfg.cls += ' navbar-right';
2296 cfg.cls += ' navbar-inverse';
2316 * @class Roo.bootstrap.Navbar.Item
2317 * @extends Roo.bootstrap.Component
2318 * Bootstrap Navbar.Button class
2319 * @cfg {String} href link to
2320 * @cfg {String} html content of button
2321 * @cfg {String} badge text inside badge
2322 * @cfg {String} glyphicon name of glyphicon
2323 * @cfg {String} icon name of font awesome icon
2324 * @cfg {Boolena} active Is item active
2325 * @cfg {Boolean} preventDefault (true | false) default false
2328 * Create a new Navbar Button
2329 * @param {Object} config The config object
2331 Roo.bootstrap.Navbar.Item = function(config){
2332 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2337 * The raw click event for the entire grid.
2338 * @param {Roo.EventObject} e
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
2353 preventDefault : false,
2355 getAutoCreate : function(){
2357 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2359 if (this.parent().parent().sidebar === true) {
2372 cfg.cn[0].html = this.html;
2376 this.cls += ' active';
2380 cfg.cn[0].cls += ' dropdown-toggle';
2381 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2385 cfg.cn[0].tag = 'a',
2386 cfg.cn[0].href = this.href;
2389 if (this.glyphicon) {
2390 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2394 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2406 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2416 if (this.glyphicon) {
2417 if(cfg.html){cfg.html = ' ' + this.html};
2421 cls: 'glyphicon glyphicon-' + this.glyphicon
2426 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2431 cfg.cn[0].html += " <span class='caret'></span>";
2432 //}else if (!this.href) {
2433 // cfg.cn[0].tag='p';
2434 // cfg.cn[0].cls='navbar-text';
2437 cfg.cn[0].href=this.href||'#';
2438 cfg.cn[0].html=this.html;
2441 if (this.badge !== '') {
2444 cfg.cn[0].html + ' ',
2455 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2460 initEvents: function() {
2461 // Roo.log('init events?');
2462 // Roo.log(this.el.dom);
2463 this.el.select('a',true).on('click', this.onClick, this);
2466 onClick : function(e)
2468 if(this.preventDefault){
2472 if(this.fireEvent('click', this, e) === false){
2476 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477 this.onTabsClick(e);
2481 onTabsClick : function(e)
2483 Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484 v.removeClass('active');
2487 this.el.addClass('active');
2489 if(this.href && this.href.substring(0,1) == '#'){
2490 var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2492 Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493 v.removeClass('active');
2496 tab.addClass('active');
2511 * @class Roo.bootstrap.Row
2512 * @extends Roo.bootstrap.Component
2513 * Bootstrap Row class (contains columns...)
2517 * @param {Object} config The config object
2520 Roo.bootstrap.Row = function(config){
2521 Roo.bootstrap.Row.superclass.constructor.call(this, config);
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
2526 getAutoCreate : function(){
2545 * @class Roo.bootstrap.Element
2546 * @extends Roo.bootstrap.Component
2547 * Bootstrap Element class
2548 * @cfg {String} html contents of the element
2549 * @cfg {String} tag tag of the element
2550 * @cfg {String} cls class of the element
2553 * Create a new Element
2554 * @param {Object} config The config object
2557 Roo.bootstrap.Element = function(config){
2558 Roo.bootstrap.Element.superclass.constructor.call(this, config);
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
2568 getAutoCreate : function(){
2593 * @class Roo.bootstrap.Pagination
2594 * @extends Roo.bootstrap.Component
2595 * Bootstrap Pagination class
2596 * @cfg {String} size xs | sm | md | lg
2597 * @cfg {Boolean} inverse false | true
2600 * Create a new Pagination
2601 * @param {Object} config The config object
2604 Roo.bootstrap.Pagination = function(config){
2605 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
2614 getAutoCreate : function(){
2620 cfg.cls += ' inverse';
2626 cfg.cls += " " + this.cls;
2644 * @class Roo.bootstrap.PaginationItem
2645 * @extends Roo.bootstrap.Component
2646 * Bootstrap PaginationItem class
2647 * @cfg {String} html text
2648 * @cfg {String} href the link
2649 * @cfg {Boolean} preventDefault (true | false) default true
2650 * @cfg {Boolean} active (true | false) default false
2654 * Create a new PaginationItem
2655 * @param {Object} config The config object
2659 Roo.bootstrap.PaginationItem = function(config){
2660 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2665 * The raw click event for the entire grid.
2666 * @param {Roo.EventObject} e
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
2676 preventDefault: true,
2680 getAutoCreate : function(){
2686 href : this.href ? this.href : '#',
2687 html : this.html ? this.html : ''
2697 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2703 initEvents: function() {
2705 this.el.on('click', this.onClick, this);
2708 onClick : function(e)
2710 Roo.log('PaginationItem on click ');
2711 if(this.preventDefault){
2715 this.fireEvent('click', this, e);
2731 * @class Roo.bootstrap.Slider
2732 * @extends Roo.bootstrap.Component
2733 * Bootstrap Slider class
2736 * Create a new Slider
2737 * @param {Object} config The config object
2740 Roo.bootstrap.Slider = function(config){
2741 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
2746 getAutoCreate : function(){
2750 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2754 cls: 'ui-slider-handle ui-state-default ui-corner-all'
2772 * @class Roo.bootstrap.Table
2773 * @extends Roo.bootstrap.Component
2774 * Bootstrap Table class
2775 * @cfg {String} cls table class
2776 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777 * @cfg {String} bgcolor Specifies the background color for a table
2778 * @cfg {Number} border Specifies whether the table cells should have borders or not
2779 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780 * @cfg {Number} cellspacing Specifies the space between cells
2781 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783 * @cfg {String} sortable Specifies that the table should be sortable
2784 * @cfg {String} summary Specifies a summary of the content of a table
2785 * @cfg {Number} width Specifies the width of a table
2787 * @cfg {boolean} striped Should the rows be alternative striped
2788 * @cfg {boolean} bordered Add borders to the table
2789 * @cfg {boolean} hover Add hover highlighting
2790 * @cfg {boolean} condensed Format condensed
2791 * @cfg {boolean} responsive Format condensed
2797 * Create a new Table
2798 * @param {Object} config The config object
2801 Roo.bootstrap.Table = function(config){
2802 Roo.bootstrap.Table.superclass.constructor.call(this, config);
2805 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806 this.sm = this.selModel;
2807 this.sm.xmodule = this.xmodule || false;
2809 if (this.cm && typeof(this.cm.config) == 'undefined') {
2810 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811 this.cm = this.colModel;
2812 this.cm.xmodule = this.xmodule || false;
2815 this.store= Roo.factory(this.store, Roo.data);
2816 this.ds = this.store;
2817 this.ds.xmodule = this.xmodule || false;
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
2844 getAutoCreate : function(){
2845 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2854 cfg.cls += ' table-striped';
2857 cfg.cls += ' table-hover';
2859 if (this.bordered) {
2860 cfg.cls += ' table-bordered';
2862 if (this.condensed) {
2863 cfg.cls += ' table-condensed';
2865 if (this.responsive) {
2866 cfg.cls += ' table-responsive';
2873 cfg.cls+= ' ' +this.cls;
2876 // this lot should be simplifed...
2879 cfg.align=this.align;
2882 cfg.bgcolor=this.bgcolor;
2885 cfg.border=this.border;
2887 if (this.cellpadding) {
2888 cfg.cellpadding=this.cellpadding;
2890 if (this.cellspacing) {
2891 cfg.cellspacing=this.cellspacing;
2894 cfg.frame=this.frame;
2897 cfg.rules=this.rules;
2899 if (this.sortable) {
2900 cfg.sortable=this.sortable;
2903 cfg.summary=this.summary;
2906 cfg.width=this.width;
2909 if(this.store || this.cm){
2910 cfg.cn.push(this.renderHeader());
2911 cfg.cn.push(this.renderBody());
2912 cfg.cn.push(this.renderFooter());
2914 cfg.cls+= ' TableGrid';
2920 // initTableGrid : function()
2929 // var cm = this.cm;
2931 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2934 // html: cm.getColumnHeader(i)
2938 // cfg.push(header);
2945 initEvents : function()
2947 if(!this.store || !this.cm){
2952 Roo.log('initEvents with ds!!!!');
2954 Roo.each(this.el.select('thead th.sortable').elements, function(e){
2955 e.on('click', _this.store.load());
2957 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2958 // this.maskEl.enableDisplayMode("block");
2959 // this.maskEl.show();
2961 this.store.on('load', this.onLoad, this);
2962 this.store.on('beforeload', this.onBeforeLoad, this);
2970 renderHeader : function()
2979 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2981 var config = cm.config[i];
2985 html: cm.getColumnHeader(i)
2988 if(typeof(config.dataIndex) != 'undefined'){
2989 c.sort = config.dataIndex;
2992 if(typeof(config.sortable) != 'undefined' && config.sortable){
3002 renderBody : function()
3012 renderFooter : function()
3024 Roo.log('ds onload');
3028 var tbody = this.el.select('tbody', true).first();
3032 if(this.store.getCount() > 0){
3033 this.store.data.each(function(d){
3039 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3040 var renderer = cm.getRenderer(i);
3041 var config = cm.config[i];
3045 if(typeof(renderer) !== 'undefined'){
3046 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3049 if(typeof(value) === 'object'){
3059 html: (typeof(value) === 'object') ? '' : value
3062 if(typeof(config.width) != 'undefined'){
3063 td.width = config.width;
3070 tbody.createChild(row);
3078 Roo.each(renders, function(r){
3079 _this.renderColumn(r);
3083 // if(this.loadMask){
3084 // this.maskEl.hide();
3088 onBeforeLoad : function()
3090 Roo.log('ds onBeforeLoad');
3094 // if(this.loadMask){
3095 // this.maskEl.show();
3101 this.el.select('tbody', true).first().dom.innerHTML = '';
3104 getSelectionModel : function(){
3106 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3108 return this.selModel;
3111 renderColumn : function(r)
3114 r.cfg.render(Roo.get(r.id));
3117 Roo.each(r.cfg.cn, function(c){
3122 _this.renderColumn(child);
3139 * @class Roo.bootstrap.TableCell
3140 * @extends Roo.bootstrap.Component
3141 * Bootstrap TableCell class
3142 * @cfg {String} html cell contain text
3143 * @cfg {String} cls cell class
3144 * @cfg {String} tag cell tag (td|th) default td
3145 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3146 * @cfg {String} align Aligns the content in a cell
3147 * @cfg {String} axis Categorizes cells
3148 * @cfg {String} bgcolor Specifies the background color of a cell
3149 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3150 * @cfg {Number} colspan Specifies the number of columns a cell should span
3151 * @cfg {String} headers Specifies one or more header cells a cell is related to
3152 * @cfg {Number} height Sets the height of a cell
3153 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3154 * @cfg {Number} rowspan Sets the number of rows a cell should span
3155 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3156 * @cfg {String} valign Vertical aligns the content in a cell
3157 * @cfg {Number} width Specifies the width of a cell
3160 * Create a new TableCell
3161 * @param {Object} config The config object
3164 Roo.bootstrap.TableCell = function(config){
3165 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3168 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3188 getAutoCreate : function(){
3189 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3209 cfg.align=this.align
3215 cfg.bgcolor=this.bgcolor
3218 cfg.charoff=this.charoff
3221 cfg.colspan=this.colspan
3224 cfg.headers=this.headers
3227 cfg.height=this.height
3230 cfg.nowrap=this.nowrap
3233 cfg.rowspan=this.rowspan
3236 cfg.scope=this.scope
3239 cfg.valign=this.valign
3242 cfg.width=this.width
3261 * @class Roo.bootstrap.TableRow
3262 * @extends Roo.bootstrap.Component
3263 * Bootstrap TableRow class
3264 * @cfg {String} cls row class
3265 * @cfg {String} align Aligns the content in a table row
3266 * @cfg {String} bgcolor Specifies a background color for a table row
3267 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3268 * @cfg {String} valign Vertical aligns the content in a table row
3271 * Create a new TableRow
3272 * @param {Object} config The config object
3275 Roo.bootstrap.TableRow = function(config){
3276 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3279 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3287 getAutoCreate : function(){
3288 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3298 cfg.align = this.align;
3301 cfg.bgcolor = this.bgcolor;
3304 cfg.charoff = this.charoff;
3307 cfg.valign = this.valign;
3325 * @class Roo.bootstrap.TableBody
3326 * @extends Roo.bootstrap.Component
3327 * Bootstrap TableBody class
3328 * @cfg {String} cls element class
3329 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3330 * @cfg {String} align Aligns the content inside the element
3331 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3332 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3335 * Create a new TableBody
3336 * @param {Object} config The config object
3339 Roo.bootstrap.TableBody = function(config){
3340 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3343 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3351 getAutoCreate : function(){
3352 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3366 cfg.align = this.align;
3369 cfg.charoff = this.charoff;
3372 cfg.valign = this.valign;
3379 // initEvents : function()
3386 // this.store = Roo.factory(this.store, Roo.data);
3387 // this.store.on('load', this.onLoad, this);
3389 // this.store.load();
3393 // onLoad: function ()
3395 // this.fireEvent('load', this);
3405 * Ext JS Library 1.1.1
3406 * Copyright(c) 2006-2007, Ext JS, LLC.
3408 * Originally Released Under LGPL - original licence link has changed is not relivant.
3411 * <script type="text/javascript">
3414 // as we use this in bootstrap.
3415 Roo.namespace('Roo.form');
3417 * @class Roo.form.Action
3418 * Internal Class used to handle form actions
3420 * @param {Roo.form.BasicForm} el The form element or its id
3421 * @param {Object} config Configuration options
3426 // define the action interface
3427 Roo.form.Action = function(form, options){
3429 this.options = options || {};
3432 * Client Validation Failed
3435 Roo.form.Action.CLIENT_INVALID = 'client';
3437 * Server Validation Failed
3440 Roo.form.Action.SERVER_INVALID = 'server';
3442 * Connect to Server Failed
3445 Roo.form.Action.CONNECT_FAILURE = 'connect';
3447 * Reading Data from Server Failed
3450 Roo.form.Action.LOAD_FAILURE = 'load';
3452 Roo.form.Action.prototype = {
3454 failureType : undefined,
3455 response : undefined,
3459 run : function(options){
3464 success : function(response){
3469 handleResponse : function(response){
3473 // default connection failure
3474 failure : function(response){
3476 this.response = response;
3477 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3478 this.form.afterAction(this, false);
3481 processResponse : function(response){
3482 this.response = response;
3483 if(!response.responseText){
3486 this.result = this.handleResponse(response);
3490 // utility functions used internally
3491 getUrl : function(appendParams){
3492 var url = this.options.url || this.form.url || this.form.el.dom.action;
3494 var p = this.getParams();
3496 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3502 getMethod : function(){
3503 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3506 getParams : function(){
3507 var bp = this.form.baseParams;
3508 var p = this.options.params;
3510 if(typeof p == "object"){
3511 p = Roo.urlEncode(Roo.applyIf(p, bp));
3512 }else if(typeof p == 'string' && bp){
3513 p += '&' + Roo.urlEncode(bp);
3516 p = Roo.urlEncode(bp);
3521 createCallback : function(){
3523 success: this.success,
3524 failure: this.failure,
3526 timeout: (this.form.timeout*1000),
3527 upload: this.form.fileUpload ? this.success : undefined
3532 Roo.form.Action.Submit = function(form, options){
3533 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3536 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3539 haveProgress : false,
3540 uploadComplete : false,
3542 // uploadProgress indicator.
3543 uploadProgress : function()
3545 if (!this.form.progressUrl) {
3549 if (!this.haveProgress) {
3550 Roo.MessageBox.progress("Uploading", "Uploading");
3552 if (this.uploadComplete) {
3553 Roo.MessageBox.hide();
3557 this.haveProgress = true;
3559 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3561 var c = new Roo.data.Connection();
3563 url : this.form.progressUrl,
3568 success : function(req){
3569 //console.log(data);
3573 rdata = Roo.decode(req.responseText)
3575 Roo.log("Invalid data from server..");
3579 if (!rdata || !rdata.success) {
3581 Roo.MessageBox.alert(Roo.encode(rdata));
3584 var data = rdata.data;
3586 if (this.uploadComplete) {
3587 Roo.MessageBox.hide();
3592 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3593 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3596 this.uploadProgress.defer(2000,this);
3599 failure: function(data) {
3600 Roo.log('progress url failed ');
3611 // run get Values on the form, so it syncs any secondary forms.
3612 this.form.getValues();
3614 var o = this.options;
3615 var method = this.getMethod();
3616 var isPost = method == 'POST';
3617 if(o.clientValidation === false || this.form.isValid()){
3619 if (this.form.progressUrl) {
3620 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3621 (new Date() * 1) + '' + Math.random());
3626 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3627 form:this.form.el.dom,
3628 url:this.getUrl(!isPost),
3630 params:isPost ? this.getParams() : null,
3631 isUpload: this.form.fileUpload
3634 this.uploadProgress();
3636 }else if (o.clientValidation !== false){ // client validation failed
3637 this.failureType = Roo.form.Action.CLIENT_INVALID;
3638 this.form.afterAction(this, false);
3642 success : function(response)
3644 this.uploadComplete= true;
3645 if (this.haveProgress) {
3646 Roo.MessageBox.hide();
3650 var result = this.processResponse(response);
3651 if(result === true || result.success){
3652 this.form.afterAction(this, true);
3656 this.form.markInvalid(result.errors);
3657 this.failureType = Roo.form.Action.SERVER_INVALID;
3659 this.form.afterAction(this, false);
3661 failure : function(response)
3663 this.uploadComplete= true;
3664 if (this.haveProgress) {
3665 Roo.MessageBox.hide();
3668 this.response = response;
3669 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3670 this.form.afterAction(this, false);
3673 handleResponse : function(response){
3674 if(this.form.errorReader){
3675 var rs = this.form.errorReader.read(response);
3678 for(var i = 0, len = rs.records.length; i < len; i++) {
3679 var r = rs.records[i];
3683 if(errors.length < 1){
3687 success : rs.success,
3693 ret = Roo.decode(response.responseText);
3697 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3707 Roo.form.Action.Load = function(form, options){
3708 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3709 this.reader = this.form.reader;
3712 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3717 Roo.Ajax.request(Roo.apply(
3718 this.createCallback(), {
3719 method:this.getMethod(),
3720 url:this.getUrl(false),
3721 params:this.getParams()
3725 success : function(response){
3727 var result = this.processResponse(response);
3728 if(result === true || !result.success || !result.data){
3729 this.failureType = Roo.form.Action.LOAD_FAILURE;
3730 this.form.afterAction(this, false);
3733 this.form.clearInvalid();
3734 this.form.setValues(result.data);
3735 this.form.afterAction(this, true);
3738 handleResponse : function(response){
3739 if(this.form.reader){
3740 var rs = this.form.reader.read(response);
3741 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3743 success : rs.success,
3747 return Roo.decode(response.responseText);
3751 Roo.form.Action.ACTION_TYPES = {
3752 'load' : Roo.form.Action.Load,
3753 'submit' : Roo.form.Action.Submit
3762 * @class Roo.bootstrap.Form
3763 * @extends Roo.bootstrap.Component
3764 * Bootstrap Form class
3765 * @cfg {String} method GET | POST (default POST)
3766 * @cfg {String} labelAlign top | left (default top)
3767 * @cfg {String} align left | right - for navbars
3772 * @param {Object} config The config object
3776 Roo.bootstrap.Form = function(config){
3777 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3780 * @event clientvalidation
3781 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3782 * @param {Form} this
3783 * @param {Boolean} valid true if the form has passed client-side validation
3785 clientvalidation: true,
3787 * @event beforeaction
3788 * Fires before any action is performed. Return false to cancel the action.
3789 * @param {Form} this
3790 * @param {Action} action The action to be performed
3794 * @event actionfailed
3795 * Fires when an action fails.
3796 * @param {Form} this
3797 * @param {Action} action The action that failed
3799 actionfailed : true,
3801 * @event actioncomplete
3802 * Fires when an action is completed.
3803 * @param {Form} this
3804 * @param {Action} action The action that completed
3806 actioncomplete : true
3811 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3814 * @cfg {String} method
3815 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3820 * The URL to use for form actions if one isn't supplied in the action options.
3823 * @cfg {Boolean} fileUpload
3824 * Set to true if this form is a file upload.
3828 * @cfg {Object} baseParams
3829 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3833 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3837 * @cfg {Sting} align (left|right) for navbar forms
3842 activeAction : null,
3845 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3846 * element by passing it or its id or mask the form itself by passing in true.
3849 waitMsgTarget : false,
3854 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3855 * element by passing it or its id or mask the form itself by passing in true.
3859 getAutoCreate : function(){
3863 method : this.method || 'POST',
3864 id : this.id || Roo.id(),
3867 if (this.parent().xtype.match(/^Nav/)) {
3868 cfg.cls = 'navbar-form navbar-' + this.align;
3872 if (this.labelAlign == 'left' ) {
3873 cfg.cls += ' form-horizontal';
3879 initEvents : function()
3881 this.el.on('submit', this.onSubmit, this);
3886 onSubmit : function(e){
3891 * Returns true if client-side validation on the form is successful.
3894 isValid : function(){
3895 var items = this.getItems();
3897 items.each(function(f){
3906 * Returns true if any fields in this form have changed since their original load.
3909 isDirty : function(){
3911 var items = this.getItems();
3912 items.each(function(f){
3922 * Performs a predefined action (submit or load) or custom actions you define on this form.
3923 * @param {String} actionName The name of the action type
3924 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3925 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3926 * accept other config options):
3928 Property Type Description
3929 ---------------- --------------- ----------------------------------------------------------------------------------
3930 url String The url for the action (defaults to the form's url)
3931 method String The form method to use (defaults to the form's method, or POST if not defined)
3932 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3933 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3934 validate the form on the client (defaults to false)
3936 * @return {BasicForm} this
3938 doAction : function(action, options){
3939 if(typeof action == 'string'){
3940 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3942 if(this.fireEvent('beforeaction', this, action) !== false){
3943 this.beforeAction(action);
3944 action.run.defer(100, action);
3950 beforeAction : function(action){
3951 var o = action.options;
3953 // not really supported yet.. ??
3955 //if(this.waitMsgTarget === true){
3956 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3957 //}else if(this.waitMsgTarget){
3958 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3959 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3961 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3967 afterAction : function(action, success){
3968 this.activeAction = null;
3969 var o = action.options;
3971 //if(this.waitMsgTarget === true){
3973 //}else if(this.waitMsgTarget){
3974 // this.waitMsgTarget.unmask();
3976 // Roo.MessageBox.updateProgress(1);
3977 // Roo.MessageBox.hide();
3984 Roo.callback(o.success, o.scope, [this, action]);
3985 this.fireEvent('actioncomplete', this, action);
3989 // failure condition..
3990 // we have a scenario where updates need confirming.
3991 // eg. if a locking scenario exists..
3992 // we look for { errors : { needs_confirm : true }} in the response.
3994 (typeof(action.result) != 'undefined') &&
3995 (typeof(action.result.errors) != 'undefined') &&
3996 (typeof(action.result.errors.needs_confirm) != 'undefined')
3999 Roo.log("not supported yet");
4002 Roo.MessageBox.confirm(
4003 "Change requires confirmation",
4004 action.result.errorMsg,
4009 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4019 Roo.callback(o.failure, o.scope, [this, action]);
4020 // show an error message if no failed handler is set..
4021 if (!this.hasListener('actionfailed')) {
4022 Roo.log("need to add dialog support");
4024 Roo.MessageBox.alert("Error",
4025 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4026 action.result.errorMsg :
4027 "Saving Failed, please check your entries or try again"
4032 this.fireEvent('actionfailed', this, action);
4037 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4038 * @param {String} id The value to search for
4041 findField : function(id){
4042 var items = this.getItems();
4043 var field = items.get(id);
4045 items.each(function(f){
4046 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4053 return field || null;
4056 * Mark fields in this form invalid in bulk.
4057 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4058 * @return {BasicForm} this
4060 markInvalid : function(errors){
4061 if(errors instanceof Array){
4062 for(var i = 0, len = errors.length; i < len; i++){
4063 var fieldError = errors[i];
4064 var f = this.findField(fieldError.id);
4066 f.markInvalid(fieldError.msg);
4072 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4073 field.markInvalid(errors[id]);
4077 //Roo.each(this.childForms || [], function (f) {
4078 // f.markInvalid(errors);
4085 * Set values for fields in this form in bulk.
4086 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4087 * @return {BasicForm} this
4089 setValues : function(values){
4090 if(values instanceof Array){ // array of objects
4091 for(var i = 0, len = values.length; i < len; i++){
4093 var f = this.findField(v.id);
4095 f.setValue(v.value);
4096 if(this.trackResetOnLoad){
4097 f.originalValue = f.getValue();
4101 }else{ // object hash
4104 if(typeof values[id] != 'function' && (field = this.findField(id))){
4106 if (field.setFromData &&
4108 field.displayField &&
4109 // combos' with local stores can
4110 // be queried via setValue()
4111 // to set their value..
4112 (field.store && !field.store.isLocal)
4116 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4117 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4118 field.setFromData(sd);
4121 field.setValue(values[id]);
4125 if(this.trackResetOnLoad){
4126 field.originalValue = field.getValue();
4132 //Roo.each(this.childForms || [], function (f) {
4133 // f.setValues(values);
4140 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4141 * they are returned as an array.
4142 * @param {Boolean} asString
4145 getValues : function(asString){
4146 //if (this.childForms) {
4147 // copy values from the child forms
4148 // Roo.each(this.childForms, function (f) {
4149 // this.setValues(f.getValues());
4155 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4156 if(asString === true){
4159 return Roo.urlDecode(fs);
4163 * Returns the fields in this form as an object with key/value pairs.
4164 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4167 getFieldValues : function(with_hidden)
4169 var items = this.getItems();
4171 items.each(function(f){
4175 var v = f.getValue();
4176 if (f.inputType =='radio') {
4177 if (typeof(ret[f.getName()]) == 'undefined') {
4178 ret[f.getName()] = ''; // empty..
4181 if (!f.el.dom.checked) {
4189 // not sure if this supported any more..
4190 if ((typeof(v) == 'object') && f.getRawValue) {
4191 v = f.getRawValue() ; // dates..
4193 // combo boxes where name != hiddenName...
4194 if (f.name != f.getName()) {
4195 ret[f.name] = f.getRawValue();
4197 ret[f.getName()] = v;
4204 * Clears all invalid messages in this form.
4205 * @return {BasicForm} this
4207 clearInvalid : function(){
4208 var items = this.getItems();
4210 items.each(function(f){
4221 * @return {BasicForm} this
4224 var items = this.getItems();
4225 items.each(function(f){
4229 Roo.each(this.childForms || [], function (f) {
4236 getItems : function()
4238 var r=new Roo.util.MixedCollection(false, function(o){
4239 return o.id || (o.id = Roo.id());
4241 var iter = function(el) {
4248 Roo.each(el.items,function(e) {
4267 * Ext JS Library 1.1.1
4268 * Copyright(c) 2006-2007, Ext JS, LLC.
4270 * Originally Released Under LGPL - original licence link has changed is not relivant.
4273 * <script type="text/javascript">
4276 * @class Roo.form.VTypes
4277 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4280 Roo.form.VTypes = function(){
4281 // closure these in so they are only created once.
4282 var alpha = /^[a-zA-Z_]+$/;
4283 var alphanum = /^[a-zA-Z0-9_]+$/;
4284 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4285 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4287 // All these messages and functions are configurable
4290 * The function used to validate email addresses
4291 * @param {String} value The email address
4293 'email' : function(v){
4294 return email.test(v);
4297 * The error text to display when the email validation function returns false
4300 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4302 * The keystroke filter mask to be applied on email input
4305 'emailMask' : /[a-z0-9_\.\-@]/i,
4308 * The function used to validate URLs
4309 * @param {String} value The URL
4311 'url' : function(v){
4315 * The error text to display when the url validation function returns false
4318 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4321 * The function used to validate alpha values
4322 * @param {String} value The value
4324 'alpha' : function(v){
4325 return alpha.test(v);
4328 * The error text to display when the alpha validation function returns false
4331 'alphaText' : 'This field should only contain letters and _',
4333 * The keystroke filter mask to be applied on alpha input
4336 'alphaMask' : /[a-z_]/i,
4339 * The function used to validate alphanumeric values
4340 * @param {String} value The value
4342 'alphanum' : function(v){
4343 return alphanum.test(v);
4346 * The error text to display when the alphanumeric validation function returns false
4349 'alphanumText' : 'This field should only contain letters, numbers and _',
4351 * The keystroke filter mask to be applied on alphanumeric input
4354 'alphanumMask' : /[a-z0-9_]/i
4364 * @class Roo.bootstrap.Input
4365 * @extends Roo.bootstrap.Component
4366 * Bootstrap Input class
4367 * @cfg {Boolean} disabled is it disabled
4368 * @cfg {String} fieldLabel - the label associated
4369 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4370 * @cfg {String} name name of the input
4371 * @cfg {string} fieldLabel - the label associated
4372 * @cfg {string} inputType - input / file submit ...
4373 * @cfg {string} placeholder - placeholder to put in text.
4374 * @cfg {string} before - input group add on before
4375 * @cfg {string} after - input group add on after
4376 * @cfg {string} size - (lg|sm) or leave empty..
4377 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4378 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4379 * @cfg {Number} md colspan out of 12 for computer-sized screens
4380 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4381 * @cfg {string} value default value of the input
4382 * @cfg {Number} labelWidth set the width of label (0-12)
4383 * @cfg {String} labelAlign (top|left)
4384 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4388 * Create a new Input
4389 * @param {Object} config The config object
4392 Roo.bootstrap.Input = function(config){
4393 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4398 * Fires when this field receives input focus.
4399 * @param {Roo.form.Field} this
4404 * Fires when this field loses input focus.
4405 * @param {Roo.form.Field} this
4410 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4411 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4412 * @param {Roo.form.Field} this
4413 * @param {Roo.EventObject} e The event object
4418 * Fires just before the field blurs if the field value has changed.
4419 * @param {Roo.form.Field} this
4420 * @param {Mixed} newValue The new value
4421 * @param {Mixed} oldValue The original value
4426 * Fires after the field has been marked as invalid.
4427 * @param {Roo.form.Field} this
4428 * @param {String} msg The validation message
4433 * Fires after the field has been validated with no errors.
4434 * @param {Roo.form.Field} this
4439 * Fires after the key up
4440 * @param {Roo.form.Field} this
4441 * @param {Roo.EventObject} e The event Object
4447 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4449 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4450 automatic validation (defaults to "keyup").
4452 validationEvent : "keyup",
4454 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4456 validateOnBlur : true,
4458 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4460 validationDelay : 250,
4462 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4464 focusClass : "x-form-focus", // not needed???
4468 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4470 invalidClass : "has-error",
4473 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4475 selectOnFocus : false,
4478 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4482 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4487 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4489 disableKeyFilter : false,
4492 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4496 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4500 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4502 blankText : "This field is required",
4505 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4509 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4511 maxLength : Number.MAX_VALUE,
4513 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4515 minLengthText : "The minimum length for this field is {0}",
4517 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4519 maxLengthText : "The maximum length for this field is {0}",
4523 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4524 * If available, this function will be called only after the basic validators all return true, and will be passed the
4525 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4529 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4530 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4531 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4535 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4558 parentLabelAlign : function()
4561 while (parent.parent()) {
4562 parent = parent.parent();
4563 if (typeof(parent.labelAlign) !='undefined') {
4564 return parent.labelAlign;
4571 getAutoCreate : function(){
4573 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4579 if(this.inputType != 'hidden'){
4580 cfg.cls = 'form-group' //input-group
4586 type : this.inputType,
4588 cls : 'form-control',
4589 placeholder : this.placeholder || ''
4593 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4594 input.maxLength = this.maxLength;
4597 if (this.disabled) {
4598 input.disabled=true;
4601 if (this.readOnly) {
4602 input.readonly=true;
4606 input.name = this.name;
4609 input.cls += ' input-' + this.size;
4612 ['xs','sm','md','lg'].map(function(size){
4613 if (settings[size]) {
4614 cfg.cls += ' col-' + size + '-' + settings[size];
4618 var inputblock = input;
4620 if (this.before || this.after) {
4623 cls : 'input-group',
4627 inputblock.cn.push({
4629 cls : 'input-group-addon',
4633 inputblock.cn.push(input);
4635 inputblock.cn.push({
4637 cls : 'input-group-addon',
4644 if (align ==='left' && this.fieldLabel.length) {
4645 Roo.log("left and has label");
4651 cls : 'control-label col-sm-' + this.labelWidth,
4652 html : this.fieldLabel
4656 cls : "col-sm-" + (12 - this.labelWidth),
4663 } else if ( this.fieldLabel.length) {
4669 //cls : 'input-group-addon',
4670 html : this.fieldLabel
4680 Roo.log(" no label && no align");
4689 Roo.log('input-parentType: ' + this.parentType);
4691 if (this.parentType === 'Navbar' && this.parent().bar) {
4692 cfg.cls += ' navbar-form';
4700 * return the real input element.
4702 inputEl: function ()
4704 return this.el.select('input.form-control',true).first();
4706 setDisabled : function(v)
4708 var i = this.inputEl().dom;
4710 i.removeAttribute('disabled');
4714 i.setAttribute('disabled','true');
4716 initEvents : function()
4719 this.inputEl().on("keydown" , this.fireKey, this);
4720 this.inputEl().on("focus", this.onFocus, this);
4721 this.inputEl().on("blur", this.onBlur, this);
4723 this.inputEl().relayEvent('keyup', this);
4725 // reference to original value for reset
4726 this.originalValue = this.getValue();
4727 //Roo.form.TextField.superclass.initEvents.call(this);
4728 if(this.validationEvent == 'keyup'){
4729 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4730 this.inputEl().on('keyup', this.filterValidation, this);
4732 else if(this.validationEvent !== false){
4733 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4736 if(this.selectOnFocus){
4737 this.on("focus", this.preFocus, this);
4740 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4741 this.inputEl().on("keypress", this.filterKeys, this);
4744 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4745 this.el.on("click", this.autoSize, this);
4748 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4749 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4753 filterValidation : function(e){
4754 if(!e.isNavKeyPress()){
4755 this.validationTask.delay(this.validationDelay);
4759 * Validates the field value
4760 * @return {Boolean} True if the value is valid, else false
4762 validate : function(){
4763 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4764 if(this.disabled || this.validateValue(this.getRawValue())){
4765 this.clearInvalid();
4773 * Validates a value according to the field's validation rules and marks the field as invalid
4774 * if the validation fails
4775 * @param {Mixed} value The value to validate
4776 * @return {Boolean} True if the value is valid, else false
4778 validateValue : function(value){
4779 if(value.length < 1) { // if it's blank
4780 if(this.allowBlank){
4781 this.clearInvalid();
4784 this.markInvalid(this.blankText);
4788 if(value.length < this.minLength){
4789 this.markInvalid(String.format(this.minLengthText, this.minLength));
4792 if(value.length > this.maxLength){
4793 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4797 var vt = Roo.form.VTypes;
4798 if(!vt[this.vtype](value, this)){
4799 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4803 if(typeof this.validator == "function"){
4804 var msg = this.validator(value);
4806 this.markInvalid(msg);
4810 if(this.regex && !this.regex.test(value)){
4811 this.markInvalid(this.regexText);
4820 fireKey : function(e){
4821 //Roo.log('field ' + e.getKey());
4822 if(e.isNavKeyPress()){
4823 this.fireEvent("specialkey", this, e);
4826 focus : function (selectText){
4828 this.inputEl().focus();
4829 if(selectText === true){
4830 this.inputEl().dom.select();
4836 onFocus : function(){
4837 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4838 // this.el.addClass(this.focusClass);
4841 this.hasFocus = true;
4842 this.startValue = this.getValue();
4843 this.fireEvent("focus", this);
4847 beforeBlur : Roo.emptyFn,
4851 onBlur : function(){
4853 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4854 //this.el.removeClass(this.focusClass);
4856 this.hasFocus = false;
4857 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4860 var v = this.getValue();
4861 if(String(v) !== String(this.startValue)){
4862 this.fireEvent('change', this, v, this.startValue);
4864 this.fireEvent("blur", this);
4868 * Resets the current field value to the originally loaded value and clears any validation messages
4871 this.setValue(this.originalValue);
4872 this.clearInvalid();
4875 * Returns the name of the field
4876 * @return {Mixed} name The name field
4878 getName: function(){
4882 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4883 * @return {Mixed} value The field value
4885 getValue : function(){
4886 return this.inputEl().getValue();
4889 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4890 * @return {Mixed} value The field value
4892 getRawValue : function(){
4893 var v = this.inputEl().getValue();
4899 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4900 * @param {Mixed} value The value to set
4902 setRawValue : function(v){
4903 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4906 selectText : function(start, end){
4907 var v = this.getRawValue();
4909 start = start === undefined ? 0 : start;
4910 end = end === undefined ? v.length : end;
4911 var d = this.inputEl().dom;
4912 if(d.setSelectionRange){
4913 d.setSelectionRange(start, end);
4914 }else if(d.createTextRange){
4915 var range = d.createTextRange();
4916 range.moveStart("character", start);
4917 range.moveEnd("character", v.length-end);
4924 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4925 * @param {Mixed} value The value to set
4927 setValue : function(v){
4930 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4936 processValue : function(value){
4937 if(this.stripCharsRe){
4938 var newValue = value.replace(this.stripCharsRe, '');
4939 if(newValue !== value){
4940 this.setRawValue(newValue);
4947 preFocus : function(){
4949 if(this.selectOnFocus){
4950 this.inputEl().dom.select();
4953 filterKeys : function(e){
4955 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4958 var c = e.getCharCode(), cc = String.fromCharCode(c);
4959 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4962 if(!this.maskRe.test(cc)){
4967 * Clear any invalid styles/messages for this field
4969 clearInvalid : function(){
4971 if(!this.el || this.preventMark){ // not rendered
4974 this.el.removeClass(this.invalidClass);
4976 switch(this.msgTarget){
4978 this.el.dom.qtip = '';
4981 this.el.dom.title = '';
4985 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4990 this.errorIcon.dom.qtip = '';
4991 this.errorIcon.hide();
4992 this.un('resize', this.alignErrorIcon, this);
4996 var t = Roo.getDom(this.msgTarget);
4998 t.style.display = 'none';
5002 this.fireEvent('valid', this);
5005 * Mark this field as invalid
5006 * @param {String} msg The validation message
5008 markInvalid : function(msg){
5009 if(!this.el || this.preventMark){ // not rendered
5012 this.el.addClass(this.invalidClass);
5014 msg = msg || this.invalidText;
5015 switch(this.msgTarget){
5017 this.el.dom.qtip = msg;
5018 this.el.dom.qclass = 'x-form-invalid-tip';
5019 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5020 Roo.QuickTips.enable();
5024 this.el.dom.title = msg;
5028 var elp = this.el.findParent('.x-form-element', 5, true);
5029 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5030 this.errorEl.setWidth(elp.getWidth(true)-20);
5032 this.errorEl.update(msg);
5033 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5036 if(!this.errorIcon){
5037 var elp = this.el.findParent('.x-form-element', 5, true);
5038 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5040 this.alignErrorIcon();
5041 this.errorIcon.dom.qtip = msg;
5042 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5043 this.errorIcon.show();
5044 this.on('resize', this.alignErrorIcon, this);
5047 var t = Roo.getDom(this.msgTarget);
5049 t.style.display = this.msgDisplay;
5053 this.fireEvent('invalid', this, msg);
5056 SafariOnKeyDown : function(event)
5058 // this is a workaround for a password hang bug on chrome/ webkit.
5060 var isSelectAll = false;
5062 if(this.inputEl().dom.selectionEnd > 0){
5063 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5065 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5066 event.preventDefault();
5071 if(isSelectAll){ // backspace and delete key
5073 event.preventDefault();
5074 // this is very hacky as keydown always get's upper case.
5076 var cc = String.fromCharCode(event.getCharCode());
5077 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5081 adjustWidth : function(tag, w){
5082 tag = tag.toLowerCase();
5083 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5084 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5088 if(tag == 'textarea'){
5091 }else if(Roo.isOpera){
5095 if(tag == 'textarea'){
5114 * @class Roo.bootstrap.TextArea
5115 * @extends Roo.bootstrap.Input
5116 * Bootstrap TextArea class
5117 * @cfg {Number} cols Specifies the visible width of a text area
5118 * @cfg {Number} rows Specifies the visible number of lines in a text area
5119 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5120 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5121 * @cfg {string} html text
5124 * Create a new TextArea
5125 * @param {Object} config The config object
5128 Roo.bootstrap.TextArea = function(config){
5129 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5133 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5143 getAutoCreate : function(){
5145 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5156 value : this.value || '',
5157 html: this.html || '',
5158 cls : 'form-control',
5159 placeholder : this.placeholder || ''
5163 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5164 input.maxLength = this.maxLength;
5168 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5172 input.cols = this.cols;
5175 if (this.readOnly) {
5176 input.readonly = true;
5180 input.name = this.name;
5184 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5188 ['xs','sm','md','lg'].map(function(size){
5189 if (settings[size]) {
5190 cfg.cls += ' col-' + size + '-' + settings[size];
5194 var inputblock = input;
5196 if (this.before || this.after) {
5199 cls : 'input-group',
5203 inputblock.cn.push({
5205 cls : 'input-group-addon',
5209 inputblock.cn.push(input);
5211 inputblock.cn.push({
5213 cls : 'input-group-addon',
5220 if (align ==='left' && this.fieldLabel.length) {
5221 Roo.log("left and has label");
5227 cls : 'control-label col-sm-' + this.labelWidth,
5228 html : this.fieldLabel
5232 cls : "col-sm-" + (12 - this.labelWidth),
5239 } else if ( this.fieldLabel.length) {
5245 //cls : 'input-group-addon',
5246 html : this.fieldLabel
5256 Roo.log(" no label && no align");
5266 if (this.disabled) {
5267 input.disabled=true;
5274 * return the real textarea element.
5276 inputEl: function ()
5278 return this.el.select('textarea.form-control',true).first();
5286 * trigger field - base class for combo..
5291 * @class Roo.bootstrap.TriggerField
5292 * @extends Roo.bootstrap.Input
5293 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5294 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5295 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5296 * for which you can provide a custom implementation. For example:
5298 var trigger = new Roo.bootstrap.TriggerField();
5299 trigger.onTriggerClick = myTriggerFn;
5300 trigger.applyTo('my-field');
5303 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5304 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5305 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5306 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5308 * Create a new TriggerField.
5309 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5310 * to the base TextField)
5312 Roo.bootstrap.TriggerField = function(config){
5313 this.mimicing = false;
5314 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5317 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5319 * @cfg {String} triggerClass A CSS class to apply to the trigger
5322 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5326 /** @cfg {Boolean} grow @hide */
5327 /** @cfg {Number} growMin @hide */
5328 /** @cfg {Number} growMax @hide */
5334 autoSize: Roo.emptyFn,
5341 actionMode : 'wrap',
5345 getAutoCreate : function(){
5347 var parent = this.parent();
5349 var align = this.parentLabelAlign();
5354 cls: 'form-group' //input-group
5361 type : this.inputType,
5362 cls : 'form-control',
5363 autocomplete: 'off',
5364 placeholder : this.placeholder || ''
5368 input.name = this.name;
5371 input.cls += ' input-' + this.size;
5374 if (this.disabled) {
5375 input.disabled=true;
5378 var inputblock = input;
5380 if (this.before || this.after) {
5383 cls : 'input-group',
5387 inputblock.cn.push({
5389 cls : 'input-group-addon',
5393 inputblock.cn.push(input);
5395 inputblock.cn.push({
5397 cls : 'input-group-addon',
5410 cls: 'form-hidden-field'
5418 Roo.log('multiple');
5426 cls: 'form-hidden-field'
5430 cls: 'select2-choices',
5434 cls: 'select2-search-field',
5447 cls: 'select2-container input-group',
5452 cls: 'typeahead typeahead-long dropdown-menu',
5453 style: 'display:none'
5461 cls : 'input-group-addon btn dropdown-toggle',
5469 cls: 'combobox-clear',
5483 combobox.cls += ' select2-container-multi';
5486 if (align ==='left' && this.fieldLabel.length) {
5488 Roo.log("left and has label");
5494 cls : 'control-label col-sm-' + this.labelWidth,
5495 html : this.fieldLabel
5499 cls : "col-sm-" + (12 - this.labelWidth),
5506 } else if ( this.fieldLabel.length) {
5512 //cls : 'input-group-addon',
5513 html : this.fieldLabel
5523 Roo.log(" no label && no align");
5530 ['xs','sm','md','lg'].map(function(size){
5531 if (settings[size]) {
5532 cfg.cls += ' col-' + size + '-' + settings[size];
5543 onResize : function(w, h){
5544 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5545 // if(typeof w == 'number'){
5546 // var x = w - this.trigger.getWidth();
5547 // this.inputEl().setWidth(this.adjustWidth('input', x));
5548 // this.trigger.setStyle('left', x+'px');
5553 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5556 getResizeEl : function(){
5557 return this.inputEl();
5561 getPositionEl : function(){
5562 return this.inputEl();
5566 alignErrorIcon : function(){
5567 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5571 initEvents : function(){
5573 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5574 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5576 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5577 if(this.hideTrigger){
5578 this.trigger.setDisplayed(false);
5580 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5584 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5587 //this.trigger.addClassOnOver('x-form-trigger-over');
5588 //this.trigger.addClassOnClick('x-form-trigger-click');
5591 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5596 initTrigger : function(){
5601 onDestroy : function(){
5603 this.trigger.removeAllListeners();
5604 // this.trigger.remove();
5607 // this.wrap.remove();
5609 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5613 onFocus : function(){
5614 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5617 this.wrap.addClass('x-trigger-wrap-focus');
5618 this.mimicing = true;
5619 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5620 if(this.monitorTab){
5621 this.el.on("keydown", this.checkTab, this);
5628 checkTab : function(e){
5629 if(e.getKey() == e.TAB){
5635 onBlur : function(){
5640 mimicBlur : function(e, t){
5642 if(!this.wrap.contains(t) && this.validateBlur()){
5649 triggerBlur : function(){
5650 this.mimicing = false;
5651 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5652 if(this.monitorTab){
5653 this.el.un("keydown", this.checkTab, this);
5655 //this.wrap.removeClass('x-trigger-wrap-focus');
5656 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5660 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5661 validateBlur : function(e, t){
5666 onDisable : function(){
5667 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5669 // this.wrap.addClass('x-item-disabled');
5674 onEnable : function(){
5675 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5677 // this.el.removeClass('x-item-disabled');
5682 onShow : function(){
5683 var ae = this.getActionEl();
5686 ae.dom.style.display = '';
5687 ae.dom.style.visibility = 'visible';
5693 onHide : function(){
5694 var ae = this.getActionEl();
5695 ae.dom.style.display = 'none';
5699 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5700 * by an implementing function.
5702 * @param {EventObject} e
5704 onTriggerClick : Roo.emptyFn
5708 * Ext JS Library 1.1.1
5709 * Copyright(c) 2006-2007, Ext JS, LLC.
5711 * Originally Released Under LGPL - original licence link has changed is not relivant.
5714 * <script type="text/javascript">
5719 * @class Roo.data.SortTypes
5721 * Defines the default sorting (casting?) comparison functions used when sorting data.
5723 Roo.data.SortTypes = {
5725 * Default sort that does nothing
5726 * @param {Mixed} s The value being converted
5727 * @return {Mixed} The comparison value
5734 * The regular expression used to strip tags
5738 stripTagsRE : /<\/?[^>]+>/gi,
5741 * Strips all HTML tags to sort on text only
5742 * @param {Mixed} s The value being converted
5743 * @return {String} The comparison value
5745 asText : function(s){
5746 return String(s).replace(this.stripTagsRE, "");
5750 * Strips all HTML tags to sort on text only - Case insensitive
5751 * @param {Mixed} s The value being converted
5752 * @return {String} The comparison value
5754 asUCText : function(s){
5755 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5759 * Case insensitive string
5760 * @param {Mixed} s The value being converted
5761 * @return {String} The comparison value
5763 asUCString : function(s) {
5764 return String(s).toUpperCase();
5769 * @param {Mixed} s The value being converted
5770 * @return {Number} The comparison value
5772 asDate : function(s) {
5776 if(s instanceof Date){
5779 return Date.parse(String(s));
5784 * @param {Mixed} s The value being converted
5785 * @return {Float} The comparison value
5787 asFloat : function(s) {
5788 var val = parseFloat(String(s).replace(/,/g, ""));
5789 if(isNaN(val)) val = 0;
5795 * @param {Mixed} s The value being converted
5796 * @return {Number} The comparison value
5798 asInt : function(s) {
5799 var val = parseInt(String(s).replace(/,/g, ""));
5800 if(isNaN(val)) val = 0;
5805 * Ext JS Library 1.1.1
5806 * Copyright(c) 2006-2007, Ext JS, LLC.
5808 * Originally Released Under LGPL - original licence link has changed is not relivant.
5811 * <script type="text/javascript">
5815 * @class Roo.data.Record
5816 * Instances of this class encapsulate both record <em>definition</em> information, and record
5817 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5818 * to access Records cached in an {@link Roo.data.Store} object.<br>
5820 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5821 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5824 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5826 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5827 * {@link #create}. The parameters are the same.
5828 * @param {Array} data An associative Array of data values keyed by the field name.
5829 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5830 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5831 * not specified an integer id is generated.
5833 Roo.data.Record = function(data, id){
5834 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5839 * Generate a constructor for a specific record layout.
5840 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5841 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5842 * Each field definition object may contain the following properties: <ul>
5843 * <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,
5844 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5845 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5846 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5847 * is being used, then this is a string containing the javascript expression to reference the data relative to
5848 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5849 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5850 * this may be omitted.</p></li>
5851 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5852 * <ul><li>auto (Default, implies no conversion)</li>
5857 * <li>date</li></ul></p></li>
5858 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5859 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5860 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5861 * by the Reader into an object that will be stored in the Record. It is passed the
5862 * following parameters:<ul>
5863 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5865 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5867 * <br>usage:<br><pre><code>
5868 var TopicRecord = Roo.data.Record.create(
5869 {name: 'title', mapping: 'topic_title'},
5870 {name: 'author', mapping: 'username'},
5871 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5872 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5873 {name: 'lastPoster', mapping: 'user2'},
5874 {name: 'excerpt', mapping: 'post_text'}
5877 var myNewRecord = new TopicRecord({
5878 title: 'Do my job please',
5881 lastPost: new Date(),
5882 lastPoster: 'Animal',
5883 excerpt: 'No way dude!'
5885 myStore.add(myNewRecord);
5890 Roo.data.Record.create = function(o){
5892 f.superclass.constructor.apply(this, arguments);
5894 Roo.extend(f, Roo.data.Record);
5895 var p = f.prototype;
5896 p.fields = new Roo.util.MixedCollection(false, function(field){
5899 for(var i = 0, len = o.length; i < len; i++){
5900 p.fields.add(new Roo.data.Field(o[i]));
5902 f.getField = function(name){
5903 return p.fields.get(name);
5908 Roo.data.Record.AUTO_ID = 1000;
5909 Roo.data.Record.EDIT = 'edit';
5910 Roo.data.Record.REJECT = 'reject';
5911 Roo.data.Record.COMMIT = 'commit';
5913 Roo.data.Record.prototype = {
5915 * Readonly flag - true if this record has been modified.
5924 join : function(store){
5929 * Set the named field to the specified value.
5930 * @param {String} name The name of the field to set.
5931 * @param {Object} value The value to set the field to.
5933 set : function(name, value){
5934 if(this.data[name] == value){
5941 if(typeof this.modified[name] == 'undefined'){
5942 this.modified[name] = this.data[name];
5944 this.data[name] = value;
5945 if(!this.editing && this.store){
5946 this.store.afterEdit(this);
5951 * Get the value of the named field.
5952 * @param {String} name The name of the field to get the value of.
5953 * @return {Object} The value of the field.
5955 get : function(name){
5956 return this.data[name];
5960 beginEdit : function(){
5961 this.editing = true;
5966 cancelEdit : function(){
5967 this.editing = false;
5968 delete this.modified;
5972 endEdit : function(){
5973 this.editing = false;
5974 if(this.dirty && this.store){
5975 this.store.afterEdit(this);
5980 * Usually called by the {@link Roo.data.Store} which owns the Record.
5981 * Rejects all changes made to the Record since either creation, or the last commit operation.
5982 * Modified fields are reverted to their original values.
5984 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5985 * of reject operations.
5987 reject : function(){
5988 var m = this.modified;
5990 if(typeof m[n] != "function"){
5991 this.data[n] = m[n];
5995 delete this.modified;
5996 this.editing = false;
5998 this.store.afterReject(this);
6003 * Usually called by the {@link Roo.data.Store} which owns the Record.
6004 * Commits all changes made to the Record since either creation, or the last commit operation.
6006 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6007 * of commit operations.
6009 commit : function(){
6011 delete this.modified;
6012 this.editing = false;
6014 this.store.afterCommit(this);
6019 hasError : function(){
6020 return this.error != null;
6024 clearError : function(){
6029 * Creates a copy of this record.
6030 * @param {String} id (optional) A new record id if you don't want to use this record's id
6033 copy : function(newId) {
6034 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6038 * Ext JS Library 1.1.1
6039 * Copyright(c) 2006-2007, Ext JS, LLC.
6041 * Originally Released Under LGPL - original licence link has changed is not relivant.
6044 * <script type="text/javascript">
6050 * @class Roo.data.Store
6051 * @extends Roo.util.Observable
6052 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6053 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6055 * 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
6056 * has no knowledge of the format of the data returned by the Proxy.<br>
6058 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6059 * instances from the data object. These records are cached and made available through accessor functions.
6061 * Creates a new Store.
6062 * @param {Object} config A config object containing the objects needed for the Store to access data,
6063 * and read the data into Records.
6065 Roo.data.Store = function(config){
6066 this.data = new Roo.util.MixedCollection(false);
6067 this.data.getKey = function(o){
6070 this.baseParams = {};
6077 "multisort" : "_multisort"
6080 if(config && config.data){
6081 this.inlineData = config.data;
6085 Roo.apply(this, config);
6087 if(this.reader){ // reader passed
6088 this.reader = Roo.factory(this.reader, Roo.data);
6089 this.reader.xmodule = this.xmodule || false;
6090 if(!this.recordType){
6091 this.recordType = this.reader.recordType;
6093 if(this.reader.onMetaChange){
6094 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6098 if(this.recordType){
6099 this.fields = this.recordType.prototype.fields;
6105 * @event datachanged
6106 * Fires when the data cache has changed, and a widget which is using this Store
6107 * as a Record cache should refresh its view.
6108 * @param {Store} this
6113 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6114 * @param {Store} this
6115 * @param {Object} meta The JSON metadata
6120 * Fires when Records have been added to the Store
6121 * @param {Store} this
6122 * @param {Roo.data.Record[]} records The array of Records added
6123 * @param {Number} index The index at which the record(s) were added
6128 * Fires when a Record has been removed from the Store
6129 * @param {Store} this
6130 * @param {Roo.data.Record} record The Record that was removed
6131 * @param {Number} index The index at which the record was removed
6136 * Fires when a Record has been updated
6137 * @param {Store} this
6138 * @param {Roo.data.Record} record The Record that was updated
6139 * @param {String} operation The update operation being performed. Value may be one of:
6141 Roo.data.Record.EDIT
6142 Roo.data.Record.REJECT
6143 Roo.data.Record.COMMIT
6149 * Fires when the data cache has been cleared.
6150 * @param {Store} this
6155 * Fires before a request is made for a new data object. If the beforeload handler returns false
6156 * the load action will be canceled.
6157 * @param {Store} this
6158 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6162 * @event beforeloadadd
6163 * Fires after a new set of Records has been loaded.
6164 * @param {Store} this
6165 * @param {Roo.data.Record[]} records The Records that were loaded
6166 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6168 beforeloadadd : true,
6171 * Fires after a new set of Records has been loaded, before they are added to the store.
6172 * @param {Store} this
6173 * @param {Roo.data.Record[]} records The Records that were loaded
6174 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6175 * @params {Object} return from reader
6179 * @event loadexception
6180 * Fires if an exception occurs in the Proxy during loading.
6181 * Called with the signature of the Proxy's "loadexception" event.
6182 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6185 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6186 * @param {Object} load options
6187 * @param {Object} jsonData from your request (normally this contains the Exception)
6189 loadexception : true
6193 this.proxy = Roo.factory(this.proxy, Roo.data);
6194 this.proxy.xmodule = this.xmodule || false;
6195 this.relayEvents(this.proxy, ["loadexception"]);
6197 this.sortToggle = {};
6198 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6200 Roo.data.Store.superclass.constructor.call(this);
6202 if(this.inlineData){
6203 this.loadData(this.inlineData);
6204 delete this.inlineData;
6208 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6210 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6211 * without a remote query - used by combo/forms at present.
6215 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6218 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6221 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6222 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6225 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6226 * on any HTTP request
6229 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6232 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6236 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6237 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6242 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6243 * loaded or when a record is removed. (defaults to false).
6245 pruneModifiedRecords : false,
6251 * Add Records to the Store and fires the add event.
6252 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6254 add : function(records){
6255 records = [].concat(records);
6256 for(var i = 0, len = records.length; i < len; i++){
6257 records[i].join(this);
6259 var index = this.data.length;
6260 this.data.addAll(records);
6261 this.fireEvent("add", this, records, index);
6265 * Remove a Record from the Store and fires the remove event.
6266 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6268 remove : function(record){
6269 var index = this.data.indexOf(record);
6270 this.data.removeAt(index);
6271 if(this.pruneModifiedRecords){
6272 this.modified.remove(record);
6274 this.fireEvent("remove", this, record, index);
6278 * Remove all Records from the Store and fires the clear event.
6280 removeAll : function(){
6282 if(this.pruneModifiedRecords){
6285 this.fireEvent("clear", this);
6289 * Inserts Records to the Store at the given index and fires the add event.
6290 * @param {Number} index The start index at which to insert the passed Records.
6291 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6293 insert : function(index, records){
6294 records = [].concat(records);
6295 for(var i = 0, len = records.length; i < len; i++){
6296 this.data.insert(index, records[i]);
6297 records[i].join(this);
6299 this.fireEvent("add", this, records, index);
6303 * Get the index within the cache of the passed Record.
6304 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6305 * @return {Number} The index of the passed Record. Returns -1 if not found.
6307 indexOf : function(record){
6308 return this.data.indexOf(record);
6312 * Get the index within the cache of the Record with the passed id.
6313 * @param {String} id The id of the Record to find.
6314 * @return {Number} The index of the Record. Returns -1 if not found.
6316 indexOfId : function(id){
6317 return this.data.indexOfKey(id);
6321 * Get the Record with the specified id.
6322 * @param {String} id The id of the Record to find.
6323 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6325 getById : function(id){
6326 return this.data.key(id);
6330 * Get the Record at the specified index.
6331 * @param {Number} index The index of the Record to find.
6332 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6334 getAt : function(index){
6335 return this.data.itemAt(index);
6339 * Returns a range of Records between specified indices.
6340 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6341 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6342 * @return {Roo.data.Record[]} An array of Records
6344 getRange : function(start, end){
6345 return this.data.getRange(start, end);
6349 storeOptions : function(o){
6350 o = Roo.apply({}, o);
6353 this.lastOptions = o;
6357 * Loads the Record cache from the configured Proxy using the configured Reader.
6359 * If using remote paging, then the first load call must specify the <em>start</em>
6360 * and <em>limit</em> properties in the options.params property to establish the initial
6361 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6363 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6364 * and this call will return before the new data has been loaded. Perform any post-processing
6365 * in a callback function, or in a "load" event handler.</strong>
6367 * @param {Object} options An object containing properties which control loading options:<ul>
6368 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6369 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6370 * passed the following arguments:<ul>
6371 * <li>r : Roo.data.Record[]</li>
6372 * <li>options: Options object from the load call</li>
6373 * <li>success: Boolean success indicator</li></ul></li>
6374 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6375 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6378 load : function(options){
6379 options = options || {};
6380 if(this.fireEvent("beforeload", this, options) !== false){
6381 this.storeOptions(options);
6382 var p = Roo.apply(options.params || {}, this.baseParams);
6383 // if meta was not loaded from remote source.. try requesting it.
6384 if (!this.reader.metaFromRemote) {
6387 if(this.sortInfo && this.remoteSort){
6388 var pn = this.paramNames;
6389 p[pn["sort"]] = this.sortInfo.field;
6390 p[pn["dir"]] = this.sortInfo.direction;
6392 if (this.multiSort) {
6393 var pn = this.paramNames;
6394 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6397 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6402 * Reloads the Record cache from the configured Proxy using the configured Reader and
6403 * the options from the last load operation performed.
6404 * @param {Object} options (optional) An object containing properties which may override the options
6405 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6406 * the most recently used options are reused).
6408 reload : function(options){
6409 this.load(Roo.applyIf(options||{}, this.lastOptions));
6413 // Called as a callback by the Reader during a load operation.
6414 loadRecords : function(o, options, success){
6415 if(!o || success === false){
6416 if(success !== false){
6417 this.fireEvent("load", this, [], options, o);
6419 if(options.callback){
6420 options.callback.call(options.scope || this, [], options, false);
6424 // if data returned failure - throw an exception.
6425 if (o.success === false) {
6426 // show a message if no listener is registered.
6427 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6428 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6430 // loadmask wil be hooked into this..
6431 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6434 var r = o.records, t = o.totalRecords || r.length;
6436 this.fireEvent("beforeloadadd", this, r, options, o);
6438 if(!options || options.add !== true){
6439 if(this.pruneModifiedRecords){
6442 for(var i = 0, len = r.length; i < len; i++){
6446 this.data = this.snapshot;
6447 delete this.snapshot;
6450 this.data.addAll(r);
6451 this.totalLength = t;
6453 this.fireEvent("datachanged", this);
6455 this.totalLength = Math.max(t, this.data.length+r.length);
6458 this.fireEvent("load", this, r, options, o);
6459 if(options.callback){
6460 options.callback.call(options.scope || this, r, options, true);
6466 * Loads data from a passed data block. A Reader which understands the format of the data
6467 * must have been configured in the constructor.
6468 * @param {Object} data The data block from which to read the Records. The format of the data expected
6469 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6470 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6472 loadData : function(o, append){
6473 var r = this.reader.readRecords(o);
6474 this.loadRecords(r, {add: append}, true);
6478 * Gets the number of cached records.
6480 * <em>If using paging, this may not be the total size of the dataset. If the data object
6481 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6482 * the data set size</em>
6484 getCount : function(){
6485 return this.data.length || 0;
6489 * Gets the total number of records in the dataset as returned by the server.
6491 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6492 * the dataset size</em>
6494 getTotalCount : function(){
6495 return this.totalLength || 0;
6499 * Returns the sort state of the Store as an object with two properties:
6501 field {String} The name of the field by which the Records are sorted
6502 direction {String} The sort order, "ASC" or "DESC"
6505 getSortState : function(){
6506 return this.sortInfo;
6510 applySort : function(){
6511 if(this.sortInfo && !this.remoteSort){
6512 var s = this.sortInfo, f = s.field;
6513 var st = this.fields.get(f).sortType;
6514 var fn = function(r1, r2){
6515 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6516 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6518 this.data.sort(s.direction, fn);
6519 if(this.snapshot && this.snapshot != this.data){
6520 this.snapshot.sort(s.direction, fn);
6526 * Sets the default sort column and order to be used by the next load operation.
6527 * @param {String} fieldName The name of the field to sort by.
6528 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6530 setDefaultSort : function(field, dir){
6531 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6536 * If remote sorting is used, the sort is performed on the server, and the cache is
6537 * reloaded. If local sorting is used, the cache is sorted internally.
6538 * @param {String} fieldName The name of the field to sort by.
6539 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6541 sort : function(fieldName, dir){
6542 var f = this.fields.get(fieldName);
6544 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6546 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6547 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6552 this.sortToggle[f.name] = dir;
6553 this.sortInfo = {field: f.name, direction: dir};
6554 if(!this.remoteSort){
6556 this.fireEvent("datachanged", this);
6558 this.load(this.lastOptions);
6563 * Calls the specified function for each of the Records in the cache.
6564 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6565 * Returning <em>false</em> aborts and exits the iteration.
6566 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6568 each : function(fn, scope){
6569 this.data.each(fn, scope);
6573 * Gets all records modified since the last commit. Modified records are persisted across load operations
6574 * (e.g., during paging).
6575 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6577 getModifiedRecords : function(){
6578 return this.modified;
6582 createFilterFn : function(property, value, anyMatch){
6583 if(!value.exec){ // not a regex
6584 value = String(value);
6585 if(value.length == 0){
6588 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6591 return value.test(r.data[property]);
6596 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6597 * @param {String} property A field on your records
6598 * @param {Number} start The record index to start at (defaults to 0)
6599 * @param {Number} end The last record index to include (defaults to length - 1)
6600 * @return {Number} The sum
6602 sum : function(property, start, end){
6603 var rs = this.data.items, v = 0;
6605 end = (end || end === 0) ? end : rs.length-1;
6607 for(var i = start; i <= end; i++){
6608 v += (rs[i].data[property] || 0);
6614 * Filter the records by a specified property.
6615 * @param {String} field A field on your records
6616 * @param {String/RegExp} value Either a string that the field
6617 * should start with or a RegExp to test against the field
6618 * @param {Boolean} anyMatch True to match any part not just the beginning
6620 filter : function(property, value, anyMatch){
6621 var fn = this.createFilterFn(property, value, anyMatch);
6622 return fn ? this.filterBy(fn) : this.clearFilter();
6626 * Filter by a function. The specified function will be called with each
6627 * record in this data source. If the function returns true the record is included,
6628 * otherwise it is filtered.
6629 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6630 * @param {Object} scope (optional) The scope of the function (defaults to this)
6632 filterBy : function(fn, scope){
6633 this.snapshot = this.snapshot || this.data;
6634 this.data = this.queryBy(fn, scope||this);
6635 this.fireEvent("datachanged", this);
6639 * Query the records by a specified property.
6640 * @param {String} field A field on your records
6641 * @param {String/RegExp} value Either a string that the field
6642 * should start with or a RegExp to test against the field
6643 * @param {Boolean} anyMatch True to match any part not just the beginning
6644 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6646 query : function(property, value, anyMatch){
6647 var fn = this.createFilterFn(property, value, anyMatch);
6648 return fn ? this.queryBy(fn) : this.data.clone();
6652 * Query by a function. The specified function will be called with each
6653 * record in this data source. If the function returns true the record is included
6655 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6656 * @param {Object} scope (optional) The scope of the function (defaults to this)
6657 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6659 queryBy : function(fn, scope){
6660 var data = this.snapshot || this.data;
6661 return data.filterBy(fn, scope||this);
6665 * Collects unique values for a particular dataIndex from this store.
6666 * @param {String} dataIndex The property to collect
6667 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6668 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6669 * @return {Array} An array of the unique values
6671 collect : function(dataIndex, allowNull, bypassFilter){
6672 var d = (bypassFilter === true && this.snapshot) ?
6673 this.snapshot.items : this.data.items;
6674 var v, sv, r = [], l = {};
6675 for(var i = 0, len = d.length; i < len; i++){
6676 v = d[i].data[dataIndex];
6678 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6687 * Revert to a view of the Record cache with no filtering applied.
6688 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6690 clearFilter : function(suppressEvent){
6691 if(this.snapshot && this.snapshot != this.data){
6692 this.data = this.snapshot;
6693 delete this.snapshot;
6694 if(suppressEvent !== true){
6695 this.fireEvent("datachanged", this);
6701 afterEdit : function(record){
6702 if(this.modified.indexOf(record) == -1){
6703 this.modified.push(record);
6705 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6709 afterReject : function(record){
6710 this.modified.remove(record);
6711 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6715 afterCommit : function(record){
6716 this.modified.remove(record);
6717 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6721 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6722 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6724 commitChanges : function(){
6725 var m = this.modified.slice(0);
6727 for(var i = 0, len = m.length; i < len; i++){
6733 * Cancel outstanding changes on all changed records.
6735 rejectChanges : function(){
6736 var m = this.modified.slice(0);
6738 for(var i = 0, len = m.length; i < len; i++){
6743 onMetaChange : function(meta, rtype, o){
6744 this.recordType = rtype;
6745 this.fields = rtype.prototype.fields;
6746 delete this.snapshot;
6747 this.sortInfo = meta.sortInfo || this.sortInfo;
6749 this.fireEvent('metachange', this, this.reader.meta);
6752 moveIndex : function(data, type)
6754 var index = this.indexOf(data);
6756 var newIndex = index + type;
6760 this.insert(newIndex, data);
6765 * Ext JS Library 1.1.1
6766 * Copyright(c) 2006-2007, Ext JS, LLC.
6768 * Originally Released Under LGPL - original licence link has changed is not relivant.
6771 * <script type="text/javascript">
6775 * @class Roo.data.SimpleStore
6776 * @extends Roo.data.Store
6777 * Small helper class to make creating Stores from Array data easier.
6778 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6779 * @cfg {Array} fields An array of field definition objects, or field name strings.
6780 * @cfg {Array} data The multi-dimensional array of data
6782 * @param {Object} config
6784 Roo.data.SimpleStore = function(config){
6785 Roo.data.SimpleStore.superclass.constructor.call(this, {
6787 reader: new Roo.data.ArrayReader({
6790 Roo.data.Record.create(config.fields)
6792 proxy : new Roo.data.MemoryProxy(config.data)
6796 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6798 * Ext JS Library 1.1.1
6799 * Copyright(c) 2006-2007, Ext JS, LLC.
6801 * Originally Released Under LGPL - original licence link has changed is not relivant.
6804 * <script type="text/javascript">
6809 * @extends Roo.data.Store
6810 * @class Roo.data.JsonStore
6811 * Small helper class to make creating Stores for JSON data easier. <br/>
6813 var store = new Roo.data.JsonStore({
6814 url: 'get-images.php',
6816 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6819 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6820 * JsonReader and HttpProxy (unless inline data is provided).</b>
6821 * @cfg {Array} fields An array of field definition objects, or field name strings.
6823 * @param {Object} config
6825 Roo.data.JsonStore = function(c){
6826 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6827 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6828 reader: new Roo.data.JsonReader(c, c.fields)
6831 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6833 * Ext JS Library 1.1.1
6834 * Copyright(c) 2006-2007, Ext JS, LLC.
6836 * Originally Released Under LGPL - original licence link has changed is not relivant.
6839 * <script type="text/javascript">
6843 Roo.data.Field = function(config){
6844 if(typeof config == "string"){
6845 config = {name: config};
6847 Roo.apply(this, config);
6853 var st = Roo.data.SortTypes;
6854 // named sortTypes are supported, here we look them up
6855 if(typeof this.sortType == "string"){
6856 this.sortType = st[this.sortType];
6859 // set default sortType for strings and dates
6863 this.sortType = st.asUCString;
6866 this.sortType = st.asDate;
6869 this.sortType = st.none;
6874 var stripRe = /[\$,%]/g;
6876 // prebuilt conversion function for this field, instead of
6877 // switching every time we're reading a value
6879 var cv, dateFormat = this.dateFormat;
6884 cv = function(v){ return v; };
6887 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6891 return v !== undefined && v !== null && v !== '' ?
6892 parseInt(String(v).replace(stripRe, ""), 10) : '';
6897 return v !== undefined && v !== null && v !== '' ?
6898 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6903 cv = function(v){ return v === true || v === "true" || v == 1; };
6910 if(v instanceof Date){
6914 if(dateFormat == "timestamp"){
6915 return new Date(v*1000);
6917 return Date.parseDate(v, dateFormat);
6919 var parsed = Date.parse(v);
6920 return parsed ? new Date(parsed) : null;
6929 Roo.data.Field.prototype = {
6937 * Ext JS Library 1.1.1
6938 * Copyright(c) 2006-2007, Ext JS, LLC.
6940 * Originally Released Under LGPL - original licence link has changed is not relivant.
6943 * <script type="text/javascript">
6946 // Base class for reading structured data from a data source. This class is intended to be
6947 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6950 * @class Roo.data.DataReader
6951 * Base class for reading structured data from a data source. This class is intended to be
6952 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6955 Roo.data.DataReader = function(meta, recordType){
6959 this.recordType = recordType instanceof Array ?
6960 Roo.data.Record.create(recordType) : recordType;
6963 Roo.data.DataReader.prototype = {
6965 * Create an empty record
6966 * @param {Object} data (optional) - overlay some values
6967 * @return {Roo.data.Record} record created.
6969 newRow : function(d) {
6971 this.recordType.prototype.fields.each(function(c) {
6973 case 'int' : da[c.name] = 0; break;
6974 case 'date' : da[c.name] = new Date(); break;
6975 case 'float' : da[c.name] = 0.0; break;
6976 case 'boolean' : da[c.name] = false; break;
6977 default : da[c.name] = ""; break;
6981 return new this.recordType(Roo.apply(da, d));
6986 * Ext JS Library 1.1.1
6987 * Copyright(c) 2006-2007, Ext JS, LLC.
6989 * Originally Released Under LGPL - original licence link has changed is not relivant.
6992 * <script type="text/javascript">
6996 * @class Roo.data.DataProxy
6997 * @extends Roo.data.Observable
6998 * This class is an abstract base class for implementations which provide retrieval of
6999 * unformatted data objects.<br>
7001 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7002 * (of the appropriate type which knows how to parse the data object) to provide a block of
7003 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7005 * Custom implementations must implement the load method as described in
7006 * {@link Roo.data.HttpProxy#load}.
7008 Roo.data.DataProxy = function(){
7012 * Fires before a network request is made to retrieve a data object.
7013 * @param {Object} This DataProxy object.
7014 * @param {Object} params The params parameter to the load function.
7019 * Fires before the load method's callback is called.
7020 * @param {Object} This DataProxy object.
7021 * @param {Object} o The data object.
7022 * @param {Object} arg The callback argument object passed to the load function.
7026 * @event loadexception
7027 * Fires if an Exception occurs during data retrieval.
7028 * @param {Object} This DataProxy object.
7029 * @param {Object} o The data object.
7030 * @param {Object} arg The callback argument object passed to the load function.
7031 * @param {Object} e The Exception.
7033 loadexception : true
7035 Roo.data.DataProxy.superclass.constructor.call(this);
7038 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7041 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7045 * Ext JS Library 1.1.1
7046 * Copyright(c) 2006-2007, Ext JS, LLC.
7048 * Originally Released Under LGPL - original licence link has changed is not relivant.
7051 * <script type="text/javascript">
7054 * @class Roo.data.MemoryProxy
7055 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7056 * to the Reader when its load method is called.
7058 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7060 Roo.data.MemoryProxy = function(data){
7064 Roo.data.MemoryProxy.superclass.constructor.call(this);
7068 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7070 * Load data from the requested source (in this case an in-memory
7071 * data object passed to the constructor), read the data object into
7072 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7073 * process that block using the passed callback.
7074 * @param {Object} params This parameter is not used by the MemoryProxy class.
7075 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7076 * object into a block of Roo.data.Records.
7077 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7078 * The function must be passed <ul>
7079 * <li>The Record block object</li>
7080 * <li>The "arg" argument from the load function</li>
7081 * <li>A boolean success indicator</li>
7083 * @param {Object} scope The scope in which to call the callback
7084 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7086 load : function(params, reader, callback, scope, arg){
7087 params = params || {};
7090 result = reader.readRecords(this.data);
7092 this.fireEvent("loadexception", this, arg, null, e);
7093 callback.call(scope, null, arg, false);
7096 callback.call(scope, result, arg, true);
7100 update : function(params, records){
7105 * Ext JS Library 1.1.1
7106 * Copyright(c) 2006-2007, Ext JS, LLC.
7108 * Originally Released Under LGPL - original licence link has changed is not relivant.
7111 * <script type="text/javascript">
7114 * @class Roo.data.HttpProxy
7115 * @extends Roo.data.DataProxy
7116 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7117 * configured to reference a certain URL.<br><br>
7119 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7120 * from which the running page was served.<br><br>
7122 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7124 * Be aware that to enable the browser to parse an XML document, the server must set
7125 * the Content-Type header in the HTTP response to "text/xml".
7127 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7128 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7129 * will be used to make the request.
7131 Roo.data.HttpProxy = function(conn){
7132 Roo.data.HttpProxy.superclass.constructor.call(this);
7133 // is conn a conn config or a real conn?
7135 this.useAjax = !conn || !conn.events;
7139 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7140 // thse are take from connection...
7143 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7146 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7147 * extra parameters to each request made by this object. (defaults to undefined)
7150 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7151 * to each request made by this object. (defaults to undefined)
7154 * @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)
7157 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7160 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7166 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7170 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7171 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7172 * a finer-grained basis than the DataProxy events.
7174 getConnection : function(){
7175 return this.useAjax ? Roo.Ajax : this.conn;
7179 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7180 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7181 * process that block using the passed callback.
7182 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7183 * for the request to the remote server.
7184 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7185 * object into a block of Roo.data.Records.
7186 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7187 * The function must be passed <ul>
7188 * <li>The Record block object</li>
7189 * <li>The "arg" argument from the load function</li>
7190 * <li>A boolean success indicator</li>
7192 * @param {Object} scope The scope in which to call the callback
7193 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7195 load : function(params, reader, callback, scope, arg){
7196 if(this.fireEvent("beforeload", this, params) !== false){
7198 params : params || {},
7200 callback : callback,
7205 callback : this.loadResponse,
7209 Roo.applyIf(o, this.conn);
7210 if(this.activeRequest){
7211 Roo.Ajax.abort(this.activeRequest);
7213 this.activeRequest = Roo.Ajax.request(o);
7215 this.conn.request(o);
7218 callback.call(scope||this, null, arg, false);
7223 loadResponse : function(o, success, response){
7224 delete this.activeRequest;
7226 this.fireEvent("loadexception", this, o, response);
7227 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7232 result = o.reader.read(response);
7234 this.fireEvent("loadexception", this, o, response, e);
7235 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7239 this.fireEvent("load", this, o, o.request.arg);
7240 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7244 update : function(dataSet){
7249 updateResponse : function(dataSet){
7254 * Ext JS Library 1.1.1
7255 * Copyright(c) 2006-2007, Ext JS, LLC.
7257 * Originally Released Under LGPL - original licence link has changed is not relivant.
7260 * <script type="text/javascript">
7264 * @class Roo.data.ScriptTagProxy
7265 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7266 * other than the originating domain of the running page.<br><br>
7268 * <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
7269 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7271 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7272 * source code that is used as the source inside a <script> tag.<br><br>
7274 * In order for the browser to process the returned data, the server must wrap the data object
7275 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7276 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7277 * depending on whether the callback name was passed:
7280 boolean scriptTag = false;
7281 String cb = request.getParameter("callback");
7284 response.setContentType("text/javascript");
7286 response.setContentType("application/x-json");
7288 Writer out = response.getWriter();
7290 out.write(cb + "(");
7292 out.print(dataBlock.toJsonString());
7299 * @param {Object} config A configuration object.
7301 Roo.data.ScriptTagProxy = function(config){
7302 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7303 Roo.apply(this, config);
7304 this.head = document.getElementsByTagName("head")[0];
7307 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7309 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7311 * @cfg {String} url The URL from which to request the data object.
7314 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7318 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7319 * the server the name of the callback function set up by the load call to process the returned data object.
7320 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7321 * javascript output which calls this named function passing the data object as its only parameter.
7323 callbackParam : "callback",
7325 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7326 * name to the request.
7331 * Load data from the configured URL, read the data object into
7332 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7333 * process that block using the passed callback.
7334 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7335 * for the request to the remote server.
7336 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7337 * object into a block of Roo.data.Records.
7338 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7339 * The function must be passed <ul>
7340 * <li>The Record block object</li>
7341 * <li>The "arg" argument from the load function</li>
7342 * <li>A boolean success indicator</li>
7344 * @param {Object} scope The scope in which to call the callback
7345 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7347 load : function(params, reader, callback, scope, arg){
7348 if(this.fireEvent("beforeload", this, params) !== false){
7350 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7353 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7355 url += "&_dc=" + (new Date().getTime());
7357 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7360 cb : "stcCallback"+transId,
7361 scriptId : "stcScript"+transId,
7365 callback : callback,
7371 window[trans.cb] = function(o){
7372 conn.handleResponse(o, trans);
7375 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7377 if(this.autoAbort !== false){
7381 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7383 var script = document.createElement("script");
7384 script.setAttribute("src", url);
7385 script.setAttribute("type", "text/javascript");
7386 script.setAttribute("id", trans.scriptId);
7387 this.head.appendChild(script);
7391 callback.call(scope||this, null, arg, false);
7396 isLoading : function(){
7397 return this.trans ? true : false;
7401 * Abort the current server request.
7404 if(this.isLoading()){
7405 this.destroyTrans(this.trans);
7410 destroyTrans : function(trans, isLoaded){
7411 this.head.removeChild(document.getElementById(trans.scriptId));
7412 clearTimeout(trans.timeoutId);
7414 window[trans.cb] = undefined;
7416 delete window[trans.cb];
7419 // if hasn't been loaded, wait for load to remove it to prevent script error
7420 window[trans.cb] = function(){
7421 window[trans.cb] = undefined;
7423 delete window[trans.cb];
7430 handleResponse : function(o, trans){
7432 this.destroyTrans(trans, true);
7435 result = trans.reader.readRecords(o);
7437 this.fireEvent("loadexception", this, o, trans.arg, e);
7438 trans.callback.call(trans.scope||window, null, trans.arg, false);
7441 this.fireEvent("load", this, o, trans.arg);
7442 trans.callback.call(trans.scope||window, result, trans.arg, true);
7446 handleFailure : function(trans){
7448 this.destroyTrans(trans, false);
7449 this.fireEvent("loadexception", this, null, trans.arg);
7450 trans.callback.call(trans.scope||window, null, trans.arg, false);
7454 * Ext JS Library 1.1.1
7455 * Copyright(c) 2006-2007, Ext JS, LLC.
7457 * Originally Released Under LGPL - original licence link has changed is not relivant.
7460 * <script type="text/javascript">
7464 * @class Roo.data.JsonReader
7465 * @extends Roo.data.DataReader
7466 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7467 * based on mappings in a provided Roo.data.Record constructor.
7469 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7470 * in the reply previously.
7475 var RecordDef = Roo.data.Record.create([
7476 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7477 {name: 'occupation'} // This field will use "occupation" as the mapping.
7479 var myReader = new Roo.data.JsonReader({
7480 totalProperty: "results", // The property which contains the total dataset size (optional)
7481 root: "rows", // The property which contains an Array of row objects
7482 id: "id" // The property within each row object that provides an ID for the record (optional)
7486 * This would consume a JSON file like this:
7488 { 'results': 2, 'rows': [
7489 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7490 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7493 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7494 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7495 * paged from the remote server.
7496 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7497 * @cfg {String} root name of the property which contains the Array of row objects.
7498 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7500 * Create a new JsonReader
7501 * @param {Object} meta Metadata configuration options
7502 * @param {Object} recordType Either an Array of field definition objects,
7503 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7505 Roo.data.JsonReader = function(meta, recordType){
7508 // set some defaults:
7510 totalProperty: 'total',
7511 successProperty : 'success',
7516 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7518 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7521 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7522 * Used by Store query builder to append _requestMeta to params.
7525 metaFromRemote : false,
7527 * This method is only used by a DataProxy which has retrieved data from a remote server.
7528 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7529 * @return {Object} data A data block which is used by an Roo.data.Store object as
7530 * a cache of Roo.data.Records.
7532 read : function(response){
7533 var json = response.responseText;
7535 var o = /* eval:var:o */ eval("("+json+")");
7537 throw {message: "JsonReader.read: Json object not found"};
7543 this.metaFromRemote = true;
7544 this.meta = o.metaData;
7545 this.recordType = Roo.data.Record.create(o.metaData.fields);
7546 this.onMetaChange(this.meta, this.recordType, o);
7548 return this.readRecords(o);
7551 // private function a store will implement
7552 onMetaChange : function(meta, recordType, o){
7559 simpleAccess: function(obj, subsc) {
7566 getJsonAccessor: function(){
7568 return function(expr) {
7570 return(re.test(expr))
7571 ? new Function("obj", "return obj." + expr)
7581 * Create a data block containing Roo.data.Records from an XML document.
7582 * @param {Object} o An object which contains an Array of row objects in the property specified
7583 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7584 * which contains the total size of the dataset.
7585 * @return {Object} data A data block which is used by an Roo.data.Store object as
7586 * a cache of Roo.data.Records.
7588 readRecords : function(o){
7590 * After any data loads, the raw JSON data is available for further custom processing.
7594 var s = this.meta, Record = this.recordType,
7595 f = Record.prototype.fields, fi = f.items, fl = f.length;
7597 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7599 if(s.totalProperty) {
7600 this.getTotal = this.getJsonAccessor(s.totalProperty);
7602 if(s.successProperty) {
7603 this.getSuccess = this.getJsonAccessor(s.successProperty);
7605 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7607 var g = this.getJsonAccessor(s.id);
7608 this.getId = function(rec) {
7610 return (r === undefined || r === "") ? null : r;
7613 this.getId = function(){return null;};
7616 for(var jj = 0; jj < fl; jj++){
7618 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7619 this.ef[jj] = this.getJsonAccessor(map);
7623 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7624 if(s.totalProperty){
7625 var vt = parseInt(this.getTotal(o), 10);
7630 if(s.successProperty){
7631 var vs = this.getSuccess(o);
7632 if(vs === false || vs === 'false'){
7637 for(var i = 0; i < c; i++){
7640 var id = this.getId(n);
7641 for(var j = 0; j < fl; j++){
7643 var v = this.ef[j](n);
7645 Roo.log('missing convert for ' + f.name);
7649 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7651 var record = new Record(values, id);
7653 records[i] = record;
7659 totalRecords : totalRecords
7664 * Ext JS Library 1.1.1
7665 * Copyright(c) 2006-2007, Ext JS, LLC.
7667 * Originally Released Under LGPL - original licence link has changed is not relivant.
7670 * <script type="text/javascript">
7674 * @class Roo.data.ArrayReader
7675 * @extends Roo.data.DataReader
7676 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7677 * Each element of that Array represents a row of data fields. The
7678 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7679 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7683 var RecordDef = Roo.data.Record.create([
7684 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7685 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7687 var myReader = new Roo.data.ArrayReader({
7688 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7692 * This would consume an Array like this:
7694 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7696 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7698 * Create a new JsonReader
7699 * @param {Object} meta Metadata configuration options.
7700 * @param {Object} recordType Either an Array of field definition objects
7701 * as specified to {@link Roo.data.Record#create},
7702 * or an {@link Roo.data.Record} object
7703 * created using {@link Roo.data.Record#create}.
7705 Roo.data.ArrayReader = function(meta, recordType){
7706 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7709 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7711 * Create a data block containing Roo.data.Records from an XML document.
7712 * @param {Object} o An Array of row objects which represents the dataset.
7713 * @return {Object} data A data block which is used by an Roo.data.Store object as
7714 * a cache of Roo.data.Records.
7716 readRecords : function(o){
7717 var sid = this.meta ? this.meta.id : null;
7718 var recordType = this.recordType, fields = recordType.prototype.fields;
7721 for(var i = 0; i < root.length; i++){
7724 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7725 for(var j = 0, jlen = fields.length; j < jlen; j++){
7726 var f = fields.items[j];
7727 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7728 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7732 var record = new recordType(values, id);
7734 records[records.length] = record;
7738 totalRecords : records.length
7747 * @class Roo.bootstrap.ComboBox
7748 * @extends Roo.bootstrap.TriggerField
7749 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7750 * @cfg {Boolean} append (true|false) default false
7752 * Create a new ComboBox.
7753 * @param {Object} config Configuration options
7755 Roo.bootstrap.ComboBox = function(config){
7756 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7760 * Fires when the dropdown list is expanded
7761 * @param {Roo.bootstrap.ComboBox} combo This combo box
7766 * Fires when the dropdown list is collapsed
7767 * @param {Roo.bootstrap.ComboBox} combo This combo box
7771 * @event beforeselect
7772 * Fires before a list item is selected. Return false to cancel the selection.
7773 * @param {Roo.bootstrap.ComboBox} combo This combo box
7774 * @param {Roo.data.Record} record The data record returned from the underlying store
7775 * @param {Number} index The index of the selected item in the dropdown list
7777 'beforeselect' : true,
7780 * Fires when a list item is selected
7781 * @param {Roo.bootstrap.ComboBox} combo This combo box
7782 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7783 * @param {Number} index The index of the selected item in the dropdown list
7787 * @event beforequery
7788 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7789 * The event object passed has these properties:
7790 * @param {Roo.bootstrap.ComboBox} combo This combo box
7791 * @param {String} query The query
7792 * @param {Boolean} forceAll true to force "all" query
7793 * @param {Boolean} cancel true to cancel the query
7794 * @param {Object} e The query event object
7796 'beforequery': true,
7799 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7800 * @param {Roo.bootstrap.ComboBox} combo This combo box
7805 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7806 * @param {Roo.bootstrap.ComboBox} combo This combo box
7807 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7812 * Fires when the remove value from the combobox array
7813 * @param {Roo.bootstrap.ComboBox} combo This combo box
7820 this.selectedIndex = -1;
7821 if(this.mode == 'local'){
7822 if(config.queryDelay === undefined){
7823 this.queryDelay = 10;
7825 if(config.minChars === undefined){
7831 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7834 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7835 * rendering into an Roo.Editor, defaults to false)
7838 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7839 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7842 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7845 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7846 * the dropdown list (defaults to undefined, with no header element)
7850 * @cfg {String/Roo.Template} tpl The template to use to render the output
7854 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7856 listWidth: undefined,
7858 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7859 * mode = 'remote' or 'text' if mode = 'local')
7861 displayField: undefined,
7863 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7864 * mode = 'remote' or 'value' if mode = 'local').
7865 * Note: use of a valueField requires the user make a selection
7866 * in order for a value to be mapped.
7868 valueField: undefined,
7872 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7873 * field's data value (defaults to the underlying DOM element's name)
7875 hiddenName: undefined,
7877 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7881 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7883 selectedClass: 'active',
7886 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7890 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7891 * anchor positions (defaults to 'tl-bl')
7893 listAlign: 'tl-bl?',
7895 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7899 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7900 * query specified by the allQuery config option (defaults to 'query')
7902 triggerAction: 'query',
7904 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7905 * (defaults to 4, does not apply if editable = false)
7909 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7910 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7914 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7915 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7919 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7920 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7924 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7925 * when editable = true (defaults to false)
7927 selectOnFocus:false,
7929 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7931 queryParam: 'query',
7933 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7934 * when mode = 'remote' (defaults to 'Loading...')
7936 loadingText: 'Loading...',
7938 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7942 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7946 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7947 * traditional select (defaults to true)
7951 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7955 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7959 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7960 * listWidth has a higher value)
7964 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7965 * allow the user to set arbitrary text into the field (defaults to false)
7967 forceSelection:false,
7969 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7970 * if typeAhead = true (defaults to 250)
7972 typeAheadDelay : 250,
7974 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7975 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7977 valueNotFoundText : undefined,
7979 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7984 * @cfg {Boolean} disableClear Disable showing of clear button.
7986 disableClear : false,
7988 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
7990 alwaysQuery : false,
7993 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8007 // element that contains real text value.. (when hidden is used..)
8010 initEvents: function(){
8013 throw "can not find store for combo";
8015 this.store = Roo.factory(this.store, Roo.data);
8019 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8022 if(this.hiddenName){
8024 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8026 this.hiddenField.dom.value =
8027 this.hiddenValue !== undefined ? this.hiddenValue :
8028 this.value !== undefined ? this.value : '';
8030 // prevent input submission
8031 this.el.dom.removeAttribute('name');
8032 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8037 // this.el.dom.setAttribute('autocomplete', 'off');
8040 var cls = 'x-combo-list';
8041 this.list = this.el.select('ul.dropdown-menu',true).first();
8043 //this.list = new Roo.Layer({
8044 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8047 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8048 this.list.setWidth(lw);
8050 this.list.on('mouseover', this.onViewOver, this);
8051 this.list.on('mousemove', this.onViewMove, this);
8053 this.list.on('scroll', this.onViewScroll, this);
8056 this.list.swallowEvent('mousewheel');
8057 this.assetHeight = 0;
8060 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8061 this.assetHeight += this.header.getHeight();
8064 this.innerList = this.list.createChild({cls:cls+'-inner'});
8065 this.innerList.on('mouseover', this.onViewOver, this);
8066 this.innerList.on('mousemove', this.onViewMove, this);
8067 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8069 if(this.allowBlank && !this.pageSize && !this.disableClear){
8070 this.footer = this.list.createChild({cls:cls+'-ft'});
8071 this.pageTb = new Roo.Toolbar(this.footer);
8075 this.footer = this.list.createChild({cls:cls+'-ft'});
8076 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8077 {pageSize: this.pageSize});
8081 if (this.pageTb && this.allowBlank && !this.disableClear) {
8083 this.pageTb.add(new Roo.Toolbar.Fill(), {
8084 cls: 'x-btn-icon x-btn-clear',
8090 _this.onSelect(false, -1);
8095 this.assetHeight += this.footer.getHeight();
8100 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8103 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8104 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8106 //this.view.wrapEl.setDisplayed(false);
8107 this.view.on('click', this.onViewClick, this);
8111 this.store.on('beforeload', this.onBeforeLoad, this);
8112 this.store.on('load', this.onLoad, this);
8113 this.store.on('loadexception', this.onLoadException, this);
8116 this.resizer = new Roo.Resizable(this.list, {
8117 pinned:true, handles:'se'
8119 this.resizer.on('resize', function(r, w, h){
8120 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8122 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8123 this.restrictHeight();
8125 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8129 this.editable = true;
8130 this.setEditable(false);
8135 if (typeof(this.events.add.listeners) != 'undefined') {
8137 this.addicon = this.wrap.createChild(
8138 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8140 this.addicon.on('click', function(e) {
8141 this.fireEvent('add', this);
8144 if (typeof(this.events.edit.listeners) != 'undefined') {
8146 this.editicon = this.wrap.createChild(
8147 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8149 this.editicon.setStyle('margin-left', '40px');
8151 this.editicon.on('click', function(e) {
8153 // we fire even if inothing is selected..
8154 this.fireEvent('edit', this, this.lastData );
8160 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8162 this.inKeyMode = true;
8166 "down" : function(e){
8167 if(!this.isExpanded()){
8168 this.onTriggerClick();
8170 this.inKeyMode = true;
8175 "enter" : function(e){
8180 "esc" : function(e){
8184 "tab" : function(e){
8187 if(this.fireEvent("specialkey", this, e)){
8188 this.onViewClick(false);
8196 doRelay : function(foo, bar, hname){
8197 if(hname == 'down' || this.scope.isExpanded()){
8198 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8207 this.queryDelay = Math.max(this.queryDelay || 10,
8208 this.mode == 'local' ? 10 : 250);
8211 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8214 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8216 if(this.editable !== false){
8217 this.inputEl().on("keyup", this.onKeyUp, this);
8219 if(this.forceSelection){
8220 this.on('blur', this.doForce, this);
8224 this.choices = this.el.select('ul.select2-choices', true).first();
8225 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8229 onDestroy : function(){
8231 this.view.setStore(null);
8232 this.view.el.removeAllListeners();
8233 this.view.el.remove();
8234 this.view.purgeListeners();
8237 this.list.dom.innerHTML = '';
8240 this.store.un('beforeload', this.onBeforeLoad, this);
8241 this.store.un('load', this.onLoad, this);
8242 this.store.un('loadexception', this.onLoadException, this);
8244 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8248 fireKey : function(e){
8249 if(e.isNavKeyPress() && !this.list.isVisible()){
8250 this.fireEvent("specialkey", this, e);
8255 onResize: function(w, h){
8256 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8258 // if(typeof w != 'number'){
8259 // // we do not handle it!?!?
8262 // var tw = this.trigger.getWidth();
8263 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8264 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8266 // this.inputEl().setWidth( this.adjustWidth('input', x));
8268 // //this.trigger.setStyle('left', x+'px');
8270 // if(this.list && this.listWidth === undefined){
8271 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8272 // this.list.setWidth(lw);
8273 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8281 * Allow or prevent the user from directly editing the field text. If false is passed,
8282 * the user will only be able to select from the items defined in the dropdown list. This method
8283 * is the runtime equivalent of setting the 'editable' config option at config time.
8284 * @param {Boolean} value True to allow the user to directly edit the field text
8286 setEditable : function(value){
8287 if(value == this.editable){
8290 this.editable = value;
8292 this.inputEl().dom.setAttribute('readOnly', true);
8293 this.inputEl().on('mousedown', this.onTriggerClick, this);
8294 this.inputEl().addClass('x-combo-noedit');
8296 this.inputEl().dom.setAttribute('readOnly', false);
8297 this.inputEl().un('mousedown', this.onTriggerClick, this);
8298 this.inputEl().removeClass('x-combo-noedit');
8304 onBeforeLoad : function(combo,opts){
8309 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8311 this.restrictHeight();
8312 this.selectedIndex = -1;
8316 onLoad : function(){
8318 this.hasQuery = false;
8324 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8325 this.loading.hide();
8328 if(this.store.getCount() > 0){
8330 this.restrictHeight();
8331 if(this.lastQuery == this.allQuery){
8333 this.inputEl().dom.select();
8335 if(!this.selectByValue(this.value, true)){
8336 this.select(0, true);
8340 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8341 this.taTask.delay(this.typeAheadDelay);
8345 this.onEmptyResults();
8351 onLoadException : function()
8353 this.hasQuery = false;
8355 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8356 this.loading.hide();
8360 Roo.log(this.store.reader.jsonData);
8361 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8363 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8369 onTypeAhead : function(){
8370 if(this.store.getCount() > 0){
8371 var r = this.store.getAt(0);
8372 var newValue = r.data[this.displayField];
8373 var len = newValue.length;
8374 var selStart = this.getRawValue().length;
8376 if(selStart != len){
8377 this.setRawValue(newValue);
8378 this.selectText(selStart, newValue.length);
8384 onSelect : function(record, index){
8386 if(this.fireEvent('beforeselect', this, record, index) !== false){
8388 this.setFromData(index > -1 ? record.data : false);
8391 this.fireEvent('select', this, record, index);
8396 * Returns the currently selected field value or empty string if no value is set.
8397 * @return {String} value The selected value
8399 getValue : function(){
8402 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8405 if(this.valueField){
8406 return typeof this.value != 'undefined' ? this.value : '';
8408 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8413 * Clears any text/value currently set in the field
8415 clearValue : function(){
8416 if(this.hiddenField){
8417 this.hiddenField.dom.value = '';
8420 this.setRawValue('');
8421 this.lastSelectionText = '';
8426 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8427 * will be displayed in the field. If the value does not match the data value of an existing item,
8428 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8429 * Otherwise the field will be blank (although the value will still be set).
8430 * @param {String} value The value to match
8432 setValue : function(v){
8439 if(this.valueField){
8440 var r = this.findRecord(this.valueField, v);
8442 text = r.data[this.displayField];
8443 }else if(this.valueNotFoundText !== undefined){
8444 text = this.valueNotFoundText;
8447 this.lastSelectionText = text;
8448 if(this.hiddenField){
8449 this.hiddenField.dom.value = v;
8451 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8455 * @property {Object} the last set data for the element
8460 * Sets the value of the field based on a object which is related to the record format for the store.
8461 * @param {Object} value the value to set as. or false on reset?
8463 setFromData : function(o){
8470 var dv = ''; // display value
8471 var vv = ''; // value value..
8473 if (this.displayField) {
8474 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8476 // this is an error condition!!!
8477 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8480 if(this.valueField){
8481 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8484 if(this.hiddenField){
8485 this.hiddenField.dom.value = vv;
8487 this.lastSelectionText = dv;
8488 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8492 // no hidden field.. - we store the value in 'value', but still display
8493 // display field!!!!
8494 this.lastSelectionText = dv;
8495 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8502 // overridden so that last data is reset..
8503 this.setValue(this.originalValue);
8504 this.clearInvalid();
8505 this.lastData = false;
8507 this.view.clearSelections();
8511 findRecord : function(prop, value){
8513 if(this.store.getCount() > 0){
8514 this.store.each(function(r){
8515 if(r.data[prop] == value){
8527 // returns hidden if it's set..
8528 if (!this.rendered) {return ''};
8529 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8533 onViewMove : function(e, t){
8534 this.inKeyMode = false;
8538 onViewOver : function(e, t){
8539 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8542 var item = this.view.findItemFromChild(t);
8544 var index = this.view.indexOf(item);
8545 this.select(index, false);
8550 onViewClick : function(doFocus)
8552 var index = this.view.getSelectedIndexes()[0];
8553 var r = this.store.getAt(index);
8555 this.onSelect(r, index);
8557 if(doFocus !== false && !this.blockFocus){
8558 this.inputEl().focus();
8563 restrictHeight : function(){
8564 //this.innerList.dom.style.height = '';
8565 //var inner = this.innerList.dom;
8566 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8567 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8568 //this.list.beginUpdate();
8569 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8570 this.list.alignTo(this.inputEl(), this.listAlign);
8571 //this.list.endUpdate();
8575 onEmptyResults : function(){
8580 * Returns true if the dropdown list is expanded, else false.
8582 isExpanded : function(){
8583 return this.list.isVisible();
8587 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8588 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8589 * @param {String} value The data value of the item to select
8590 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8591 * selected item if it is not currently in view (defaults to true)
8592 * @return {Boolean} True if the value matched an item in the list, else false
8594 selectByValue : function(v, scrollIntoView){
8595 if(v !== undefined && v !== null){
8596 var r = this.findRecord(this.valueField || this.displayField, v);
8598 this.select(this.store.indexOf(r), scrollIntoView);
8606 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8607 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8608 * @param {Number} index The zero-based index of the list item to select
8609 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8610 * selected item if it is not currently in view (defaults to true)
8612 select : function(index, scrollIntoView){
8613 this.selectedIndex = index;
8614 this.view.select(index);
8615 if(scrollIntoView !== false){
8616 var el = this.view.getNode(index);
8618 //this.innerList.scrollChildIntoView(el, false);
8625 selectNext : function(){
8626 var ct = this.store.getCount();
8628 if(this.selectedIndex == -1){
8630 }else if(this.selectedIndex < ct-1){
8631 this.select(this.selectedIndex+1);
8637 selectPrev : function(){
8638 var ct = this.store.getCount();
8640 if(this.selectedIndex == -1){
8642 }else if(this.selectedIndex != 0){
8643 this.select(this.selectedIndex-1);
8649 onKeyUp : function(e){
8650 if(this.editable !== false && !e.isSpecialKey()){
8651 this.lastKey = e.getKey();
8652 this.dqTask.delay(this.queryDelay);
8657 validateBlur : function(){
8658 return !this.list || !this.list.isVisible();
8662 initQuery : function(){
8663 this.doQuery(this.getRawValue());
8667 doForce : function(){
8668 if(this.el.dom.value.length > 0){
8670 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8676 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8677 * query allowing the query action to be canceled if needed.
8678 * @param {String} query The SQL query to execute
8679 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8680 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8681 * saved in the current store (defaults to false)
8683 doQuery : function(q, forceAll){
8685 if(q === undefined || q === null){
8694 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8699 forceAll = qe.forceAll;
8700 if(forceAll === true || (q.length >= this.minChars)){
8702 this.hasQuery = true;
8704 if(this.lastQuery != q || this.alwaysQuery){
8706 if(this.mode == 'local'){
8707 this.selectedIndex = -1;
8709 this.store.clearFilter();
8711 this.store.filter(this.displayField, q);
8715 this.store.baseParams[this.queryParam] = q;
8717 var options = {params : this.getParams(q)};
8721 options.params.start = this.page * this.pageSize;
8724 this.store.load(options);
8728 this.selectedIndex = -1;
8733 this.loadNext = false;
8737 getParams : function(q){
8739 //p[this.queryParam] = q;
8743 p.limit = this.pageSize;
8749 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8751 collapse : function(){
8752 if(!this.isExpanded()){
8757 Roo.get(document).un('mousedown', this.collapseIf, this);
8758 Roo.get(document).un('mousewheel', this.collapseIf, this);
8759 if (!this.editable) {
8760 Roo.get(document).un('keydown', this.listKeyPress, this);
8762 this.fireEvent('collapse', this);
8766 collapseIf : function(e){
8767 var in_combo = e.within(this.el);
8768 var in_list = e.within(this.list);
8770 if (in_combo || in_list) {
8771 //e.stopPropagation();
8780 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8782 expand : function(){
8784 if(this.isExpanded() || !this.hasFocus){
8788 this.list.alignTo(this.inputEl(), this.listAlign);
8790 Roo.get(document).on('mousedown', this.collapseIf, this);
8791 Roo.get(document).on('mousewheel', this.collapseIf, this);
8792 if (!this.editable) {
8793 Roo.get(document).on('keydown', this.listKeyPress, this);
8796 this.fireEvent('expand', this);
8800 // Implements the default empty TriggerField.onTriggerClick function
8801 onTriggerClick : function()
8803 Roo.log('trigger click');
8810 this.loadNext = false;
8812 if(this.isExpanded()){
8814 if (!this.blockFocus) {
8815 this.inputEl().focus();
8819 this.hasFocus = true;
8820 if(this.triggerAction == 'all') {
8821 this.doQuery(this.allQuery, true);
8823 this.doQuery(this.getRawValue());
8825 if (!this.blockFocus) {
8826 this.inputEl().focus();
8830 listKeyPress : function(e)
8832 //Roo.log('listkeypress');
8833 // scroll to first matching element based on key pres..
8834 if (e.isSpecialKey()) {
8837 var k = String.fromCharCode(e.getKey()).toUpperCase();
8840 var csel = this.view.getSelectedNodes();
8841 var cselitem = false;
8843 var ix = this.view.indexOf(csel[0]);
8844 cselitem = this.store.getAt(ix);
8845 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8851 this.store.each(function(v) {
8853 // start at existing selection.
8854 if (cselitem.id == v.id) {
8860 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8861 match = this.store.indexOf(v);
8867 if (match === false) {
8868 return true; // no more action?
8871 this.view.select(match);
8872 var sn = Roo.get(this.view.getSelectedNodes()[0])
8873 //sn.scrollIntoView(sn.dom.parentNode, false);
8876 onViewScroll : function(e, t){
8878 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8882 this.hasQuery = true;
8884 this.loading = this.list.select('.loading', true).first();
8886 if(this.loading === null){
8887 this.list.createChild({
8889 cls: 'loading select2-more-results select2-active',
8890 html: 'Loading more results...'
8893 this.loading = this.list.select('.loading', true).first();
8895 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8897 this.loading.hide();
8900 this.loading.show();
8905 this.loadNext = true;
8907 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8912 addItem : function(o)
8914 var dv = ''; // display value
8916 if (this.displayField) {
8917 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8919 // this is an error condition!!!
8920 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8927 var choice = this.choices.createChild({
8929 cls: 'select2-search-choice',
8938 cls: 'select2-search-choice-close',
8943 }, this.searchField);
8945 var close = choice.select('a.select2-search-choice-close', true).first()
8947 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8954 this.inputEl().dom.value = '';
8958 onRemoveItem : function(e, _self, o)
8960 Roo.log('remove item');
8961 var index = this.item.indexOf(o.data) * 1;
8964 Roo.log('not this item?!');
8968 this.item.splice(index, 1);
8973 this.fireEvent('remove', this);
8977 syncValue : function()
8979 if(!this.item.length){
8986 Roo.each(this.item, function(i){
8987 if(_this.valueField){
8988 value.push(i[_this.valueField]);
8995 this.value = value.join(',');
8997 if(this.hiddenField){
8998 this.hiddenField.dom.value = this.value;
9002 clearItem : function()
9010 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9020 * @cfg {Boolean} grow
9024 * @cfg {Number} growMin
9028 * @cfg {Number} growMax
9038 * Ext JS Library 1.1.1
9039 * Copyright(c) 2006-2007, Ext JS, LLC.
9041 * Originally Released Under LGPL - original licence link has changed is not relivant.
9044 * <script type="text/javascript">
9049 * @extends Roo.util.Observable
9050 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9051 * This class also supports single and multi selection modes. <br>
9052 * Create a data model bound view:
9054 var store = new Roo.data.Store(...);
9056 var view = new Roo.View({
9058 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9061 selectedClass: "ydataview-selected",
9065 // listen for node click?
9066 view.on("click", function(vw, index, node, e){
9067 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9071 dataModel.load("foobar.xml");
9073 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9075 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9076 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9078 * Note: old style constructor is still suported (container, template, config)
9082 * @param {Object} config The config object
9085 Roo.View = function(config, depreciated_tpl, depreciated_config){
9087 if (typeof(depreciated_tpl) == 'undefined') {
9088 // new way.. - universal constructor.
9089 Roo.apply(this, config);
9090 this.el = Roo.get(this.el);
9093 this.el = Roo.get(config);
9094 this.tpl = depreciated_tpl;
9095 Roo.apply(this, depreciated_config);
9097 this.wrapEl = this.el.wrap().wrap();
9098 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9101 if(typeof(this.tpl) == "string"){
9102 this.tpl = new Roo.Template(this.tpl);
9104 // support xtype ctors..
9105 this.tpl = new Roo.factory(this.tpl, Roo);
9117 * @event beforeclick
9118 * Fires before a click is processed. Returns false to cancel the default action.
9119 * @param {Roo.View} this
9120 * @param {Number} index The index of the target node
9121 * @param {HTMLElement} node The target node
9122 * @param {Roo.EventObject} e The raw event object
9124 "beforeclick" : true,
9127 * Fires when a template node is clicked.
9128 * @param {Roo.View} this
9129 * @param {Number} index The index of the target node
9130 * @param {HTMLElement} node The target node
9131 * @param {Roo.EventObject} e The raw event object
9136 * Fires when a template node is double clicked.
9137 * @param {Roo.View} this
9138 * @param {Number} index The index of the target node
9139 * @param {HTMLElement} node The target node
9140 * @param {Roo.EventObject} e The raw event object
9144 * @event contextmenu
9145 * Fires when a template node is right clicked.
9146 * @param {Roo.View} this
9147 * @param {Number} index The index of the target node
9148 * @param {HTMLElement} node The target node
9149 * @param {Roo.EventObject} e The raw event object
9151 "contextmenu" : true,
9153 * @event selectionchange
9154 * Fires when the selected nodes change.
9155 * @param {Roo.View} this
9156 * @param {Array} selections Array of the selected nodes
9158 "selectionchange" : true,
9161 * @event beforeselect
9162 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9163 * @param {Roo.View} this
9164 * @param {HTMLElement} node The node to be selected
9165 * @param {Array} selections Array of currently selected nodes
9167 "beforeselect" : true,
9169 * @event preparedata
9170 * Fires on every row to render, to allow you to change the data.
9171 * @param {Roo.View} this
9172 * @param {Object} data to be rendered (change this)
9174 "preparedata" : true
9182 "click": this.onClick,
9183 "dblclick": this.onDblClick,
9184 "contextmenu": this.onContextMenu,
9188 this.selections = [];
9190 this.cmp = new Roo.CompositeElementLite([]);
9192 this.store = Roo.factory(this.store, Roo.data);
9193 this.setStore(this.store, true);
9196 if ( this.footer && this.footer.xtype) {
9198 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9200 this.footer.dataSource = this.store
9201 this.footer.container = fctr;
9202 this.footer = Roo.factory(this.footer, Roo);
9203 fctr.insertFirst(this.el);
9205 // this is a bit insane - as the paging toolbar seems to detach the el..
9206 // dom.parentNode.parentNode.parentNode
9207 // they get detached?
9211 Roo.View.superclass.constructor.call(this);
9216 Roo.extend(Roo.View, Roo.util.Observable, {
9219 * @cfg {Roo.data.Store} store Data store to load data from.
9224 * @cfg {String|Roo.Element} el The container element.
9229 * @cfg {String|Roo.Template} tpl The template used by this View
9233 * @cfg {String} dataName the named area of the template to use as the data area
9234 * Works with domtemplates roo-name="name"
9238 * @cfg {String} selectedClass The css class to add to selected nodes
9240 selectedClass : "x-view-selected",
9242 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9247 * @cfg {String} text to display on mask (default Loading)
9251 * @cfg {Boolean} multiSelect Allow multiple selection
9253 multiSelect : false,
9255 * @cfg {Boolean} singleSelect Allow single selection
9257 singleSelect: false,
9260 * @cfg {Boolean} toggleSelect - selecting
9262 toggleSelect : false,
9265 * Returns the element this view is bound to.
9266 * @return {Roo.Element}
9275 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9277 refresh : function(){
9281 // if we are using something like 'domtemplate', then
9282 // the what gets used is:
9283 // t.applySubtemplate(NAME, data, wrapping data..)
9284 // the outer template then get' applied with
9285 // the store 'extra data'
9286 // and the body get's added to the
9287 // roo-name="data" node?
9288 // <span class='roo-tpl-{name}'></span> ?????
9292 this.clearSelections();
9295 var records = this.store.getRange();
9296 if(records.length < 1) {
9298 // is this valid?? = should it render a template??
9300 this.el.update(this.emptyText);
9304 if (this.dataName) {
9305 this.el.update(t.apply(this.store.meta)); //????
9306 el = this.el.child('.roo-tpl-' + this.dataName);
9309 for(var i = 0, len = records.length; i < len; i++){
9310 var data = this.prepareData(records[i].data, i, records[i]);
9311 this.fireEvent("preparedata", this, data, i, records[i]);
9312 html[html.length] = Roo.util.Format.trim(
9314 t.applySubtemplate(this.dataName, data, this.store.meta) :
9321 el.update(html.join(""));
9322 this.nodes = el.dom.childNodes;
9323 this.updateIndexes(0);
9328 * Function to override to reformat the data that is sent to
9329 * the template for each node.
9330 * DEPRICATED - use the preparedata event handler.
9331 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9332 * a JSON object for an UpdateManager bound view).
9334 prepareData : function(data, index, record)
9336 this.fireEvent("preparedata", this, data, index, record);
9340 onUpdate : function(ds, record){
9341 Roo.log('on update');
9342 this.clearSelections();
9343 var index = this.store.indexOf(record);
9344 var n = this.nodes[index];
9345 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9346 n.parentNode.removeChild(n);
9347 this.updateIndexes(index, index);
9353 onAdd : function(ds, records, index)
9355 Roo.log(['on Add', ds, records, index] );
9356 this.clearSelections();
9357 if(this.nodes.length == 0){
9361 var n = this.nodes[index];
9362 for(var i = 0, len = records.length; i < len; i++){
9363 var d = this.prepareData(records[i].data, i, records[i]);
9365 this.tpl.insertBefore(n, d);
9368 this.tpl.append(this.el, d);
9371 this.updateIndexes(index);
9374 onRemove : function(ds, record, index){
9375 Roo.log('onRemove');
9376 this.clearSelections();
9377 var el = this.dataName ?
9378 this.el.child('.roo-tpl-' + this.dataName) :
9381 el.dom.removeChild(this.nodes[index]);
9382 this.updateIndexes(index);
9386 * Refresh an individual node.
9387 * @param {Number} index
9389 refreshNode : function(index){
9390 this.onUpdate(this.store, this.store.getAt(index));
9393 updateIndexes : function(startIndex, endIndex){
9394 var ns = this.nodes;
9395 startIndex = startIndex || 0;
9396 endIndex = endIndex || ns.length - 1;
9397 for(var i = startIndex; i <= endIndex; i++){
9398 ns[i].nodeIndex = i;
9403 * Changes the data store this view uses and refresh the view.
9404 * @param {Store} store
9406 setStore : function(store, initial){
9407 if(!initial && this.store){
9408 this.store.un("datachanged", this.refresh);
9409 this.store.un("add", this.onAdd);
9410 this.store.un("remove", this.onRemove);
9411 this.store.un("update", this.onUpdate);
9412 this.store.un("clear", this.refresh);
9413 this.store.un("beforeload", this.onBeforeLoad);
9414 this.store.un("load", this.onLoad);
9415 this.store.un("loadexception", this.onLoad);
9419 store.on("datachanged", this.refresh, this);
9420 store.on("add", this.onAdd, this);
9421 store.on("remove", this.onRemove, this);
9422 store.on("update", this.onUpdate, this);
9423 store.on("clear", this.refresh, this);
9424 store.on("beforeload", this.onBeforeLoad, this);
9425 store.on("load", this.onLoad, this);
9426 store.on("loadexception", this.onLoad, this);
9434 * onbeforeLoad - masks the loading area.
9437 onBeforeLoad : function(store,opts)
9439 Roo.log('onBeforeLoad');
9443 this.el.mask(this.mask ? this.mask : "Loading" );
9445 onLoad : function ()
9452 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9453 * @param {HTMLElement} node
9454 * @return {HTMLElement} The template node
9456 findItemFromChild : function(node){
9457 var el = this.dataName ?
9458 this.el.child('.roo-tpl-' + this.dataName,true) :
9461 if(!node || node.parentNode == el){
9464 var p = node.parentNode;
9465 while(p && p != el){
9466 if(p.parentNode == el){
9475 onClick : function(e){
9476 var item = this.findItemFromChild(e.getTarget());
9478 var index = this.indexOf(item);
9479 if(this.onItemClick(item, index, e) !== false){
9480 this.fireEvent("click", this, index, item, e);
9483 this.clearSelections();
9488 onContextMenu : function(e){
9489 var item = this.findItemFromChild(e.getTarget());
9491 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9496 onDblClick : function(e){
9497 var item = this.findItemFromChild(e.getTarget());
9499 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9503 onItemClick : function(item, index, e)
9505 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9508 if (this.toggleSelect) {
9509 var m = this.isSelected(item) ? 'unselect' : 'select';
9512 _t[m](item, true, false);
9515 if(this.multiSelect || this.singleSelect){
9516 if(this.multiSelect && e.shiftKey && this.lastSelection){
9517 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9519 this.select(item, this.multiSelect && e.ctrlKey);
9520 this.lastSelection = item;
9528 * Get the number of selected nodes.
9531 getSelectionCount : function(){
9532 return this.selections.length;
9536 * Get the currently selected nodes.
9537 * @return {Array} An array of HTMLElements
9539 getSelectedNodes : function(){
9540 return this.selections;
9544 * Get the indexes of the selected nodes.
9547 getSelectedIndexes : function(){
9548 var indexes = [], s = this.selections;
9549 for(var i = 0, len = s.length; i < len; i++){
9550 indexes.push(s[i].nodeIndex);
9556 * Clear all selections
9557 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9559 clearSelections : function(suppressEvent){
9560 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9561 this.cmp.elements = this.selections;
9562 this.cmp.removeClass(this.selectedClass);
9563 this.selections = [];
9565 this.fireEvent("selectionchange", this, this.selections);
9571 * Returns true if the passed node is selected
9572 * @param {HTMLElement/Number} node The node or node index
9575 isSelected : function(node){
9576 var s = this.selections;
9580 node = this.getNode(node);
9581 return s.indexOf(node) !== -1;
9586 * @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
9587 * @param {Boolean} keepExisting (optional) true to keep existing selections
9588 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9590 select : function(nodeInfo, keepExisting, suppressEvent){
9591 if(nodeInfo instanceof Array){
9593 this.clearSelections(true);
9595 for(var i = 0, len = nodeInfo.length; i < len; i++){
9596 this.select(nodeInfo[i], true, true);
9600 var node = this.getNode(nodeInfo);
9601 if(!node || this.isSelected(node)){
9602 return; // already selected.
9605 this.clearSelections(true);
9607 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9608 Roo.fly(node).addClass(this.selectedClass);
9609 this.selections.push(node);
9611 this.fireEvent("selectionchange", this, this.selections);
9619 * @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
9620 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9621 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9623 unselect : function(nodeInfo, keepExisting, suppressEvent)
9625 if(nodeInfo instanceof Array){
9626 Roo.each(this.selections, function(s) {
9627 this.unselect(s, nodeInfo);
9631 var node = this.getNode(nodeInfo);
9632 if(!node || !this.isSelected(node)){
9633 Roo.log("not selected");
9634 return; // not selected.
9638 Roo.each(this.selections, function(s) {
9640 Roo.fly(node).removeClass(this.selectedClass);
9647 this.selections= ns;
9648 this.fireEvent("selectionchange", this, this.selections);
9652 * Gets a template node.
9653 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9654 * @return {HTMLElement} The node or null if it wasn't found
9656 getNode : function(nodeInfo){
9657 if(typeof nodeInfo == "string"){
9658 return document.getElementById(nodeInfo);
9659 }else if(typeof nodeInfo == "number"){
9660 return this.nodes[nodeInfo];
9666 * Gets a range template nodes.
9667 * @param {Number} startIndex
9668 * @param {Number} endIndex
9669 * @return {Array} An array of nodes
9671 getNodes : function(start, end){
9672 var ns = this.nodes;
9674 end = typeof end == "undefined" ? ns.length - 1 : end;
9677 for(var i = start; i <= end; i++){
9681 for(var i = start; i >= end; i--){
9689 * Finds the index of the passed node
9690 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9691 * @return {Number} The index of the node or -1
9693 indexOf : function(node){
9694 node = this.getNode(node);
9695 if(typeof node.nodeIndex == "number"){
9696 return node.nodeIndex;
9698 var ns = this.nodes;
9699 for(var i = 0, len = ns.length; i < len; i++){
9710 * based on jquery fullcalendar
9714 Roo.bootstrap = Roo.bootstrap || {};
9716 * @class Roo.bootstrap.Calendar
9717 * @extends Roo.bootstrap.Component
9718 * Bootstrap Calendar class
9719 * @cfg {Boolean} loadMask (true|false) default false
9722 * Create a new Container
9723 * @param {Object} config The config object
9728 Roo.bootstrap.Calendar = function(config){
9729 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9733 * Fires when a date is selected
9734 * @param {DatePicker} this
9735 * @param {Date} date The selected date
9739 * @event monthchange
9740 * Fires when the displayed month changes
9741 * @param {DatePicker} this
9742 * @param {Date} date The selected month
9744 'monthchange': true,
9747 * Fires when mouse over an event
9748 * @param {Calendar} this
9749 * @param {event} Event
9754 * Fires when the mouse leaves an
9755 * @param {Calendar} this
9761 * Fires when the mouse click an
9762 * @param {Calendar} this
9771 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9774 * @cfg {Number} startDay
9775 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9781 getAutoCreate : function(){
9784 var fc_button = function(name, corner, style, content ) {
9785 return Roo.apply({},{
9787 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9789 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9792 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9800 style : 'width:100%',
9807 cls : 'fc-header-left',
9809 fc_button('prev', 'left', 'arrow', '‹' ),
9810 fc_button('next', 'right', 'arrow', '›' ),
9811 { tag: 'span', cls: 'fc-header-space' },
9812 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9820 cls : 'fc-header-center',
9824 cls: 'fc-header-title',
9827 html : 'month / year'
9835 cls : 'fc-header-right',
9837 /* fc_button('month', 'left', '', 'month' ),
9838 fc_button('week', '', '', 'week' ),
9839 fc_button('day', 'right', '', 'day' )
9851 var cal_heads = function() {
9853 // fixme - handle this.
9855 for (var i =0; i < Date.dayNames.length; i++) {
9856 var d = Date.dayNames[i];
9859 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9860 html : d.substring(0,3)
9864 ret[0].cls += ' fc-first';
9865 ret[6].cls += ' fc-last';
9868 var cal_cell = function(n) {
9871 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9876 cls: 'fc-day-number',
9880 cls: 'fc-day-content',
9884 style: 'position: relative;' // height: 17px;
9896 var cal_rows = function() {
9899 for (var r = 0; r < 6; r++) {
9906 for (var i =0; i < Date.dayNames.length; i++) {
9907 var d = Date.dayNames[i];
9908 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9911 row.cn[0].cls+=' fc-first';
9912 row.cn[0].cn[0].style = 'min-height:90px';
9913 row.cn[6].cls+=' fc-last';
9917 ret[0].cls += ' fc-first';
9918 ret[4].cls += ' fc-prev-last';
9919 ret[5].cls += ' fc-last';
9926 cls: 'fc-border-separate',
9927 style : 'width:100%',
9935 cls : 'fc-first fc-last',
9954 style : "position: relative;",
9957 cls : 'fc-view fc-view-month fc-grid',
9958 style : 'position: relative',
9959 unselectable : 'on',
9962 cls : 'fc-event-container',
9963 style : 'position:absolute;z-index:8;top:0;left:0;'
9981 initEvents : function()
9984 throw "can not find store for calendar";
9990 style: "text-align:center",
9994 style: "background-color:white;width:50%;margin:250 auto",
9998 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10009 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10011 var size = this.el.select('.fc-content', true).first().getSize();
10012 this.maskEl.setSize(size.width, size.height);
10013 this.maskEl.enableDisplayMode("block");
10014 if(!this.loadMask){
10015 this.maskEl.hide();
10018 this.store = Roo.factory(this.store, Roo.data);
10019 this.store.on('load', this.onLoad, this);
10020 this.store.on('beforeload', this.onBeforeLoad, this);
10024 this.cells = this.el.select('.fc-day',true);
10025 //Roo.log(this.cells);
10026 this.textNodes = this.el.query('.fc-day-number');
10027 this.cells.addClassOnOver('fc-state-hover');
10029 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10030 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10031 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10032 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10034 this.on('monthchange', this.onMonthChange, this);
10036 this.update(new Date().clearTime());
10039 resize : function() {
10040 var sz = this.el.getSize();
10042 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10043 this.el.select('.fc-day-content div',true).setHeight(34);
10048 showPrevMonth : function(e){
10049 this.update(this.activeDate.add("mo", -1));
10051 showToday : function(e){
10052 this.update(new Date().clearTime());
10055 showNextMonth : function(e){
10056 this.update(this.activeDate.add("mo", 1));
10060 showPrevYear : function(){
10061 this.update(this.activeDate.add("y", -1));
10065 showNextYear : function(){
10066 this.update(this.activeDate.add("y", 1));
10071 update : function(date)
10073 var vd = this.activeDate;
10074 this.activeDate = date;
10075 // if(vd && this.el){
10076 // var t = date.getTime();
10077 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10078 // Roo.log('using add remove');
10080 // this.fireEvent('monthchange', this, date);
10082 // this.cells.removeClass("fc-state-highlight");
10083 // this.cells.each(function(c){
10084 // if(c.dateValue == t){
10085 // c.addClass("fc-state-highlight");
10086 // setTimeout(function(){
10087 // try{c.dom.firstChild.focus();}catch(e){}
10097 var days = date.getDaysInMonth();
10099 var firstOfMonth = date.getFirstDateOfMonth();
10100 var startingPos = firstOfMonth.getDay()-this.startDay;
10102 if(startingPos < this.startDay){
10106 var pm = date.add(Date.MONTH, -1);
10107 var prevStart = pm.getDaysInMonth()-startingPos;
10109 this.cells = this.el.select('.fc-day',true);
10110 this.textNodes = this.el.query('.fc-day-number');
10111 this.cells.addClassOnOver('fc-state-hover');
10113 var cells = this.cells.elements;
10114 var textEls = this.textNodes;
10116 Roo.each(cells, function(cell){
10117 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10120 days += startingPos;
10122 // convert everything to numbers so it's fast
10123 var day = 86400000;
10124 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10127 //Roo.log(prevStart);
10129 var today = new Date().clearTime().getTime();
10130 var sel = date.clearTime().getTime();
10131 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10132 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10133 var ddMatch = this.disabledDatesRE;
10134 var ddText = this.disabledDatesText;
10135 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10136 var ddaysText = this.disabledDaysText;
10137 var format = this.format;
10139 var setCellClass = function(cal, cell){
10141 //Roo.log('set Cell Class');
10143 var t = d.getTime();
10147 cell.dateValue = t;
10149 cell.className += " fc-today";
10150 cell.className += " fc-state-highlight";
10151 cell.title = cal.todayText;
10154 // disable highlight in other month..
10155 //cell.className += " fc-state-highlight";
10160 cell.className = " fc-state-disabled";
10161 cell.title = cal.minText;
10165 cell.className = " fc-state-disabled";
10166 cell.title = cal.maxText;
10170 if(ddays.indexOf(d.getDay()) != -1){
10171 cell.title = ddaysText;
10172 cell.className = " fc-state-disabled";
10175 if(ddMatch && format){
10176 var fvalue = d.dateFormat(format);
10177 if(ddMatch.test(fvalue)){
10178 cell.title = ddText.replace("%0", fvalue);
10179 cell.className = " fc-state-disabled";
10183 if (!cell.initialClassName) {
10184 cell.initialClassName = cell.dom.className;
10187 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10192 for(; i < startingPos; i++) {
10193 textEls[i].innerHTML = (++prevStart);
10194 d.setDate(d.getDate()+1);
10196 cells[i].className = "fc-past fc-other-month";
10197 setCellClass(this, cells[i]);
10202 for(; i < days; i++){
10203 intDay = i - startingPos + 1;
10204 textEls[i].innerHTML = (intDay);
10205 d.setDate(d.getDate()+1);
10207 cells[i].className = ''; // "x-date-active";
10208 setCellClass(this, cells[i]);
10212 for(; i < 42; i++) {
10213 textEls[i].innerHTML = (++extraDays);
10214 d.setDate(d.getDate()+1);
10216 cells[i].className = "fc-future fc-other-month";
10217 setCellClass(this, cells[i]);
10220 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10222 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10224 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10225 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10227 if(totalRows != 6){
10228 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10229 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10232 this.fireEvent('monthchange', this, date);
10236 if(!this.internalRender){
10237 var main = this.el.dom.firstChild;
10238 var w = main.offsetWidth;
10239 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10240 Roo.fly(main).setWidth(w);
10241 this.internalRender = true;
10242 // opera does not respect the auto grow header center column
10243 // then, after it gets a width opera refuses to recalculate
10244 // without a second pass
10245 if(Roo.isOpera && !this.secondPass){
10246 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10247 this.secondPass = true;
10248 this.update.defer(10, this, [date]);
10255 findCell : function(dt) {
10256 dt = dt.clearTime().getTime();
10258 this.cells.each(function(c){
10259 //Roo.log("check " +c.dateValue + '?=' + dt);
10260 if(c.dateValue == dt){
10270 findCells : function(ev) {
10271 var s = ev.start.clone().clearTime().getTime();
10273 var e= ev.end.clone().clearTime().getTime();
10276 this.cells.each(function(c){
10277 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10279 if(c.dateValue > e){
10282 if(c.dateValue < s){
10291 findBestRow: function(cells)
10295 for (var i =0 ; i < cells.length;i++) {
10296 ret = Math.max(cells[i].rows || 0,ret);
10303 addItem : function(ev)
10305 // look for vertical location slot in
10306 var cells = this.findCells(ev);
10308 ev.row = this.findBestRow(cells);
10310 // work out the location.
10314 for(var i =0; i < cells.length; i++) {
10322 if (crow.start.getY() == cells[i].getY()) {
10324 crow.end = cells[i];
10340 for (var i = 0; i < cells.length;i++) {
10341 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10345 this.calevents.push(ev);
10348 clearEvents: function() {
10350 if(!this.calevents){
10354 Roo.each(this.cells.elements, function(c){
10358 Roo.each(this.calevents, function(e) {
10359 Roo.each(e.els, function(el) {
10360 el.un('mouseenter' ,this.onEventEnter, this);
10361 el.un('mouseleave' ,this.onEventLeave, this);
10368 renderEvents: function()
10370 // first make sure there is enough space..
10372 this.cells.each(function(c) {
10374 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10377 for (var e = 0; e < this.calevents.length; e++) {
10378 var ev = this.calevents[e];
10379 var cells = ev.cells;
10380 var rows = ev.rows;
10382 for(var i =0; i < rows.length; i++) {
10385 // how many rows should it span..
10388 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10389 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10391 unselectable : "on",
10394 cls: 'fc-event-inner',
10398 // cls: 'fc-event-time',
10399 // html : cells.length > 1 ? '' : ev.time
10403 cls: 'fc-event-title',
10404 html : String.format('{0}', ev.title)
10411 cls: 'ui-resizable-handle ui-resizable-e',
10412 html : '  '
10418 cfg.cls += ' fc-event-start';
10420 if ((i+1) == rows.length) {
10421 cfg.cls += ' fc-event-end';
10424 var ctr = this.el.select('.fc-event-container',true).first();
10425 var cg = ctr.createChild(cfg);
10427 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10428 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10429 cg.on('click', this.onEventClick, this, ev);
10433 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10434 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10436 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10437 cg.setWidth(ebox.right - sbox.x -2);
10445 onEventEnter: function (e, el,event,d) {
10446 this.fireEvent('evententer', this, el, event);
10449 onEventLeave: function (e, el,event,d) {
10450 this.fireEvent('eventleave', this, el, event);
10453 onEventClick: function (e, el,event,d) {
10454 this.fireEvent('eventclick', this, el, event);
10457 onMonthChange: function () {
10461 onLoad: function ()
10463 this.calevents = [];
10466 if(this.store.getCount() > 0){
10467 this.store.data.each(function(d){
10470 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10471 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10472 time : d.data.start_time,
10473 title : d.data.title,
10474 description : d.data.description,
10475 venue : d.data.venue
10480 this.renderEvents();
10483 this.maskEl.hide();
10487 onBeforeLoad: function()
10489 this.clearEvents();
10492 this.maskEl.show();
10506 * @class Roo.bootstrap.Popover
10507 * @extends Roo.bootstrap.Component
10508 * Bootstrap Popover class
10509 * @cfg {String} html contents of the popover (or false to use children..)
10510 * @cfg {String} title of popover (or false to hide)
10511 * @cfg {String} placement how it is placed
10512 * @cfg {String} trigger click || hover (or false to trigger manually)
10513 * @cfg {String} over what (parent or false to trigger manually.)
10516 * Create a new Popover
10517 * @param {Object} config The config object
10520 Roo.bootstrap.Popover = function(config){
10521 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10524 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10526 title: 'Fill in a title',
10529 placement : 'right',
10530 trigger : 'hover', // hover
10534 can_build_overlaid : false,
10536 getChildContainer : function()
10538 return this.el.select('.popover-content',true).first();
10541 getAutoCreate : function(){
10542 Roo.log('make popover?');
10544 cls : 'popover roo-dynamic',
10545 style: 'display:block',
10551 cls : 'popover-inner',
10555 cls: 'popover-title',
10559 cls : 'popover-content',
10570 setTitle: function(str)
10572 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10574 setContent: function(str)
10576 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10578 // as it get's added to the bottom of the page.
10579 onRender : function(ct, position)
10581 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10583 var cfg = Roo.apply({}, this.getAutoCreate());
10587 cfg.cls += ' ' + this.cls;
10590 cfg.style = this.style;
10592 Roo.log("adding to ")
10593 this.el = Roo.get(document.body).createChild(cfg, position);
10599 initEvents : function()
10601 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10602 this.el.enableDisplayMode('block');
10604 if (this.over === false) {
10607 if (this.triggers === false) {
10610 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10611 var triggers = this.trigger ? this.trigger.split(' ') : [];
10612 Roo.each(triggers, function(trigger) {
10614 if (trigger == 'click') {
10615 on_el.on('click', this.toggle, this);
10616 } else if (trigger != 'manual') {
10617 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10618 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10620 on_el.on(eventIn ,this.enter, this);
10621 on_el.on(eventOut, this.leave, this);
10632 toggle : function () {
10633 this.hoverState == 'in' ? this.leave() : this.enter();
10636 enter : function () {
10639 clearTimeout(this.timeout);
10641 this.hoverState = 'in'
10643 if (!this.delay || !this.delay.show) {
10648 this.timeout = setTimeout(function () {
10649 if (_t.hoverState == 'in') {
10652 }, this.delay.show)
10654 leave : function() {
10655 clearTimeout(this.timeout);
10657 this.hoverState = 'out'
10659 if (!this.delay || !this.delay.hide) {
10664 this.timeout = setTimeout(function () {
10665 if (_t.hoverState == 'out') {
10668 }, this.delay.hide)
10671 show : function (on_el)
10674 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10677 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10678 if (this.html !== false) {
10679 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10681 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10682 if (!this.title.length) {
10683 this.el.select('.popover-title',true).hide();
10686 var placement = typeof this.placement == 'function' ?
10687 this.placement.call(this, this.el, on_el) :
10690 var autoToken = /\s?auto?\s?/i;
10691 var autoPlace = autoToken.test(placement);
10693 placement = placement.replace(autoToken, '') || 'top';
10697 //this.el.setXY([0,0]);
10699 this.el.dom.style.display='block';
10700 this.el.addClass(placement);
10702 //this.el.appendTo(on_el);
10704 var p = this.getPosition();
10705 var box = this.el.getBox();
10710 var align = Roo.bootstrap.Popover.alignment[placement]
10711 this.el.alignTo(on_el, align[0],align[1]);
10712 //var arrow = this.el.select('.arrow',true).first();
10713 //arrow.set(align[2],
10715 this.el.addClass('in');
10716 this.hoverState = null;
10718 if (this.el.hasClass('fade')) {
10725 this.el.setXY([0,0]);
10726 this.el.removeClass('in');
10733 Roo.bootstrap.Popover.alignment = {
10734 'left' : ['r-l', [-10,0], 'right'],
10735 'right' : ['l-r', [10,0], 'left'],
10736 'bottom' : ['t-b', [0,10], 'top'],
10737 'top' : [ 'b-t', [0,-10], 'bottom']
10748 * @class Roo.bootstrap.Progress
10749 * @extends Roo.bootstrap.Component
10750 * Bootstrap Progress class
10751 * @cfg {Boolean} striped striped of the progress bar
10752 * @cfg {Boolean} active animated of the progress bar
10756 * Create a new Progress
10757 * @param {Object} config The config object
10760 Roo.bootstrap.Progress = function(config){
10761 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10764 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10769 getAutoCreate : function(){
10777 cfg.cls += ' progress-striped';
10781 cfg.cls += ' active';
10800 * @class Roo.bootstrap.ProgressBar
10801 * @extends Roo.bootstrap.Component
10802 * Bootstrap ProgressBar class
10803 * @cfg {Number} aria_valuenow aria-value now
10804 * @cfg {Number} aria_valuemin aria-value min
10805 * @cfg {Number} aria_valuemax aria-value max
10806 * @cfg {String} label label for the progress bar
10807 * @cfg {String} panel (success | info | warning | danger )
10808 * @cfg {String} role role of the progress bar
10809 * @cfg {String} sr_only text
10813 * Create a new ProgressBar
10814 * @param {Object} config The config object
10817 Roo.bootstrap.ProgressBar = function(config){
10818 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10821 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10825 aria_valuemax : 100,
10831 getAutoCreate : function()
10836 cls: 'progress-bar',
10837 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10849 cfg.role = this.role;
10852 if(this.aria_valuenow){
10853 cfg['aria-valuenow'] = this.aria_valuenow;
10856 if(this.aria_valuemin){
10857 cfg['aria-valuemin'] = this.aria_valuemin;
10860 if(this.aria_valuemax){
10861 cfg['aria-valuemax'] = this.aria_valuemax;
10864 if(this.label && !this.sr_only){
10865 cfg.html = this.label;
10869 cfg.cls += ' progress-bar-' + this.panel;
10875 update : function(aria_valuenow)
10877 this.aria_valuenow = aria_valuenow;
10879 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10894 * @class Roo.bootstrap.TabPanel
10895 * @extends Roo.bootstrap.Component
10896 * Bootstrap TabPanel class
10897 * @cfg {Boolean} active panel active
10898 * @cfg {String} html panel content
10899 * @cfg {String} tabId tab relate id
10903 * Create a new TabPanel
10904 * @param {Object} config The config object
10907 Roo.bootstrap.TabPanel = function(config){
10908 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10911 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10917 getAutoCreate : function(){
10921 html: this.html || ''
10925 cfg.cls += ' active';
10929 cfg.tabId = this.tabId;
10947 * @class Roo.bootstrap.DateField
10948 * @extends Roo.bootstrap.Input
10949 * Bootstrap DateField class
10950 * @cfg {Number} weekStart default 0
10951 * @cfg {Number} weekStart default 0
10952 * @cfg {Number} viewMode default empty, (months|years)
10953 * @cfg {Number} minViewMode default empty, (months|years)
10954 * @cfg {Number} startDate default -Infinity
10955 * @cfg {Number} endDate default Infinity
10956 * @cfg {Boolean} todayHighlight default false
10957 * @cfg {Boolean} todayBtn default false
10958 * @cfg {Boolean} calendarWeeks default false
10959 * @cfg {Object} daysOfWeekDisabled default empty
10961 * @cfg {Boolean} keyboardNavigation default true
10962 * @cfg {String} language default en
10965 * Create a new DateField
10966 * @param {Object} config The config object
10969 Roo.bootstrap.DateField = function(config){
10970 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10974 * Fires when this field show.
10975 * @param {Roo.bootstrap.DateField} this
10976 * @param {Mixed} date The date value
10981 * Fires when this field hide.
10982 * @param {Roo.bootstrap.DateField} this
10983 * @param {Mixed} date The date value
10988 * Fires when select a date.
10989 * @param {Roo.bootstrap.DateField} this
10990 * @param {Mixed} date The date value
10996 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
10999 * @cfg {String} format
11000 * The default date format string which can be overriden for localization support. The format must be
11001 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11005 * @cfg {String} altFormats
11006 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11007 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11009 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11017 todayHighlight : false,
11023 keyboardNavigation: true,
11025 calendarWeeks: false,
11027 startDate: -Infinity,
11031 daysOfWeekDisabled: [],
11035 UTCDate: function()
11037 return new Date(Date.UTC.apply(Date, arguments));
11040 UTCToday: function()
11042 var today = new Date();
11043 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11046 getDate: function() {
11047 var d = this.getUTCDate();
11048 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11051 getUTCDate: function() {
11055 setDate: function(d) {
11056 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11059 setUTCDate: function(d) {
11061 this.setValue(this.formatDate(this.date));
11064 onRender: function(ct, position)
11067 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11069 this.language = this.language || 'en';
11070 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11071 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11073 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11074 this.format = this.format || 'm/d/y';
11075 this.isInline = false;
11076 this.isInput = true;
11077 this.component = this.el.select('.add-on', true).first() || false;
11078 this.component = (this.component && this.component.length === 0) ? false : this.component;
11079 this.hasInput = this.component && this.inputEL().length;
11081 if (typeof(this.minViewMode === 'string')) {
11082 switch (this.minViewMode) {
11084 this.minViewMode = 1;
11087 this.minViewMode = 2;
11090 this.minViewMode = 0;
11095 if (typeof(this.viewMode === 'string')) {
11096 switch (this.viewMode) {
11109 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11111 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11113 this.picker().on('mousedown', this.onMousedown, this);
11114 this.picker().on('click', this.onClick, this);
11116 this.picker().addClass('datepicker-dropdown');
11118 this.startViewMode = this.viewMode;
11121 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11122 if(!this.calendarWeeks){
11127 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11128 v.attr('colspan', function(i, val){
11129 return parseInt(val) + 1;
11134 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11136 this.setStartDate(this.startDate);
11137 this.setEndDate(this.endDate);
11139 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11146 if(this.isInline) {
11151 picker : function()
11153 return this.el.select('.datepicker', true).first();
11156 fillDow: function()
11158 var dowCnt = this.weekStart;
11167 if(this.calendarWeeks){
11175 while (dowCnt < this.weekStart + 7) {
11179 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11183 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11186 fillMonths: function()
11189 var months = this.picker().select('>.datepicker-months td', true).first();
11191 months.dom.innerHTML = '';
11197 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11200 months.createChild(month);
11205 update: function(){
11207 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11209 if (this.date < this.startDate) {
11210 this.viewDate = new Date(this.startDate);
11211 } else if (this.date > this.endDate) {
11212 this.viewDate = new Date(this.endDate);
11214 this.viewDate = new Date(this.date);
11221 var d = new Date(this.viewDate),
11222 year = d.getUTCFullYear(),
11223 month = d.getUTCMonth(),
11224 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11225 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11226 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11227 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11228 currentDate = this.date && this.date.valueOf(),
11229 today = this.UTCToday();
11231 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11233 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11235 // this.picker.select('>tfoot th.today').
11236 // .text(dates[this.language].today)
11237 // .toggle(this.todayBtn !== false);
11239 this.updateNavArrows();
11242 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11244 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11246 prevMonth.setUTCDate(day);
11248 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11250 var nextMonth = new Date(prevMonth);
11252 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11254 nextMonth = nextMonth.valueOf();
11256 var fillMonths = false;
11258 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11260 while(prevMonth.valueOf() < nextMonth) {
11263 if (prevMonth.getUTCDay() === this.weekStart) {
11265 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11273 if(this.calendarWeeks){
11274 // ISO 8601: First week contains first thursday.
11275 // ISO also states week starts on Monday, but we can be more abstract here.
11277 // Start of current week: based on weekstart/current date
11278 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11279 // Thursday of this week
11280 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11281 // First Thursday of year, year from thursday
11282 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11283 // Calendar week: ms between thursdays, div ms per day, div 7 days
11284 calWeek = (th - yth) / 864e5 / 7 + 1;
11286 fillMonths.cn.push({
11294 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11296 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11299 if (this.todayHighlight &&
11300 prevMonth.getUTCFullYear() == today.getFullYear() &&
11301 prevMonth.getUTCMonth() == today.getMonth() &&
11302 prevMonth.getUTCDate() == today.getDate()) {
11303 clsName += ' today';
11306 if (currentDate && prevMonth.valueOf() === currentDate) {
11307 clsName += ' active';
11310 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11311 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11312 clsName += ' disabled';
11315 fillMonths.cn.push({
11317 cls: 'day ' + clsName,
11318 html: prevMonth.getDate()
11321 prevMonth.setDate(prevMonth.getDate()+1);
11324 var currentYear = this.date && this.date.getUTCFullYear();
11325 var currentMonth = this.date && this.date.getUTCMonth();
11327 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11329 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11330 v.removeClass('active');
11332 if(currentYear === year && k === currentMonth){
11333 v.addClass('active');
11336 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11337 v.addClass('disabled');
11343 year = parseInt(year/10, 10) * 10;
11345 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11347 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11350 for (var i = -1; i < 11; i++) {
11351 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11353 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11361 showMode: function(dir) {
11363 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11365 Roo.each(this.picker().select('>div',true).elements, function(v){
11366 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11369 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11374 if(this.isInline) return;
11376 this.picker().removeClass(['bottom', 'top']);
11378 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11380 * place to the top of element!
11384 this.picker().addClass('top');
11385 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11390 this.picker().addClass('bottom');
11392 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11395 parseDate : function(value){
11396 if(!value || value instanceof Date){
11399 var v = Date.parseDate(value, this.format);
11400 if (!v && this.useIso) {
11401 v = Date.parseDate(value, 'Y-m-d');
11403 if(!v && this.altFormats){
11404 if(!this.altFormatsArray){
11405 this.altFormatsArray = this.altFormats.split("|");
11407 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11408 v = Date.parseDate(value, this.altFormatsArray[i]);
11414 formatDate : function(date, fmt){
11415 return (!date || !(date instanceof Date)) ?
11416 date : date.dateFormat(fmt || this.format);
11419 onFocus : function()
11421 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11425 onBlur : function()
11427 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11433 this.picker().show();
11437 this.fireEvent('show', this, this.date);
11442 if(this.isInline) return;
11443 this.picker().hide();
11444 this.viewMode = this.startViewMode;
11447 this.fireEvent('hide', this, this.date);
11451 onMousedown: function(e){
11452 e.stopPropagation();
11453 e.preventDefault();
11456 keyup: function(e){
11457 Roo.bootstrap.DateField.superclass.keyup.call(this);
11462 setValue: function(v){
11463 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11465 this.fireEvent('select', this, this.date);
11469 fireKey: function(e){
11470 if (!this.picker().isVisible()){
11471 if (e.keyCode == 27) // allow escape to hide and re-show picker
11475 var dateChanged = false,
11477 newDate, newViewDate;
11481 e.preventDefault();
11485 if (!this.keyboardNavigation) break;
11486 dir = e.keyCode == 37 ? -1 : 1;
11489 newDate = this.moveYear(this.date, dir);
11490 newViewDate = this.moveYear(this.viewDate, dir);
11491 } else if (e.shiftKey){
11492 newDate = this.moveMonth(this.date, dir);
11493 newViewDate = this.moveMonth(this.viewDate, dir);
11495 newDate = new Date(this.date);
11496 newDate.setUTCDate(this.date.getUTCDate() + dir);
11497 newViewDate = new Date(this.viewDate);
11498 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11500 if (this.dateWithinRange(newDate)){
11501 this.date = newDate;
11502 this.viewDate = newViewDate;
11503 this.setValue(this.formatDate(this.date));
11505 e.preventDefault();
11506 dateChanged = true;
11511 if (!this.keyboardNavigation) break;
11512 dir = e.keyCode == 38 ? -1 : 1;
11514 newDate = this.moveYear(this.date, dir);
11515 newViewDate = this.moveYear(this.viewDate, dir);
11516 } else if (e.shiftKey){
11517 newDate = this.moveMonth(this.date, dir);
11518 newViewDate = this.moveMonth(this.viewDate, dir);
11520 newDate = new Date(this.date);
11521 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11522 newViewDate = new Date(this.viewDate);
11523 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11525 if (this.dateWithinRange(newDate)){
11526 this.date = newDate;
11527 this.viewDate = newViewDate;
11528 this.setValue(this.formatDate(this.date));
11530 e.preventDefault();
11531 dateChanged = true;
11535 this.setValue(this.formatDate(this.date));
11537 e.preventDefault();
11540 this.setValue(this.formatDate(this.date));
11547 onClick: function(e) {
11548 e.stopPropagation();
11549 e.preventDefault();
11551 var target = e.getTarget();
11553 if(target.nodeName.toLowerCase() === 'i'){
11554 target = Roo.get(target).dom.parentNode;
11557 var nodeName = target.nodeName;
11558 var className = target.className;
11559 var html = target.innerHTML;
11561 switch(nodeName.toLowerCase()) {
11563 switch(className) {
11569 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11570 switch(this.viewMode){
11572 this.viewDate = this.moveMonth(this.viewDate, dir);
11576 this.viewDate = this.moveYear(this.viewDate, dir);
11582 var date = new Date();
11583 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11585 this.setValue(this.formatDate(this.date));
11591 if (className.indexOf('disabled') === -1) {
11592 this.viewDate.setUTCDate(1);
11593 if (className.indexOf('month') !== -1) {
11594 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11596 var year = parseInt(html, 10) || 0;
11597 this.viewDate.setUTCFullYear(year);
11606 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11607 var day = parseInt(html, 10) || 1;
11608 var year = this.viewDate.getUTCFullYear(),
11609 month = this.viewDate.getUTCMonth();
11611 if (className.indexOf('old') !== -1) {
11618 } else if (className.indexOf('new') !== -1) {
11626 this.date = this.UTCDate(year, month, day,0,0,0,0);
11627 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11629 this.setValue(this.formatDate(this.date));
11636 setStartDate: function(startDate){
11637 this.startDate = startDate || -Infinity;
11638 if (this.startDate !== -Infinity) {
11639 this.startDate = this.parseDate(this.startDate);
11642 this.updateNavArrows();
11645 setEndDate: function(endDate){
11646 this.endDate = endDate || Infinity;
11647 if (this.endDate !== Infinity) {
11648 this.endDate = this.parseDate(this.endDate);
11651 this.updateNavArrows();
11654 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11655 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11656 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11657 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11659 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11660 return parseInt(d, 10);
11663 this.updateNavArrows();
11666 updateNavArrows: function() {
11667 var d = new Date(this.viewDate),
11668 year = d.getUTCFullYear(),
11669 month = d.getUTCMonth();
11671 Roo.each(this.picker().select('.prev', true).elements, function(v){
11673 switch (this.viewMode) {
11676 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11682 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11689 Roo.each(this.picker().select('.next', true).elements, function(v){
11691 switch (this.viewMode) {
11694 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11700 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11708 moveMonth: function(date, dir){
11709 if (!dir) return date;
11710 var new_date = new Date(date.valueOf()),
11711 day = new_date.getUTCDate(),
11712 month = new_date.getUTCMonth(),
11713 mag = Math.abs(dir),
11715 dir = dir > 0 ? 1 : -1;
11718 // If going back one month, make sure month is not current month
11719 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11721 return new_date.getUTCMonth() == month;
11723 // If going forward one month, make sure month is as expected
11724 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11726 return new_date.getUTCMonth() != new_month;
11728 new_month = month + dir;
11729 new_date.setUTCMonth(new_month);
11730 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11731 if (new_month < 0 || new_month > 11)
11732 new_month = (new_month + 12) % 12;
11734 // For magnitudes >1, move one month at a time...
11735 for (var i=0; i<mag; i++)
11736 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11737 new_date = this.moveMonth(new_date, dir);
11738 // ...then reset the day, keeping it in the new month
11739 new_month = new_date.getUTCMonth();
11740 new_date.setUTCDate(day);
11742 return new_month != new_date.getUTCMonth();
11745 // Common date-resetting loop -- if date is beyond end of month, make it
11748 new_date.setUTCDate(--day);
11749 new_date.setUTCMonth(new_month);
11754 moveYear: function(date, dir){
11755 return this.moveMonth(date, dir*12);
11758 dateWithinRange: function(date){
11759 return date >= this.startDate && date <= this.endDate;
11763 remove: function() {
11764 this.picker().remove();
11769 Roo.apply(Roo.bootstrap.DateField, {
11780 html: '<i class="icon-arrow-left"/>'
11790 html: '<i class="icon-arrow-right"/>'
11832 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11833 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11834 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11835 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11836 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11849 navFnc: 'FullYear',
11854 navFnc: 'FullYear',
11859 Roo.apply(Roo.bootstrap.DateField, {
11863 cls: 'datepicker dropdown-menu',
11867 cls: 'datepicker-days',
11871 cls: 'table-condensed',
11873 Roo.bootstrap.DateField.head,
11877 Roo.bootstrap.DateField.footer
11884 cls: 'datepicker-months',
11888 cls: 'table-condensed',
11890 Roo.bootstrap.DateField.head,
11891 Roo.bootstrap.DateField.content,
11892 Roo.bootstrap.DateField.footer
11899 cls: 'datepicker-years',
11903 cls: 'table-condensed',
11905 Roo.bootstrap.DateField.head,
11906 Roo.bootstrap.DateField.content,
11907 Roo.bootstrap.DateField.footer
11926 * @class Roo.bootstrap.TimeField
11927 * @extends Roo.bootstrap.Input
11928 * Bootstrap DateField class
11932 * Create a new TimeField
11933 * @param {Object} config The config object
11936 Roo.bootstrap.TimeField = function(config){
11937 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11941 * Fires when this field show.
11942 * @param {Roo.bootstrap.DateField} this
11943 * @param {Mixed} date The date value
11948 * Fires when this field hide.
11949 * @param {Roo.bootstrap.DateField} this
11950 * @param {Mixed} date The date value
11955 * Fires when select a date.
11956 * @param {Roo.bootstrap.DateField} this
11957 * @param {Mixed} date The date value
11963 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
11966 * @cfg {String} format
11967 * The default time format string which can be overriden for localization support. The format must be
11968 * valid according to {@link Date#parseDate} (defaults to 'H:i').
11972 onRender: function(ct, position)
11975 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11977 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11979 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11981 this.pop = this.picker().select('>.datepicker-time',true).first();
11982 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
11984 this.picker().on('mousedown', this.onMousedown, this);
11985 this.picker().on('click', this.onClick, this);
11987 this.picker().addClass('datepicker-dropdown');
11992 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11993 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11994 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11995 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11996 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11997 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12001 fireKey: function(e){
12002 if (!this.picker().isVisible()){
12003 if (e.keyCode == 27) // allow escape to hide and re-show picker
12008 e.preventDefault();
12016 this.onTogglePeriod();
12019 this.onIncrementMinutes();
12022 this.onDecrementMinutes();
12031 onClick: function(e) {
12032 e.stopPropagation();
12033 e.preventDefault();
12036 picker : function()
12038 return this.el.select('.datepicker', true).first();
12041 fillTime: function()
12043 var time = this.pop.select('tbody', true).first();
12045 time.dom.innerHTML = '';
12060 cls: 'hours-up glyphicon glyphicon-chevron-up'
12080 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12101 cls: 'timepicker-hour',
12116 cls: 'timepicker-minute',
12131 cls: 'btn btn-primary period',
12153 cls: 'hours-down glyphicon glyphicon-chevron-down'
12173 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12191 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12198 var hours = this.time.getHours();
12199 var minutes = this.time.getMinutes();
12212 hours = hours - 12;
12216 hours = '0' + hours;
12220 minutes = '0' + minutes;
12223 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12224 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12225 this.pop.select('button', true).first().dom.innerHTML = period;
12231 this.picker().removeClass(['bottom', 'top']);
12233 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12235 * place to the top of element!
12239 this.picker().addClass('top');
12240 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12245 this.picker().addClass('bottom');
12247 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12250 onFocus : function()
12252 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12256 onBlur : function()
12258 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12264 this.picker().show();
12269 this.fireEvent('show', this, this.date);
12274 this.picker().hide();
12277 this.fireEvent('hide', this, this.date);
12280 setTime : function()
12283 this.setValue(this.time.format(this.format));
12285 this.fireEvent('select', this, this.date);
12290 onMousedown: function(e){
12291 e.stopPropagation();
12292 e.preventDefault();
12295 onIncrementHours: function()
12297 Roo.log('onIncrementHours');
12298 this.time = this.time.add(Date.HOUR, 1);
12303 onDecrementHours: function()
12305 Roo.log('onDecrementHours');
12306 this.time = this.time.add(Date.HOUR, -1);
12310 onIncrementMinutes: function()
12312 Roo.log('onIncrementMinutes');
12313 this.time = this.time.add(Date.MINUTE, 1);
12317 onDecrementMinutes: function()
12319 Roo.log('onDecrementMinutes');
12320 this.time = this.time.add(Date.MINUTE, -1);
12324 onTogglePeriod: function()
12326 Roo.log('onTogglePeriod');
12327 this.time = this.time.add(Date.HOUR, 12);
12334 Roo.apply(Roo.bootstrap.TimeField, {
12364 cls: 'btn btn-info ok',
12376 Roo.apply(Roo.bootstrap.TimeField, {
12380 cls: 'datepicker dropdown-menu',
12384 cls: 'datepicker-time',
12388 cls: 'table-condensed',
12390 Roo.bootstrap.TimeField.content,
12391 Roo.bootstrap.TimeField.footer
12410 * @class Roo.bootstrap.CheckBox
12411 * @extends Roo.bootstrap.Input
12412 * Bootstrap CheckBox class
12414 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12415 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12416 * @cfg {String} boxLabel The text that appears beside the checkbox
12417 * @cfg {Boolean} checked initnal the element
12420 * Create a new CheckBox
12421 * @param {Object} config The config object
12424 Roo.bootstrap.CheckBox = function(config){
12425 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12430 * Fires when the element is checked or unchecked.
12431 * @param {Roo.bootstrap.CheckBox} this This input
12432 * @param {Boolean} checked The new checked value
12438 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12440 inputType: 'checkbox',
12446 getAutoCreate : function()
12448 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12454 cfg.cls = 'form-group' //input-group
12459 type : this.inputType,
12460 value : (!this.checked) ? this.valueOff : this.inputValue,
12462 placeholder : this.placeholder || ''
12466 if (this.disabled) {
12467 input.disabled=true;
12471 input.checked = this.checked;
12475 input.name = this.name;
12479 input.cls += ' input-' + this.size;
12483 ['xs','sm','md','lg'].map(function(size){
12484 if (settings[size]) {
12485 cfg.cls += ' col-' + size + '-' + settings[size];
12489 var inputblock = input;
12491 if (this.before || this.after) {
12494 cls : 'input-group',
12498 inputblock.cn.push({
12500 cls : 'input-group-addon',
12504 inputblock.cn.push(input);
12506 inputblock.cn.push({
12508 cls : 'input-group-addon',
12515 if (align ==='left' && this.fieldLabel.length) {
12516 Roo.log("left and has label");
12522 cls : 'control-label col-md-' + this.labelWidth,
12523 html : this.fieldLabel
12527 cls : "col-md-" + (12 - this.labelWidth),
12534 } else if ( this.fieldLabel.length) {
12539 tag: this.boxLabel ? 'span' : 'label',
12541 cls: 'control-label box-input-label',
12542 //cls : 'input-group-addon',
12543 html : this.fieldLabel
12553 Roo.log(" no label && no align");
12568 html: this.boxLabel
12577 * return the real input element.
12579 inputEl: function ()
12581 return this.el.select('input.form-box',true).first();
12584 initEvents : function()
12586 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12588 this.inputEl().on('click', this.onClick, this);
12592 onClick : function()
12594 this.setChecked(!this.checked);
12597 setChecked : function(state,suppressEvent)
12599 this.checked = state;
12601 this.inputEl().dom.checked = state;
12603 if(suppressEvent !== true){
12604 this.fireEvent('check', this, state);
12607 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12611 setValue : function(v,suppressEvent)
12613 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12627 * @class Roo.bootstrap.Radio
12628 * @extends Roo.bootstrap.CheckBox
12629 * Bootstrap Radio class
12632 * Create a new Radio
12633 * @param {Object} config The config object
12636 Roo.bootstrap.Radio = function(config){
12637 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12641 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12643 inputType: 'radio',
12647 getAutoCreate : function()
12649 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12655 cfg.cls = 'form-group' //input-group
12660 type : this.inputType,
12661 value : (!this.checked) ? this.valueOff : this.inputValue,
12663 placeholder : this.placeholder || ''
12667 if (this.disabled) {
12668 input.disabled=true;
12672 input.checked = this.checked;
12676 input.name = this.name;
12680 input.cls += ' input-' + this.size;
12684 ['xs','sm','md','lg'].map(function(size){
12685 if (settings[size]) {
12686 cfg.cls += ' col-' + size + '-' + settings[size];
12690 var inputblock = input;
12692 if (this.before || this.after) {
12695 cls : 'input-group',
12699 inputblock.cn.push({
12701 cls : 'input-group-addon',
12705 inputblock.cn.push(input);
12707 inputblock.cn.push({
12709 cls : 'input-group-addon',
12716 if (align ==='left' && this.fieldLabel.length) {
12717 Roo.log("left and has label");
12723 cls : 'control-label col-md-' + this.labelWidth,
12724 html : this.fieldLabel
12728 cls : "col-md-" + (12 - this.labelWidth),
12735 } else if ( this.fieldLabel.length) {
12742 cls: 'control-label box-input-label',
12743 //cls : 'input-group-addon',
12744 html : this.fieldLabel
12754 Roo.log(" no label && no align");
12769 html: this.boxLabel
12777 onClick : function()
12779 this.setChecked(true);
12782 setChecked : function(state,suppressEvent)
12785 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12786 v.dom.checked = false;
12790 this.checked = state;
12791 this.inputEl().dom.checked = state;
12793 if(suppressEvent !== true){
12794 this.fireEvent('check', this, state);
12797 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12801 getGroupValue : function()
12804 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12805 if(v.dom.checked == true){
12806 value = v.dom.value;
12814 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12815 * @return {Mixed} value The field value
12817 getValue : function(){
12818 return this.getGroupValue();
12824 //<script type="text/javascript">
12827 * Based Ext JS Library 1.1.1
12828 * Copyright(c) 2006-2007, Ext JS, LLC.
12834 * @class Roo.HtmlEditorCore
12835 * @extends Roo.Component
12836 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12838 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12841 Roo.HtmlEditorCore = function(config){
12844 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12847 * @event initialize
12848 * Fires when the editor is fully initialized (including the iframe)
12849 * @param {Roo.HtmlEditorCore} this
12854 * Fires when the editor is first receives the focus. Any insertion must wait
12855 * until after this event.
12856 * @param {Roo.HtmlEditorCore} this
12860 * @event beforesync
12861 * Fires before the textarea is updated with content from the editor iframe. Return false
12862 * to cancel the sync.
12863 * @param {Roo.HtmlEditorCore} this
12864 * @param {String} html
12868 * @event beforepush
12869 * Fires before the iframe editor is updated with content from the textarea. Return false
12870 * to cancel the push.
12871 * @param {Roo.HtmlEditorCore} this
12872 * @param {String} html
12877 * Fires when the textarea is updated with content from the editor iframe.
12878 * @param {Roo.HtmlEditorCore} this
12879 * @param {String} html
12884 * Fires when the iframe editor is updated with content from the textarea.
12885 * @param {Roo.HtmlEditorCore} this
12886 * @param {String} html
12891 * @event editorevent
12892 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12893 * @param {Roo.HtmlEditorCore} this
12901 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12905 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12911 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12916 * @cfg {Number} height (in pixels)
12920 * @cfg {Number} width (in pixels)
12925 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12928 stylesheets: false,
12933 // private properties
12934 validationEvent : false,
12936 initialized : false,
12938 sourceEditMode : false,
12939 onFocus : Roo.emptyFn,
12941 hideMode:'offsets',
12949 * Protected method that will not generally be called directly. It
12950 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12951 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12953 getDocMarkup : function(){
12956 Roo.log(this.stylesheets);
12958 // inherit styels from page...??
12959 if (this.stylesheets === false) {
12961 Roo.get(document.head).select('style').each(function(node) {
12962 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12965 Roo.get(document.head).select('link').each(function(node) {
12966 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12969 } else if (!this.stylesheets.length) {
12971 st = '<style type="text/css">' +
12972 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12975 Roo.each(this.stylesheets, function(s) {
12976 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12981 st += '<style type="text/css">' +
12982 'IMG { cursor: pointer } ' +
12986 return '<html><head>' + st +
12987 //<style type="text/css">' +
12988 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12990 ' </head><body class="roo-htmleditor-body"></body></html>';
12994 onRender : function(ct, position)
12997 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12998 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13001 this.el.dom.style.border = '0 none';
13002 this.el.dom.setAttribute('tabIndex', -1);
13003 this.el.addClass('x-hidden hide');
13007 if(Roo.isIE){ // fix IE 1px bogus margin
13008 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13012 this.frameId = Roo.id();
13016 var iframe = this.owner.wrap.createChild({
13018 cls: 'form-control', // bootstrap..
13020 name: this.frameId,
13021 frameBorder : 'no',
13022 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13027 this.iframe = iframe.dom;
13029 this.assignDocWin();
13031 this.doc.designMode = 'on';
13034 this.doc.write(this.getDocMarkup());
13038 var task = { // must defer to wait for browser to be ready
13040 //console.log("run task?" + this.doc.readyState);
13041 this.assignDocWin();
13042 if(this.doc.body || this.doc.readyState == 'complete'){
13044 this.doc.designMode="on";
13048 Roo.TaskMgr.stop(task);
13049 this.initEditor.defer(10, this);
13056 Roo.TaskMgr.start(task);
13063 onResize : function(w, h)
13065 Roo.log('resize: ' +w + ',' + h );
13066 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13070 if(typeof w == 'number'){
13072 this.iframe.style.width = w + 'px';
13074 if(typeof h == 'number'){
13076 this.iframe.style.height = h + 'px';
13078 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13085 * Toggles the editor between standard and source edit mode.
13086 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13088 toggleSourceEdit : function(sourceEditMode){
13090 this.sourceEditMode = sourceEditMode === true;
13092 if(this.sourceEditMode){
13094 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13097 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13098 //this.iframe.className = '';
13101 //this.setSize(this.owner.wrap.getSize());
13102 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13109 * Protected method that will not generally be called directly. If you need/want
13110 * custom HTML cleanup, this is the method you should override.
13111 * @param {String} html The HTML to be cleaned
13112 * return {String} The cleaned HTML
13114 cleanHtml : function(html){
13115 html = String(html);
13116 if(html.length > 5){
13117 if(Roo.isSafari){ // strip safari nonsense
13118 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13121 if(html == ' '){
13128 * HTML Editor -> Textarea
13129 * Protected method that will not generally be called directly. Syncs the contents
13130 * of the editor iframe with the textarea.
13132 syncValue : function(){
13133 if(this.initialized){
13134 var bd = (this.doc.body || this.doc.documentElement);
13135 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13136 var html = bd.innerHTML;
13138 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13139 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13141 html = '<div style="'+m[0]+'">' + html + '</div>';
13144 html = this.cleanHtml(html);
13145 // fix up the special chars.. normaly like back quotes in word...
13146 // however we do not want to do this with chinese..
13147 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13148 var cc = b.charCodeAt();
13150 (cc >= 0x4E00 && cc < 0xA000 ) ||
13151 (cc >= 0x3400 && cc < 0x4E00 ) ||
13152 (cc >= 0xf900 && cc < 0xfb00 )
13158 if(this.owner.fireEvent('beforesync', this, html) !== false){
13159 this.el.dom.value = html;
13160 this.owner.fireEvent('sync', this, html);
13166 * Protected method that will not generally be called directly. Pushes the value of the textarea
13167 * into the iframe editor.
13169 pushValue : function(){
13170 if(this.initialized){
13171 var v = this.el.dom.value.trim();
13173 // if(v.length < 1){
13177 if(this.owner.fireEvent('beforepush', this, v) !== false){
13178 var d = (this.doc.body || this.doc.documentElement);
13180 this.cleanUpPaste();
13181 this.el.dom.value = d.innerHTML;
13182 this.owner.fireEvent('push', this, v);
13188 deferFocus : function(){
13189 this.focus.defer(10, this);
13193 focus : function(){
13194 if(this.win && !this.sourceEditMode){
13201 assignDocWin: function()
13203 var iframe = this.iframe;
13206 this.doc = iframe.contentWindow.document;
13207 this.win = iframe.contentWindow;
13209 if (!Roo.get(this.frameId)) {
13212 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13213 this.win = Roo.get(this.frameId).dom.contentWindow;
13218 initEditor : function(){
13219 //console.log("INIT EDITOR");
13220 this.assignDocWin();
13224 this.doc.designMode="on";
13226 this.doc.write(this.getDocMarkup());
13229 var dbody = (this.doc.body || this.doc.documentElement);
13230 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13231 // this copies styles from the containing element into thsi one..
13232 // not sure why we need all of this..
13233 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13234 ss['background-attachment'] = 'fixed'; // w3c
13235 dbody.bgProperties = 'fixed'; // ie
13236 Roo.DomHelper.applyStyles(dbody, ss);
13237 Roo.EventManager.on(this.doc, {
13238 //'mousedown': this.onEditorEvent,
13239 'mouseup': this.onEditorEvent,
13240 'dblclick': this.onEditorEvent,
13241 'click': this.onEditorEvent,
13242 'keyup': this.onEditorEvent,
13247 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13249 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13250 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13252 this.initialized = true;
13254 this.owner.fireEvent('initialize', this);
13259 onDestroy : function(){
13265 //for (var i =0; i < this.toolbars.length;i++) {
13266 // // fixme - ask toolbars for heights?
13267 // this.toolbars[i].onDestroy();
13270 //this.wrap.dom.innerHTML = '';
13271 //this.wrap.remove();
13276 onFirstFocus : function(){
13278 this.assignDocWin();
13281 this.activated = true;
13284 if(Roo.isGecko){ // prevent silly gecko errors
13286 var s = this.win.getSelection();
13287 if(!s.focusNode || s.focusNode.nodeType != 3){
13288 var r = s.getRangeAt(0);
13289 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13294 this.execCmd('useCSS', true);
13295 this.execCmd('styleWithCSS', false);
13298 this.owner.fireEvent('activate', this);
13302 adjustFont: function(btn){
13303 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13304 //if(Roo.isSafari){ // safari
13307 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13308 if(Roo.isSafari){ // safari
13309 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13310 v = (v < 10) ? 10 : v;
13311 v = (v > 48) ? 48 : v;
13312 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13317 v = Math.max(1, v+adjust);
13319 this.execCmd('FontSize', v );
13322 onEditorEvent : function(e){
13323 this.owner.fireEvent('editorevent', this, e);
13324 // this.updateToolbar();
13325 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13328 insertTag : function(tg)
13330 // could be a bit smarter... -> wrap the current selected tRoo..
13331 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13333 range = this.createRange(this.getSelection());
13334 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13335 wrappingNode.appendChild(range.extractContents());
13336 range.insertNode(wrappingNode);
13343 this.execCmd("formatblock", tg);
13347 insertText : function(txt)
13351 var range = this.createRange();
13352 range.deleteContents();
13353 //alert(Sender.getAttribute('label'));
13355 range.insertNode(this.doc.createTextNode(txt));
13361 * Executes a Midas editor command on the editor document and performs necessary focus and
13362 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13363 * @param {String} cmd The Midas command
13364 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13366 relayCmd : function(cmd, value){
13368 this.execCmd(cmd, value);
13369 this.owner.fireEvent('editorevent', this);
13370 //this.updateToolbar();
13371 this.owner.deferFocus();
13375 * Executes a Midas editor command directly on the editor document.
13376 * For visual commands, you should use {@link #relayCmd} instead.
13377 * <b>This should only be called after the editor is initialized.</b>
13378 * @param {String} cmd The Midas command
13379 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13381 execCmd : function(cmd, value){
13382 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13389 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13391 * @param {String} text | dom node..
13393 insertAtCursor : function(text)
13398 if(!this.activated){
13404 var r = this.doc.selection.createRange();
13415 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13419 // from jquery ui (MIT licenced)
13421 var win = this.win;
13423 if (win.getSelection && win.getSelection().getRangeAt) {
13424 range = win.getSelection().getRangeAt(0);
13425 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13426 range.insertNode(node);
13427 } else if (win.document.selection && win.document.selection.createRange) {
13428 // no firefox support
13429 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13430 win.document.selection.createRange().pasteHTML(txt);
13432 // no firefox support
13433 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13434 this.execCmd('InsertHTML', txt);
13443 mozKeyPress : function(e){
13445 var c = e.getCharCode(), cmd;
13448 c = String.fromCharCode(c).toLowerCase();
13462 this.cleanUpPaste.defer(100, this);
13470 e.preventDefault();
13478 fixKeys : function(){ // load time branching for fastest keydown performance
13480 return function(e){
13481 var k = e.getKey(), r;
13484 r = this.doc.selection.createRange();
13487 r.pasteHTML('    ');
13494 r = this.doc.selection.createRange();
13496 var target = r.parentElement();
13497 if(!target || target.tagName.toLowerCase() != 'li'){
13499 r.pasteHTML('<br />');
13505 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13506 this.cleanUpPaste.defer(100, this);
13512 }else if(Roo.isOpera){
13513 return function(e){
13514 var k = e.getKey();
13518 this.execCmd('InsertHTML','    ');
13521 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13522 this.cleanUpPaste.defer(100, this);
13527 }else if(Roo.isSafari){
13528 return function(e){
13529 var k = e.getKey();
13533 this.execCmd('InsertText','\t');
13537 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13538 this.cleanUpPaste.defer(100, this);
13546 getAllAncestors: function()
13548 var p = this.getSelectedNode();
13551 a.push(p); // push blank onto stack..
13552 p = this.getParentElement();
13556 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13560 a.push(this.doc.body);
13564 lastSelNode : false,
13567 getSelection : function()
13569 this.assignDocWin();
13570 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13573 getSelectedNode: function()
13575 // this may only work on Gecko!!!
13577 // should we cache this!!!!
13582 var range = this.createRange(this.getSelection()).cloneRange();
13585 var parent = range.parentElement();
13587 var testRange = range.duplicate();
13588 testRange.moveToElementText(parent);
13589 if (testRange.inRange(range)) {
13592 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13595 parent = parent.parentElement;
13600 // is ancestor a text element.
13601 var ac = range.commonAncestorContainer;
13602 if (ac.nodeType == 3) {
13603 ac = ac.parentNode;
13606 var ar = ac.childNodes;
13609 var other_nodes = [];
13610 var has_other_nodes = false;
13611 for (var i=0;i<ar.length;i++) {
13612 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13615 // fullly contained node.
13617 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13622 // probably selected..
13623 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13624 other_nodes.push(ar[i]);
13628 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13633 has_other_nodes = true;
13635 if (!nodes.length && other_nodes.length) {
13636 nodes= other_nodes;
13638 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13644 createRange: function(sel)
13646 // this has strange effects when using with
13647 // top toolbar - not sure if it's a great idea.
13648 //this.editor.contentWindow.focus();
13649 if (typeof sel != "undefined") {
13651 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13653 return this.doc.createRange();
13656 return this.doc.createRange();
13659 getParentElement: function()
13662 this.assignDocWin();
13663 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13665 var range = this.createRange(sel);
13668 var p = range.commonAncestorContainer;
13669 while (p.nodeType == 3) { // text node
13680 * Range intersection.. the hard stuff...
13684 * [ -- selected range --- ]
13688 * if end is before start or hits it. fail.
13689 * if start is after end or hits it fail.
13691 * if either hits (but other is outside. - then it's not
13697 // @see http://www.thismuchiknow.co.uk/?p=64.
13698 rangeIntersectsNode : function(range, node)
13700 var nodeRange = node.ownerDocument.createRange();
13702 nodeRange.selectNode(node);
13704 nodeRange.selectNodeContents(node);
13707 var rangeStartRange = range.cloneRange();
13708 rangeStartRange.collapse(true);
13710 var rangeEndRange = range.cloneRange();
13711 rangeEndRange.collapse(false);
13713 var nodeStartRange = nodeRange.cloneRange();
13714 nodeStartRange.collapse(true);
13716 var nodeEndRange = nodeRange.cloneRange();
13717 nodeEndRange.collapse(false);
13719 return rangeStartRange.compareBoundaryPoints(
13720 Range.START_TO_START, nodeEndRange) == -1 &&
13721 rangeEndRange.compareBoundaryPoints(
13722 Range.START_TO_START, nodeStartRange) == 1;
13726 rangeCompareNode : function(range, node)
13728 var nodeRange = node.ownerDocument.createRange();
13730 nodeRange.selectNode(node);
13732 nodeRange.selectNodeContents(node);
13736 range.collapse(true);
13738 nodeRange.collapse(true);
13740 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13741 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13743 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13745 var nodeIsBefore = ss == 1;
13746 var nodeIsAfter = ee == -1;
13748 if (nodeIsBefore && nodeIsAfter)
13750 if (!nodeIsBefore && nodeIsAfter)
13751 return 1; //right trailed.
13753 if (nodeIsBefore && !nodeIsAfter)
13754 return 2; // left trailed.
13759 // private? - in a new class?
13760 cleanUpPaste : function()
13762 // cleans up the whole document..
13763 Roo.log('cleanuppaste');
13765 this.cleanUpChildren(this.doc.body);
13766 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13767 if (clean != this.doc.body.innerHTML) {
13768 this.doc.body.innerHTML = clean;
13773 cleanWordChars : function(input) {// change the chars to hex code
13774 var he = Roo.HtmlEditorCore;
13776 var output = input;
13777 Roo.each(he.swapCodes, function(sw) {
13778 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13780 output = output.replace(swapper, sw[1]);
13787 cleanUpChildren : function (n)
13789 if (!n.childNodes.length) {
13792 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13793 this.cleanUpChild(n.childNodes[i]);
13800 cleanUpChild : function (node)
13803 //console.log(node);
13804 if (node.nodeName == "#text") {
13805 // clean up silly Windows -- stuff?
13808 if (node.nodeName == "#comment") {
13809 node.parentNode.removeChild(node);
13810 // clean up silly Windows -- stuff?
13814 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13816 node.parentNode.removeChild(node);
13821 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13823 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13824 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13826 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13827 // remove_keep_children = true;
13830 if (remove_keep_children) {
13831 this.cleanUpChildren(node);
13832 // inserts everything just before this node...
13833 while (node.childNodes.length) {
13834 var cn = node.childNodes[0];
13835 node.removeChild(cn);
13836 node.parentNode.insertBefore(cn, node);
13838 node.parentNode.removeChild(node);
13842 if (!node.attributes || !node.attributes.length) {
13843 this.cleanUpChildren(node);
13847 function cleanAttr(n,v)
13850 if (v.match(/^\./) || v.match(/^\//)) {
13853 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13856 if (v.match(/^#/)) {
13859 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13860 node.removeAttribute(n);
13864 function cleanStyle(n,v)
13866 if (v.match(/expression/)) { //XSS?? should we even bother..
13867 node.removeAttribute(n);
13870 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13871 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13874 var parts = v.split(/;/);
13877 Roo.each(parts, function(p) {
13878 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13882 var l = p.split(':').shift().replace(/\s+/g,'');
13883 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13885 if ( cblack.indexOf(l) > -1) {
13886 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13887 //node.removeAttribute(n);
13891 // only allow 'c whitelisted system attributes'
13892 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13893 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13894 //node.removeAttribute(n);
13904 if (clean.length) {
13905 node.setAttribute(n, clean.join(';'));
13907 node.removeAttribute(n);
13913 for (var i = node.attributes.length-1; i > -1 ; i--) {
13914 var a = node.attributes[i];
13917 if (a.name.toLowerCase().substr(0,2)=='on') {
13918 node.removeAttribute(a.name);
13921 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13922 node.removeAttribute(a.name);
13925 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13926 cleanAttr(a.name,a.value); // fixme..
13929 if (a.name == 'style') {
13930 cleanStyle(a.name,a.value);
13933 /// clean up MS crap..
13934 // tecnically this should be a list of valid class'es..
13937 if (a.name == 'class') {
13938 if (a.value.match(/^Mso/)) {
13939 node.className = '';
13942 if (a.value.match(/body/)) {
13943 node.className = '';
13954 this.cleanUpChildren(node);
13960 // hide stuff that is not compatible
13974 * @event specialkey
13978 * @cfg {String} fieldClass @hide
13981 * @cfg {String} focusClass @hide
13984 * @cfg {String} autoCreate @hide
13987 * @cfg {String} inputType @hide
13990 * @cfg {String} invalidClass @hide
13993 * @cfg {String} invalidText @hide
13996 * @cfg {String} msgFx @hide
13999 * @cfg {String} validateOnBlur @hide
14003 Roo.HtmlEditorCore.white = [
14004 'area', 'br', 'img', 'input', 'hr', 'wbr',
14006 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14007 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14008 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14009 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14010 'table', 'ul', 'xmp',
14012 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14015 'dir', 'menu', 'ol', 'ul', 'dl',
14021 Roo.HtmlEditorCore.black = [
14022 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14024 'base', 'basefont', 'bgsound', 'blink', 'body',
14025 'frame', 'frameset', 'head', 'html', 'ilayer',
14026 'iframe', 'layer', 'link', 'meta', 'object',
14027 'script', 'style' ,'title', 'xml' // clean later..
14029 Roo.HtmlEditorCore.clean = [
14030 'script', 'style', 'title', 'xml'
14032 Roo.HtmlEditorCore.remove = [
14037 Roo.HtmlEditorCore.ablack = [
14041 Roo.HtmlEditorCore.aclean = [
14042 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14046 Roo.HtmlEditorCore.pwhite= [
14047 'http', 'https', 'mailto'
14050 // white listed style attributes.
14051 Roo.HtmlEditorCore.cwhite= [
14052 // 'text-align', /// default is to allow most things..
14058 // black listed style attributes.
14059 Roo.HtmlEditorCore.cblack= [
14060 // 'font-size' -- this can be set by the project
14064 Roo.HtmlEditorCore.swapCodes =[
14083 * @class Roo.bootstrap.HtmlEditor
14084 * @extends Roo.bootstrap.TextArea
14085 * Bootstrap HtmlEditor class
14088 * Create a new HtmlEditor
14089 * @param {Object} config The config object
14092 Roo.bootstrap.HtmlEditor = function(config){
14093 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14094 if (!this.toolbars) {
14095 this.toolbars = [];
14097 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14100 * @event initialize
14101 * Fires when the editor is fully initialized (including the iframe)
14102 * @param {HtmlEditor} this
14107 * Fires when the editor is first receives the focus. Any insertion must wait
14108 * until after this event.
14109 * @param {HtmlEditor} this
14113 * @event beforesync
14114 * Fires before the textarea is updated with content from the editor iframe. Return false
14115 * to cancel the sync.
14116 * @param {HtmlEditor} this
14117 * @param {String} html
14121 * @event beforepush
14122 * Fires before the iframe editor is updated with content from the textarea. Return false
14123 * to cancel the push.
14124 * @param {HtmlEditor} this
14125 * @param {String} html
14130 * Fires when the textarea is updated with content from the editor iframe.
14131 * @param {HtmlEditor} this
14132 * @param {String} html
14137 * Fires when the iframe editor is updated with content from the textarea.
14138 * @param {HtmlEditor} this
14139 * @param {String} html
14143 * @event editmodechange
14144 * Fires when the editor switches edit modes
14145 * @param {HtmlEditor} this
14146 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14148 editmodechange: true,
14150 * @event editorevent
14151 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14152 * @param {HtmlEditor} this
14156 * @event firstfocus
14157 * Fires when on first focus - needed by toolbars..
14158 * @param {HtmlEditor} this
14163 * Auto save the htmlEditor value as a file into Events
14164 * @param {HtmlEditor} this
14168 * @event savedpreview
14169 * preview the saved version of htmlEditor
14170 * @param {HtmlEditor} this
14177 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14181 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14186 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14191 * @cfg {Number} height (in pixels)
14195 * @cfg {Number} width (in pixels)
14200 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14203 stylesheets: false,
14208 // private properties
14209 validationEvent : false,
14211 initialized : false,
14214 onFocus : Roo.emptyFn,
14216 hideMode:'offsets',
14219 tbContainer : false,
14221 toolbarContainer :function() {
14222 return this.wrap.select('.x-html-editor-tb',true).first();
14226 * Protected method that will not generally be called directly. It
14227 * is called when the editor creates its toolbar. Override this method if you need to
14228 * add custom toolbar buttons.
14229 * @param {HtmlEditor} editor
14231 createToolbar : function(){
14233 Roo.log("create toolbars");
14235 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14236 this.toolbars[0].render(this.toolbarContainer());
14240 // if (!editor.toolbars || !editor.toolbars.length) {
14241 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14244 // for (var i =0 ; i < editor.toolbars.length;i++) {
14245 // editor.toolbars[i] = Roo.factory(
14246 // typeof(editor.toolbars[i]) == 'string' ?
14247 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14248 // Roo.bootstrap.HtmlEditor);
14249 // editor.toolbars[i].init(editor);
14255 onRender : function(ct, position)
14257 // Roo.log("Call onRender: " + this.xtype);
14259 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14261 this.wrap = this.inputEl().wrap({
14262 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14265 this.editorcore.onRender(ct, position);
14267 if (this.resizable) {
14268 this.resizeEl = new Roo.Resizable(this.wrap, {
14272 minHeight : this.height,
14273 height: this.height,
14274 handles : this.resizable,
14277 resize : function(r, w, h) {
14278 _t.onResize(w,h); // -something
14284 this.createToolbar(this);
14287 if(!this.width && this.resizable){
14288 this.setSize(this.wrap.getSize());
14290 if (this.resizeEl) {
14291 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14292 // should trigger onReize..
14298 onResize : function(w, h)
14300 Roo.log('resize: ' +w + ',' + h );
14301 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14305 if(this.inputEl() ){
14306 if(typeof w == 'number'){
14307 var aw = w - this.wrap.getFrameWidth('lr');
14308 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14311 if(typeof h == 'number'){
14312 var tbh = -11; // fixme it needs to tool bar size!
14313 for (var i =0; i < this.toolbars.length;i++) {
14314 // fixme - ask toolbars for heights?
14315 tbh += this.toolbars[i].el.getHeight();
14316 //if (this.toolbars[i].footer) {
14317 // tbh += this.toolbars[i].footer.el.getHeight();
14325 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14326 ah -= 5; // knock a few pixes off for look..
14327 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14331 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14332 this.editorcore.onResize(ew,eh);
14337 * Toggles the editor between standard and source edit mode.
14338 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14340 toggleSourceEdit : function(sourceEditMode)
14342 this.editorcore.toggleSourceEdit(sourceEditMode);
14344 if(this.editorcore.sourceEditMode){
14345 Roo.log('editor - showing textarea');
14348 // Roo.log(this.syncValue());
14350 this.inputEl().removeClass('hide');
14351 this.inputEl().dom.removeAttribute('tabIndex');
14352 this.inputEl().focus();
14354 Roo.log('editor - hiding textarea');
14356 // Roo.log(this.pushValue());
14359 this.inputEl().addClass('hide');
14360 this.inputEl().dom.setAttribute('tabIndex', -1);
14361 //this.deferFocus();
14364 if(this.resizable){
14365 this.setSize(this.wrap.getSize());
14368 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14371 // private (for BoxComponent)
14372 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14374 // private (for BoxComponent)
14375 getResizeEl : function(){
14379 // private (for BoxComponent)
14380 getPositionEl : function(){
14385 initEvents : function(){
14386 this.originalValue = this.getValue();
14390 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14393 // markInvalid : Roo.emptyFn,
14395 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14398 // clearInvalid : Roo.emptyFn,
14400 setValue : function(v){
14401 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14402 this.editorcore.pushValue();
14407 deferFocus : function(){
14408 this.focus.defer(10, this);
14412 focus : function(){
14413 this.editorcore.focus();
14419 onDestroy : function(){
14425 for (var i =0; i < this.toolbars.length;i++) {
14426 // fixme - ask toolbars for heights?
14427 this.toolbars[i].onDestroy();
14430 this.wrap.dom.innerHTML = '';
14431 this.wrap.remove();
14436 onFirstFocus : function(){
14437 //Roo.log("onFirstFocus");
14438 this.editorcore.onFirstFocus();
14439 for (var i =0; i < this.toolbars.length;i++) {
14440 this.toolbars[i].onFirstFocus();
14446 syncValue : function()
14448 this.editorcore.syncValue();
14451 pushValue : function()
14453 this.editorcore.pushValue();
14457 // hide stuff that is not compatible
14471 * @event specialkey
14475 * @cfg {String} fieldClass @hide
14478 * @cfg {String} focusClass @hide
14481 * @cfg {String} autoCreate @hide
14484 * @cfg {String} inputType @hide
14487 * @cfg {String} invalidClass @hide
14490 * @cfg {String} invalidText @hide
14493 * @cfg {String} msgFx @hide
14496 * @cfg {String} validateOnBlur @hide
14507 * @class Roo.bootstrap.HtmlEditorToolbar1
14512 new Roo.bootstrap.HtmlEditor({
14515 new Roo.bootstrap.HtmlEditorToolbar1({
14516 disable : { fonts: 1 , format: 1, ..., ... , ...],
14522 * @cfg {Object} disable List of elements to disable..
14523 * @cfg {Array} btns List of additional buttons.
14527 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14530 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14533 Roo.apply(this, config);
14535 // default disabled, based on 'good practice'..
14536 this.disable = this.disable || {};
14537 Roo.applyIf(this.disable, {
14540 specialElements : true
14542 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14544 this.editor = config.editor;
14545 this.editorcore = config.editor.editorcore;
14547 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14549 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14550 // dont call parent... till later.
14552 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14558 editorcore : false,
14563 "h1","h2","h3","h4","h5","h6",
14565 "abbr", "acronym", "address", "cite", "samp", "var",
14569 onRender : function(ct, position)
14571 // Roo.log("Call onRender: " + this.xtype);
14573 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14575 this.el.dom.style.marginBottom = '0';
14577 var editorcore = this.editorcore;
14578 var editor= this.editor;
14581 var btn = function(id,cmd , toggle, handler){
14583 var event = toggle ? 'toggle' : 'click';
14588 xns: Roo.bootstrap,
14591 enableToggle:toggle !== false,
14593 pressed : toggle ? false : null,
14596 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14597 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14606 xns: Roo.bootstrap,
14607 glyphicon : 'font',
14611 xns: Roo.bootstrap,
14615 Roo.each(this.formats, function(f) {
14616 style.menu.items.push({
14618 xns: Roo.bootstrap,
14619 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14624 editorcore.insertTag(this.tagname);
14631 children.push(style);
14634 btn('bold',false,true);
14635 btn('italic',false,true);
14636 btn('align-left', 'justifyleft',true);
14637 btn('align-center', 'justifycenter',true);
14638 btn('align-right' , 'justifyright',true);
14639 btn('link', false, false, function(btn) {
14640 //Roo.log("create link?");
14641 var url = prompt(this.createLinkText, this.defaultLinkValue);
14642 if(url && url != 'http:/'+'/'){
14643 this.editorcore.relayCmd('createlink', url);
14646 btn('list','insertunorderedlist',true);
14647 btn('pencil', false,true, function(btn){
14650 this.toggleSourceEdit(btn.pressed);
14656 xns: Roo.bootstrap,
14661 xns: Roo.bootstrap,
14666 cog.menu.items.push({
14668 xns: Roo.bootstrap,
14669 html : Clean styles,
14674 editorcore.insertTag(this.tagname);
14683 this.xtype = 'Navbar';
14685 for(var i=0;i< children.length;i++) {
14687 this.buttons.add(this.addxtypeChild(children[i]));
14691 editor.on('editorevent', this.updateToolbar, this);
14693 onBtnClick : function(id)
14695 this.editorcore.relayCmd(id);
14696 this.editorcore.focus();
14700 * Protected method that will not generally be called directly. It triggers
14701 * a toolbar update by reading the markup state of the current selection in the editor.
14703 updateToolbar: function(){
14705 if(!this.editorcore.activated){
14706 this.editor.onFirstFocus(); // is this neeed?
14710 var btns = this.buttons;
14711 var doc = this.editorcore.doc;
14712 btns.get('bold').setActive(doc.queryCommandState('bold'));
14713 btns.get('italic').setActive(doc.queryCommandState('italic'));
14714 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14716 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14717 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14718 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14720 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14721 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14724 var ans = this.editorcore.getAllAncestors();
14725 if (this.formatCombo) {
14728 var store = this.formatCombo.store;
14729 this.formatCombo.setValue("");
14730 for (var i =0; i < ans.length;i++) {
14731 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14733 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14741 // hides menus... - so this cant be on a menu...
14742 Roo.bootstrap.MenuMgr.hideAll();
14744 Roo.bootstrap.MenuMgr.hideAll();
14745 //this.editorsyncValue();
14747 onFirstFocus: function() {
14748 this.buttons.each(function(item){
14752 toggleSourceEdit : function(sourceEditMode){
14755 if(sourceEditMode){
14756 Roo.log("disabling buttons");
14757 this.buttons.each( function(item){
14758 if(item.cmd != 'pencil'){
14764 Roo.log("enabling buttons");
14765 if(this.editorcore.initialized){
14766 this.buttons.each( function(item){
14772 Roo.log("calling toggole on editor");
14773 // tell the editor that it's been pressed..
14774 this.editor.toggleSourceEdit(sourceEditMode);
14784 * @class Roo.bootstrap.Table.AbstractSelectionModel
14785 * @extends Roo.util.Observable
14786 * Abstract base class for grid SelectionModels. It provides the interface that should be
14787 * implemented by descendant classes. This class should not be directly instantiated.
14790 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14791 this.locked = false;
14792 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14796 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14797 /** @ignore Called by the grid automatically. Do not call directly. */
14798 init : function(grid){
14804 * Locks the selections.
14807 this.locked = true;
14811 * Unlocks the selections.
14813 unlock : function(){
14814 this.locked = false;
14818 * Returns true if the selections are locked.
14819 * @return {Boolean}
14821 isLocked : function(){
14822 return this.locked;
14826 * @class Roo.bootstrap.Table.ColumnModel
14827 * @extends Roo.util.Observable
14828 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14829 * the columns in the table.
14832 * @param {Object} config An Array of column config objects. See this class's
14833 * config objects for details.
14835 Roo.bootstrap.Table.ColumnModel = function(config){
14837 * The config passed into the constructor
14839 this.config = config;
14842 // if no id, create one
14843 // if the column does not have a dataIndex mapping,
14844 // map it to the order it is in the config
14845 for(var i = 0, len = config.length; i < len; i++){
14847 if(typeof c.dataIndex == "undefined"){
14850 if(typeof c.renderer == "string"){
14851 c.renderer = Roo.util.Format[c.renderer];
14853 if(typeof c.id == "undefined"){
14856 // if(c.editor && c.editor.xtype){
14857 // c.editor = Roo.factory(c.editor, Roo.grid);
14859 // if(c.editor && c.editor.isFormField){
14860 // c.editor = new Roo.grid.GridEditor(c.editor);
14863 this.lookup[c.id] = c;
14867 * The width of columns which have no width specified (defaults to 100)
14870 this.defaultWidth = 100;
14873 * Default sortable of columns which have no sortable specified (defaults to false)
14876 this.defaultSortable = false;
14880 * @event widthchange
14881 * Fires when the width of a column changes.
14882 * @param {ColumnModel} this
14883 * @param {Number} columnIndex The column index
14884 * @param {Number} newWidth The new width
14886 "widthchange": true,
14888 * @event headerchange
14889 * Fires when the text of a header changes.
14890 * @param {ColumnModel} this
14891 * @param {Number} columnIndex The column index
14892 * @param {Number} newText The new header text
14894 "headerchange": true,
14896 * @event hiddenchange
14897 * Fires when a column is hidden or "unhidden".
14898 * @param {ColumnModel} this
14899 * @param {Number} columnIndex The column index
14900 * @param {Boolean} hidden true if hidden, false otherwise
14902 "hiddenchange": true,
14904 * @event columnmoved
14905 * Fires when a column is moved.
14906 * @param {ColumnModel} this
14907 * @param {Number} oldIndex
14908 * @param {Number} newIndex
14910 "columnmoved" : true,
14912 * @event columlockchange
14913 * Fires when a column's locked state is changed
14914 * @param {ColumnModel} this
14915 * @param {Number} colIndex
14916 * @param {Boolean} locked true if locked
14918 "columnlockchange" : true
14920 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14922 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14924 * @cfg {String} header The header text to display in the Grid view.
14927 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14928 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14929 * specified, the column's index is used as an index into the Record's data Array.
14932 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14933 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14936 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14937 * Defaults to the value of the {@link #defaultSortable} property.
14938 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14941 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14944 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14947 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14950 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14953 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14954 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14955 * default renderer uses the raw data value.
14958 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14962 * Returns the id of the column at the specified index.
14963 * @param {Number} index The column index
14964 * @return {String} the id
14966 getColumnId : function(index){
14967 return this.config[index].id;
14971 * Returns the column for a specified id.
14972 * @param {String} id The column id
14973 * @return {Object} the column
14975 getColumnById : function(id){
14976 return this.lookup[id];
14981 * Returns the column for a specified dataIndex.
14982 * @param {String} dataIndex The column dataIndex
14983 * @return {Object|Boolean} the column or false if not found
14985 getColumnByDataIndex: function(dataIndex){
14986 var index = this.findColumnIndex(dataIndex);
14987 return index > -1 ? this.config[index] : false;
14991 * Returns the index for a specified column id.
14992 * @param {String} id The column id
14993 * @return {Number} the index, or -1 if not found
14995 getIndexById : function(id){
14996 for(var i = 0, len = this.config.length; i < len; i++){
14997 if(this.config[i].id == id){
15005 * Returns the index for a specified column dataIndex.
15006 * @param {String} dataIndex The column dataIndex
15007 * @return {Number} the index, or -1 if not found
15010 findColumnIndex : function(dataIndex){
15011 for(var i = 0, len = this.config.length; i < len; i++){
15012 if(this.config[i].dataIndex == dataIndex){
15020 moveColumn : function(oldIndex, newIndex){
15021 var c = this.config[oldIndex];
15022 this.config.splice(oldIndex, 1);
15023 this.config.splice(newIndex, 0, c);
15024 this.dataMap = null;
15025 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15028 isLocked : function(colIndex){
15029 return this.config[colIndex].locked === true;
15032 setLocked : function(colIndex, value, suppressEvent){
15033 if(this.isLocked(colIndex) == value){
15036 this.config[colIndex].locked = value;
15037 if(!suppressEvent){
15038 this.fireEvent("columnlockchange", this, colIndex, value);
15042 getTotalLockedWidth : function(){
15043 var totalWidth = 0;
15044 for(var i = 0; i < this.config.length; i++){
15045 if(this.isLocked(i) && !this.isHidden(i)){
15046 this.totalWidth += this.getColumnWidth(i);
15052 getLockedCount : function(){
15053 for(var i = 0, len = this.config.length; i < len; i++){
15054 if(!this.isLocked(i)){
15061 * Returns the number of columns.
15064 getColumnCount : function(visibleOnly){
15065 if(visibleOnly === true){
15067 for(var i = 0, len = this.config.length; i < len; i++){
15068 if(!this.isHidden(i)){
15074 return this.config.length;
15078 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15079 * @param {Function} fn
15080 * @param {Object} scope (optional)
15081 * @return {Array} result
15083 getColumnsBy : function(fn, scope){
15085 for(var i = 0, len = this.config.length; i < len; i++){
15086 var c = this.config[i];
15087 if(fn.call(scope||this, c, i) === true){
15095 * Returns true if the specified column is sortable.
15096 * @param {Number} col The column index
15097 * @return {Boolean}
15099 isSortable : function(col){
15100 if(typeof this.config[col].sortable == "undefined"){
15101 return this.defaultSortable;
15103 return this.config[col].sortable;
15107 * Returns the rendering (formatting) function defined for the column.
15108 * @param {Number} col The column index.
15109 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15111 getRenderer : function(col){
15112 if(!this.config[col].renderer){
15113 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15115 return this.config[col].renderer;
15119 * Sets the rendering (formatting) function for a column.
15120 * @param {Number} col The column index
15121 * @param {Function} fn The function to use to process the cell's raw data
15122 * to return HTML markup for the grid view. The render function is called with
15123 * the following parameters:<ul>
15124 * <li>Data value.</li>
15125 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15126 * <li>css A CSS style string to apply to the table cell.</li>
15127 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15128 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15129 * <li>Row index</li>
15130 * <li>Column index</li>
15131 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15133 setRenderer : function(col, fn){
15134 this.config[col].renderer = fn;
15138 * Returns the width for the specified column.
15139 * @param {Number} col The column index
15142 getColumnWidth : function(col){
15143 return this.config[col].width * 1 || this.defaultWidth;
15147 * Sets the width for a column.
15148 * @param {Number} col The column index
15149 * @param {Number} width The new width
15151 setColumnWidth : function(col, width, suppressEvent){
15152 this.config[col].width = width;
15153 this.totalWidth = null;
15154 if(!suppressEvent){
15155 this.fireEvent("widthchange", this, col, width);
15160 * Returns the total width of all columns.
15161 * @param {Boolean} includeHidden True to include hidden column widths
15164 getTotalWidth : function(includeHidden){
15165 if(!this.totalWidth){
15166 this.totalWidth = 0;
15167 for(var i = 0, len = this.config.length; i < len; i++){
15168 if(includeHidden || !this.isHidden(i)){
15169 this.totalWidth += this.getColumnWidth(i);
15173 return this.totalWidth;
15177 * Returns the header for the specified column.
15178 * @param {Number} col The column index
15181 getColumnHeader : function(col){
15182 return this.config[col].header;
15186 * Sets the header for a column.
15187 * @param {Number} col The column index
15188 * @param {String} header The new header
15190 setColumnHeader : function(col, header){
15191 this.config[col].header = header;
15192 this.fireEvent("headerchange", this, col, header);
15196 * Returns the tooltip for the specified column.
15197 * @param {Number} col The column index
15200 getColumnTooltip : function(col){
15201 return this.config[col].tooltip;
15204 * Sets the tooltip for a column.
15205 * @param {Number} col The column index
15206 * @param {String} tooltip The new tooltip
15208 setColumnTooltip : function(col, tooltip){
15209 this.config[col].tooltip = tooltip;
15213 * Returns the dataIndex for the specified column.
15214 * @param {Number} col The column index
15217 getDataIndex : function(col){
15218 return this.config[col].dataIndex;
15222 * Sets the dataIndex for a column.
15223 * @param {Number} col The column index
15224 * @param {Number} dataIndex The new dataIndex
15226 setDataIndex : function(col, dataIndex){
15227 this.config[col].dataIndex = dataIndex;
15233 * Returns true if the cell is editable.
15234 * @param {Number} colIndex The column index
15235 * @param {Number} rowIndex The row index
15236 * @return {Boolean}
15238 isCellEditable : function(colIndex, rowIndex){
15239 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15243 * Returns the editor defined for the cell/column.
15244 * return false or null to disable editing.
15245 * @param {Number} colIndex The column index
15246 * @param {Number} rowIndex The row index
15249 getCellEditor : function(colIndex, rowIndex){
15250 return this.config[colIndex].editor;
15254 * Sets if a column is editable.
15255 * @param {Number} col The column index
15256 * @param {Boolean} editable True if the column is editable
15258 setEditable : function(col, editable){
15259 this.config[col].editable = editable;
15264 * Returns true if the column is hidden.
15265 * @param {Number} colIndex The column index
15266 * @return {Boolean}
15268 isHidden : function(colIndex){
15269 return this.config[colIndex].hidden;
15274 * Returns true if the column width cannot be changed
15276 isFixed : function(colIndex){
15277 return this.config[colIndex].fixed;
15281 * Returns true if the column can be resized
15282 * @return {Boolean}
15284 isResizable : function(colIndex){
15285 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15288 * Sets if a column is hidden.
15289 * @param {Number} colIndex The column index
15290 * @param {Boolean} hidden True if the column is hidden
15292 setHidden : function(colIndex, hidden){
15293 this.config[colIndex].hidden = hidden;
15294 this.totalWidth = null;
15295 this.fireEvent("hiddenchange", this, colIndex, hidden);
15299 * Sets the editor for a column.
15300 * @param {Number} col The column index
15301 * @param {Object} editor The editor object
15303 setEditor : function(col, editor){
15304 this.config[col].editor = editor;
15308 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15309 if(typeof value == "string" && value.length < 1){
15315 // Alias for backwards compatibility
15316 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15319 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15320 * @class Roo.bootstrap.Table.RowSelectionModel
15321 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15322 * It supports multiple selections and keyboard selection/navigation.
15324 * @param {Object} config
15327 Roo.bootstrap.Table.RowSelectionModel = function(config){
15328 Roo.apply(this, config);
15329 this.selections = new Roo.util.MixedCollection(false, function(o){
15334 this.lastActive = false;
15338 * @event selectionchange
15339 * Fires when the selection changes
15340 * @param {SelectionModel} this
15342 "selectionchange" : true,
15344 * @event afterselectionchange
15345 * Fires after the selection changes (eg. by key press or clicking)
15346 * @param {SelectionModel} this
15348 "afterselectionchange" : true,
15350 * @event beforerowselect
15351 * Fires when a row is selected being selected, return false to cancel.
15352 * @param {SelectionModel} this
15353 * @param {Number} rowIndex The selected index
15354 * @param {Boolean} keepExisting False if other selections will be cleared
15356 "beforerowselect" : true,
15359 * Fires when a row is selected.
15360 * @param {SelectionModel} this
15361 * @param {Number} rowIndex The selected index
15362 * @param {Roo.data.Record} r The record
15364 "rowselect" : true,
15366 * @event rowdeselect
15367 * Fires when a row is deselected.
15368 * @param {SelectionModel} this
15369 * @param {Number} rowIndex The selected index
15371 "rowdeselect" : true
15373 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15374 this.locked = false;
15377 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15379 * @cfg {Boolean} singleSelect
15380 * True to allow selection of only one row at a time (defaults to false)
15382 singleSelect : false,
15385 initEvents : function(){
15387 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15388 this.grid.on("mousedown", this.handleMouseDown, this);
15389 }else{ // allow click to work like normal
15390 this.grid.on("rowclick", this.handleDragableRowClick, this);
15393 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15394 "up" : function(e){
15396 this.selectPrevious(e.shiftKey);
15397 }else if(this.last !== false && this.lastActive !== false){
15398 var last = this.last;
15399 this.selectRange(this.last, this.lastActive-1);
15400 this.grid.getView().focusRow(this.lastActive);
15401 if(last !== false){
15405 this.selectFirstRow();
15407 this.fireEvent("afterselectionchange", this);
15409 "down" : function(e){
15411 this.selectNext(e.shiftKey);
15412 }else if(this.last !== false && this.lastActive !== false){
15413 var last = this.last;
15414 this.selectRange(this.last, this.lastActive+1);
15415 this.grid.getView().focusRow(this.lastActive);
15416 if(last !== false){
15420 this.selectFirstRow();
15422 this.fireEvent("afterselectionchange", this);
15427 var view = this.grid.view;
15428 view.on("refresh", this.onRefresh, this);
15429 view.on("rowupdated", this.onRowUpdated, this);
15430 view.on("rowremoved", this.onRemove, this);
15434 onRefresh : function(){
15435 var ds = this.grid.dataSource, i, v = this.grid.view;
15436 var s = this.selections;
15437 s.each(function(r){
15438 if((i = ds.indexOfId(r.id)) != -1){
15447 onRemove : function(v, index, r){
15448 this.selections.remove(r);
15452 onRowUpdated : function(v, index, r){
15453 if(this.isSelected(r)){
15454 v.onRowSelect(index);
15460 * @param {Array} records The records to select
15461 * @param {Boolean} keepExisting (optional) True to keep existing selections
15463 selectRecords : function(records, keepExisting){
15465 this.clearSelections();
15467 var ds = this.grid.dataSource;
15468 for(var i = 0, len = records.length; i < len; i++){
15469 this.selectRow(ds.indexOf(records[i]), true);
15474 * Gets the number of selected rows.
15477 getCount : function(){
15478 return this.selections.length;
15482 * Selects the first row in the grid.
15484 selectFirstRow : function(){
15489 * Select the last row.
15490 * @param {Boolean} keepExisting (optional) True to keep existing selections
15492 selectLastRow : function(keepExisting){
15493 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15497 * Selects the row immediately following the last selected row.
15498 * @param {Boolean} keepExisting (optional) True to keep existing selections
15500 selectNext : function(keepExisting){
15501 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15502 this.selectRow(this.last+1, keepExisting);
15503 this.grid.getView().focusRow(this.last);
15508 * Selects the row that precedes the last selected row.
15509 * @param {Boolean} keepExisting (optional) True to keep existing selections
15511 selectPrevious : function(keepExisting){
15513 this.selectRow(this.last-1, keepExisting);
15514 this.grid.getView().focusRow(this.last);
15519 * Returns the selected records
15520 * @return {Array} Array of selected records
15522 getSelections : function(){
15523 return [].concat(this.selections.items);
15527 * Returns the first selected record.
15530 getSelected : function(){
15531 return this.selections.itemAt(0);
15536 * Clears all selections.
15538 clearSelections : function(fast){
15539 if(this.locked) return;
15541 var ds = this.grid.dataSource;
15542 var s = this.selections;
15543 s.each(function(r){
15544 this.deselectRow(ds.indexOfId(r.id));
15548 this.selections.clear();
15555 * Selects all rows.
15557 selectAll : function(){
15558 if(this.locked) return;
15559 this.selections.clear();
15560 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15561 this.selectRow(i, true);
15566 * Returns True if there is a selection.
15567 * @return {Boolean}
15569 hasSelection : function(){
15570 return this.selections.length > 0;
15574 * Returns True if the specified row is selected.
15575 * @param {Number/Record} record The record or index of the record to check
15576 * @return {Boolean}
15578 isSelected : function(index){
15579 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15580 return (r && this.selections.key(r.id) ? true : false);
15584 * Returns True if the specified record id is selected.
15585 * @param {String} id The id of record to check
15586 * @return {Boolean}
15588 isIdSelected : function(id){
15589 return (this.selections.key(id) ? true : false);
15593 handleMouseDown : function(e, t){
15594 var view = this.grid.getView(), rowIndex;
15595 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15598 if(e.shiftKey && this.last !== false){
15599 var last = this.last;
15600 this.selectRange(last, rowIndex, e.ctrlKey);
15601 this.last = last; // reset the last
15602 view.focusRow(rowIndex);
15604 var isSelected = this.isSelected(rowIndex);
15605 if(e.button !== 0 && isSelected){
15606 view.focusRow(rowIndex);
15607 }else if(e.ctrlKey && isSelected){
15608 this.deselectRow(rowIndex);
15609 }else if(!isSelected){
15610 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15611 view.focusRow(rowIndex);
15614 this.fireEvent("afterselectionchange", this);
15617 handleDragableRowClick : function(grid, rowIndex, e)
15619 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15620 this.selectRow(rowIndex, false);
15621 grid.view.focusRow(rowIndex);
15622 this.fireEvent("afterselectionchange", this);
15627 * Selects multiple rows.
15628 * @param {Array} rows Array of the indexes of the row to select
15629 * @param {Boolean} keepExisting (optional) True to keep existing selections
15631 selectRows : function(rows, keepExisting){
15633 this.clearSelections();
15635 for(var i = 0, len = rows.length; i < len; i++){
15636 this.selectRow(rows[i], true);
15641 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15642 * @param {Number} startRow The index of the first row in the range
15643 * @param {Number} endRow The index of the last row in the range
15644 * @param {Boolean} keepExisting (optional) True to retain existing selections
15646 selectRange : function(startRow, endRow, keepExisting){
15647 if(this.locked) return;
15649 this.clearSelections();
15651 if(startRow <= endRow){
15652 for(var i = startRow; i <= endRow; i++){
15653 this.selectRow(i, true);
15656 for(var i = startRow; i >= endRow; i--){
15657 this.selectRow(i, true);
15663 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15664 * @param {Number} startRow The index of the first row in the range
15665 * @param {Number} endRow The index of the last row in the range
15667 deselectRange : function(startRow, endRow, preventViewNotify){
15668 if(this.locked) return;
15669 for(var i = startRow; i <= endRow; i++){
15670 this.deselectRow(i, preventViewNotify);
15676 * @param {Number} row The index of the row to select
15677 * @param {Boolean} keepExisting (optional) True to keep existing selections
15679 selectRow : function(index, keepExisting, preventViewNotify){
15680 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15681 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15682 if(!keepExisting || this.singleSelect){
15683 this.clearSelections();
15685 var r = this.grid.dataSource.getAt(index);
15686 this.selections.add(r);
15687 this.last = this.lastActive = index;
15688 if(!preventViewNotify){
15689 this.grid.getView().onRowSelect(index);
15691 this.fireEvent("rowselect", this, index, r);
15692 this.fireEvent("selectionchange", this);
15698 * @param {Number} row The index of the row to deselect
15700 deselectRow : function(index, preventViewNotify){
15701 if(this.locked) return;
15702 if(this.last == index){
15705 if(this.lastActive == index){
15706 this.lastActive = false;
15708 var r = this.grid.dataSource.getAt(index);
15709 this.selections.remove(r);
15710 if(!preventViewNotify){
15711 this.grid.getView().onRowDeselect(index);
15713 this.fireEvent("rowdeselect", this, index);
15714 this.fireEvent("selectionchange", this);
15718 restoreLast : function(){
15720 this.last = this._last;
15725 acceptsNav : function(row, col, cm){
15726 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15730 onEditorKey : function(field, e){
15731 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15736 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15738 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15740 }else if(k == e.ENTER && !e.ctrlKey){
15744 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15746 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15748 }else if(k == e.ESC){
15752 g.startEditing(newCell[0], newCell[1]);
15763 * @class Roo.bootstrap.MessageBar
15764 * @extends Roo.bootstrap.Component
15765 * Bootstrap MessageBar class
15766 * @cfg {String} html contents of the MessageBar
15767 * @cfg {String} weight (info | success | warning | danger) default info
15768 * @cfg {String} beforeClass insert the bar before the given class
15769 * @cfg {Boolean} closable (true | false) default false
15770 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15773 * Create a new Element
15774 * @param {Object} config The config object
15777 Roo.bootstrap.MessageBar = function(config){
15778 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15781 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15787 beforeClass: 'bootstrap-sticky-wrap',
15789 getAutoCreate : function(){
15793 cls: 'alert alert-dismissable alert-' + this.weight,
15798 html: this.html || ''
15804 cfg.cls += ' alert-messages-fixed';
15818 onRender : function(ct, position)
15820 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15823 var cfg = Roo.apply({}, this.getAutoCreate());
15827 cfg.cls += ' ' + this.cls;
15830 cfg.style = this.style;
15832 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15834 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15837 this.el.select('>button.close').on('click', this.hide, this);
15843 if (!this.rendered) {
15849 this.fireEvent('show', this);
15855 if (!this.rendered) {
15861 this.fireEvent('hide', this);
15864 update : function()
15866 // var e = this.el.dom.firstChild;
15868 // if(this.closable){
15869 // e = e.nextSibling;
15872 // e.data = this.html || '';
15874 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';