4 * base class for bootstrap elements.
8 Roo.bootstrap = Roo.bootstrap || {};
10 * @class Roo.bootstrap.Component
11 * @extends Roo.Component
12 * Bootstrap Component base class
13 * @cfg {String} cls css class
14 * @cfg {String} style any extra css
15 * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16 * @cfg {Boolean} can_build_overlaid True if element can be rebuild from a HTML page
17 * @cfg {string} dataId cutomer id
18 * @cfg {string} name Specifies name attribute
21 * Do not use directly - it does not do anything..
22 * @param {Object} config The config object
27 Roo.bootstrap.Component = function(config){
28 Roo.bootstrap.Component.superclass.constructor.call(this, config);
31 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent, {
34 allowDomMove : false, // to stop relocations in parent onRender...
42 initEvents : function() { },
48 can_build_overlaid : true,
55 // returns the parent component..
56 return Roo.ComponentMgr.get(this.parentId)
62 onRender : function(ct, position)
64 // Roo.log("Call onRender: " + this.xtype);
66 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
69 if (this.el.attr('xtype')) {
70 this.el.attr('xtypex', this.el.attr('xtype'));
71 this.el.dom.removeAttribute('xtype');
81 var cfg = Roo.apply({}, this.getAutoCreate());
84 // fill in the extra attributes
85 if (this.xattr && typeof(this.xattr) =='object') {
86 for (var i in this.xattr) {
87 cfg[i] = this.xattr[i];
92 cfg.dataId = this.dataId;
96 cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
99 if (this.style) { // fixme needs to support more complex style data.
100 cfg.style = this.style;
104 cfg.name = this.name;
107 this.el = ct.createChild(cfg, position);
109 if(this.tabIndex !== undefined){
110 this.el.dom.setAttribute('tabIndex', this.tabIndex);
117 getChildContainer : function()
123 addxtype : function(tree,cntr)
127 cn = Roo.factory(tree);
129 cn.parentType = this.xtype; //??
130 cn.parentId = this.id;
132 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
134 var has_flexy_each = (typeof(tree['flexy:foreach']) != 'undefined');
136 var has_flexy_if = (typeof(tree['flexy:if']) != 'undefined');
138 var build_from_html = Roo.XComponent.build_from_html;
140 var is_body = (tree.xtype == 'Body') ;
142 var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
144 var self_cntr_el = Roo.get(this[cntr]());
146 if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
147 if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
148 return this.addxtypeChild(tree,cntr);
151 var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
154 return this.addxtypeChild(Roo.apply({}, tree),cntr);
157 Roo.log('skipping render');
165 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
171 if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
175 ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
180 addxtypeChild : function (tree, cntr)
182 Roo.log('addxtypeChild:' + cntr);
184 cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
187 var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
188 (typeof(tree['flexy:foreach']) != 'undefined');
193 // render the element if it's not BODY.
194 if (tree.xtype != 'Body') {
196 cn = Roo.factory(tree);
198 cn.parentType = this.xtype; //??
199 cn.parentId = this.id;
201 var build_from_html = Roo.XComponent.build_from_html;
204 // does the container contain child eleemnts with 'xtype' attributes.
205 // that match this xtype..
206 // note - when we render we create these as well..
207 // so we should check to see if body has xtype set.
208 if (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
210 var self_cntr_el = Roo.get(this[cntr]());
211 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
213 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
214 // Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
220 //echild.dom.removeAttribute('xtype');
222 Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
229 // if object has flexy:if - then it may or may not be rendered.
230 if (build_from_html && has_flexy && !cn.el && cn.can_build_overlaid) {
231 // skip a flexy if element.
232 Roo.log('skipping render');
235 // actually if flexy:foreach is found, we really want to create
236 // multiple copies here...
238 //Roo.log(this[cntr]());
239 cn.render(this[cntr]());
241 // then add the element..
248 if (typeof (tree.menu) != 'undefined') {
249 tree.menu.parentType = cn.xtype;
250 tree.menu.triggerEl = cn.el;
251 nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
255 if (!tree.items || !tree.items.length) {
259 var items = tree.items;
262 //Roo.log(items.length);
264 for(var i =0;i < items.length;i++) {
265 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
286 * @class Roo.bootstrap.Body
287 * @extends Roo.bootstrap.Component
288 * Bootstrap Body class
292 * @param {Object} config The config object
295 Roo.bootstrap.Body = function(config){
296 Roo.bootstrap.Body.superclass.constructor.call(this, config);
297 this.el = Roo.get(document.body);
300 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component, {
305 onRender : function(ct, position){
308 //this.el.addClass([this.fieldClass, this.cls]);
326 * @class Roo.bootstrap.ButtonGroup
327 * @extends Roo.bootstrap.Component
328 * Bootstrap ButtonGroup class
329 * @cfg {String} size lg | sm | xs (default empty normal)
330 * @cfg {String} align vertical | justified (default none)
331 * @cfg {String} direction up | down (default down)
332 * @cfg {Boolean} toolbar false | true
333 * @cfg {Boolean} btn true | false
338 * @param {Object} config The config object
341 Roo.bootstrap.ButtonGroup = function(config){
342 Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
345 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component, {
353 getAutoCreate : function(){
359 cfg.html = this.html || cfg.html;
370 if (['vertical','justified'].indexOf(this.align)!==-1) {
371 cfg.cls = 'btn-group-' + this.align;
373 if (this.align == 'justified') {
374 console.log(this.items);
378 if (['lg','sm','xs'].indexOf(this.size)!==-1) {
379 cfg.cls += ' btn-group-' + this.size;
382 if (this.direction == 'up') {
383 cfg.cls += ' dropup' ;
399 * @class Roo.bootstrap.Button
400 * @extends Roo.bootstrap.Component
401 * Bootstrap Button class
402 * @cfg {String} html The button content
403 * @cfg {String} weight default (or empty) | primary | success | info | warning | danger | link
404 * @cfg {String} size empty | lg | sm | xs
405 * @cfg {String} tag empty | a | input | submit
406 * @cfg {String} href empty or href
407 * @cfg {Boolean} disabled false | true
408 * @cfg {Boolean} isClose false | true
409 * @cfg {String} glyphicon empty | adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out
410 * @cfg {String} badge text for badge
411 * @cfg {String} theme default (or empty) | glow
412 * @cfg {Boolean} inverse false | true
413 * @cfg {Boolean} toggle false | true
414 * @cfg {String} ontext text for on toggle state
415 * @cfg {String} offtext text for off toggle state
416 * @cfg {Boolean} defaulton true | false
417 * @cfg {Boolean} preventDefault (true | false) default true
418 * @cfg {Boolean} removeClass true | false remove the standard class..
419 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
422 * Create a new button
423 * @param {Object} config The config object
427 Roo.bootstrap.Button = function(config){
428 Roo.bootstrap.Button.superclass.constructor.call(this, config);
433 * When a butotn is pressed
434 * @param {Roo.EventObject} e
439 * After the button has been toggles
440 * @param {Roo.EventObject} e
441 * @param {boolean} pressed (also available as button.pressed)
447 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component, {
465 preventDefault: true,
474 getAutoCreate : function(){
482 if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
483 throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
488 cfg.html = this.html || cfg.html;
490 if (this.toggle == true) {
493 cls: 'slider-frame roo-button',
498 'data-off-text':'OFF',
499 cls: 'slider-button',
505 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
506 cfg.cls += ' '+this.weight;
515 cfg["aria-hidden"] = true;
517 cfg.html = "×";
523 if (this.theme==='default') {
524 cfg.cls = 'btn roo-button';
526 //if (this.parentType != 'Navbar') {
527 this.weight = this.weight.length ? this.weight : 'default';
529 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
531 cfg.cls += ' btn-' + this.weight;
533 } else if (this.theme==='glow') {
536 cfg.cls = 'btn-glow roo-button';
538 if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
540 cfg.cls += ' ' + this.weight;
546 this.cls += ' inverse';
551 cfg.cls += ' active';
555 cfg.disabled = 'disabled';
559 Roo.log('changing to ul' );
561 this.glyphicon = 'caret';
564 cfg.cls += this.size.length ? (' btn-' + this.size) : '';
566 //gsRoo.log(this.parentType);
567 if (this.parentType === 'Navbar' && !this.parent().bar) {
568 Roo.log('changing to li?');
577 href : this.href || '#'
580 cfg.cn[0].html = this.html + ' <span class="caret"></span>';
581 cfg.cls += ' dropdown';
588 cfg.cls += this.parentType === 'Navbar' ? ' navbar-btn' : '';
590 if (this.glyphicon) {
591 cfg.html = ' ' + cfg.html;
596 cls: 'glyphicon glyphicon-' + this.glyphicon
606 // cfg.cls='btn roo-button';
610 var value = cfg.html;
615 cls: 'glyphicon glyphicon-' + this.glyphicon,
634 cfg.cls += ' dropdown';
635 cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
638 if (cfg.tag !== 'a' && this.href !== '') {
639 throw "Tag must be a to set href.";
640 } else if (this.href.length > 0) {
641 cfg.href = this.href;
644 if(this.removeClass){
649 cfg.target = this.target;
654 initEvents: function() {
655 // Roo.log('init events?');
656 // Roo.log(this.el.dom);
657 if (this.el.hasClass('roo-button')) {
658 this.el.on('click', this.onClick, this);
660 this.el.select('.roo-button').on('click', this.onClick, this);
666 onClick : function(e)
672 Roo.log('button on click ');
673 if(this.preventDefault){
676 if (this.pressed === true || this.pressed === false) {
677 this.pressed = !this.pressed;
678 this.el[this.pressed ? 'addClass' : 'removeClass']('active');
679 this.fireEvent('toggle', this, e, this.pressed);
683 this.fireEvent('click', this, e);
687 * Enables this button
691 this.disabled = false;
692 this.el.removeClass('disabled');
696 * Disable this button
700 this.disabled = true;
701 this.el.addClass('disabled');
704 * sets the active state on/off,
705 * @param {Boolean} state (optional) Force a particular state
707 setActive : function(v) {
709 this.el[v ? 'addClass' : 'removeClass']('active');
712 * toggles the current active state
714 toggleActive : function()
716 var active = this.el.hasClass('active');
717 this.setActive(!active);
734 * @class Roo.bootstrap.Column
735 * @extends Roo.bootstrap.Component
736 * Bootstrap Column class
737 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
738 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
739 * @cfg {Number} md colspan out of 12 for computer-sized screens
740 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
741 * @cfg {String} html content of column.
744 * Create a new Column
745 * @param {Object} config The config object
748 Roo.bootstrap.Column = function(config){
749 Roo.bootstrap.Column.superclass.constructor.call(this, config);
752 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component, {
761 getAutoCreate : function(){
762 var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
770 ['xs','sm','md','lg'].map(function(size){
771 if (settings[size]) {
772 cfg.cls += ' col-' + size + '-' + settings[size];
775 if (this.html.length) {
776 cfg.html = this.html;
795 * @class Roo.bootstrap.Container
796 * @extends Roo.bootstrap.Component
797 * Bootstrap Container class
798 * @cfg {Boolean} jumbotron is it a jumbotron element
799 * @cfg {String} html content of element
800 * @cfg {String} well (lg|sm|md) a well, large, small or medium.
801 * @cfg {String} panel (primary|success|info|warning|danger) render as a panel.
802 * @cfg {String} header content of header (for panel)
803 * @cfg {String} footer content of footer (for panel)
804 * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
807 * Create a new Container
808 * @param {Object} config The config object
811 Roo.bootstrap.Container = function(config){
812 Roo.bootstrap.Container.superclass.constructor.call(this, config);
815 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component, {
825 getChildContainer : function() {
831 if (this.panel.length) {
832 return this.el.select('.panel-body',true).first();
839 getAutoCreate : function(){
845 if (this.jumbotron) {
846 cfg.cls = 'jumbotron';
849 cfg.cls = this.cls + '';
852 if (this.sticky.length) {
854 var bd = Roo.get(document.body);
855 if (!bd.hasClass('bootstrap-sticky')) {
856 bd.addClass('bootstrap-sticky');
857 Roo.select('html',true).setStyle('height', '100%');
860 cfg.cls += 'bootstrap-sticky-' + this.sticky;
864 if (this.well.length) {
868 cfg.cls +=' well well-' +this.well;
878 if (this.panel.length) {
879 cfg.cls += ' panel panel-' + this.panel;
881 if (this.header.length) {
884 cls : 'panel-heading',
900 if (this.footer.length) {
902 cls : 'panel-footer',
910 body.html = this.html || cfg.html;
912 if (!cfg.cls.length) {
913 cfg.cls = 'container';
930 * @class Roo.bootstrap.Img
931 * @extends Roo.bootstrap.Component
932 * Bootstrap Img class
933 * @cfg {Boolean} imgResponsive false | true
934 * @cfg {String} border rounded | circle | thumbnail
935 * @cfg {String} src image source
936 * @cfg {String} alt image alternative text
937 * @cfg {String} href a tag href
938 * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
942 * @param {Object} config The config object
945 Roo.bootstrap.Img = function(config){
946 Roo.bootstrap.Img.superclass.constructor.call(this, config);
952 * The img click event for the img.
953 * @param {Roo.EventObject} e
959 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component, {
967 getAutoCreate : function(){
971 cls: 'img-responsive',
975 cfg.html = this.html || cfg.html;
977 cfg.src = this.src || cfg.src;
979 if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
980 cfg.cls += ' img-' + this.border;
997 a.target = this.target;
1003 return (this.href) ? a : cfg;
1006 initEvents: function() {
1009 this.el.on('click', this.onClick, this);
1013 onClick : function(e)
1015 Roo.log('img onclick');
1016 this.fireEvent('click', this, e);
1029 * @class Roo.bootstrap.Header
1030 * @extends Roo.bootstrap.Component
1031 * Bootstrap Header class
1032 * @cfg {String} html content of header
1033 * @cfg {Number} level (1|2|3|4|5|6) default 1
1036 * Create a new Header
1037 * @param {Object} config The config object
1041 Roo.bootstrap.Header = function(config){
1042 Roo.bootstrap.Header.superclass.constructor.call(this, config);
1045 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component, {
1053 getAutoCreate : function(){
1056 tag: 'h' + (1 *this.level),
1057 html: this.html || 'fill in html'
1069 * Ext JS Library 1.1.1
1070 * Copyright(c) 2006-2007, Ext JS, LLC.
1072 * Originally Released Under LGPL - original licence link has changed is not relivant.
1075 * <script type="text/javascript">
1079 * @class Roo.bootstrap.MenuMgr
1080 * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1083 Roo.bootstrap.MenuMgr = function(){
1084 var menus, active, groups = {}, attached = false, lastShow = new Date();
1086 // private - called when first menu is created
1089 active = new Roo.util.MixedCollection();
1090 Roo.get(document).addKeyListener(27, function(){
1091 if(active.length > 0){
1099 if(active && active.length > 0){
1100 var c = active.clone();
1110 if(active.length < 1){
1111 Roo.get(document).un("mouseup", onMouseDown);
1119 var last = active.last();
1120 lastShow = new Date();
1123 Roo.get(document).on("mouseup", onMouseDown);
1128 //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1129 m.parentMenu.activeChild = m;
1130 }else if(last && last.isVisible()){
1131 //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1136 function onBeforeHide(m){
1138 m.activeChild.hide();
1140 if(m.autoHideTimer){
1141 clearTimeout(m.autoHideTimer);
1142 delete m.autoHideTimer;
1147 function onBeforeShow(m){
1148 var pm = m.parentMenu;
1149 if(!pm && !m.allowOtherMenus){
1151 }else if(pm && pm.activeChild && active != m){
1152 pm.activeChild.hide();
1157 function onMouseDown(e){
1158 Roo.log("on MouseDown");
1159 if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
1167 function onBeforeCheck(mi, state){
1169 var g = groups[mi.group];
1170 for(var i = 0, l = g.length; i < l; i++){
1172 g[i].setChecked(false);
1181 * Hides all menus that are currently visible
1183 hideAll : function(){
1188 register : function(menu){
1192 menus[menu.id] = menu;
1193 menu.on("beforehide", onBeforeHide);
1194 menu.on("hide", onHide);
1195 menu.on("beforeshow", onBeforeShow);
1196 menu.on("show", onShow);
1198 if(g && menu.events["checkchange"]){
1202 groups[g].push(menu);
1203 menu.on("checkchange", onCheck);
1208 * Returns a {@link Roo.menu.Menu} object
1209 * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1210 * be used to generate and return a new Menu instance.
1212 get : function(menu){
1213 if(typeof menu == "string"){ // menu id
1215 }else if(menu.events){ // menu instance
1218 /*else if(typeof menu.length == 'number'){ // array of menu items?
1219 return new Roo.bootstrap.Menu({items:menu});
1220 }else{ // otherwise, must be a config
1221 return new Roo.bootstrap.Menu(menu);
1228 unregister : function(menu){
1229 delete menus[menu.id];
1230 menu.un("beforehide", onBeforeHide);
1231 menu.un("hide", onHide);
1232 menu.un("beforeshow", onBeforeShow);
1233 menu.un("show", onShow);
1235 if(g && menu.events["checkchange"]){
1236 groups[g].remove(menu);
1237 menu.un("checkchange", onCheck);
1242 registerCheckable : function(menuItem){
1243 var g = menuItem.group;
1248 groups[g].push(menuItem);
1249 menuItem.on("beforecheckchange", onBeforeCheck);
1254 unregisterCheckable : function(menuItem){
1255 var g = menuItem.group;
1257 groups[g].remove(menuItem);
1258 menuItem.un("beforecheckchange", onBeforeCheck);
1270 * @class Roo.bootstrap.Menu
1271 * @extends Roo.bootstrap.Component
1272 * Bootstrap Menu class - container for MenuItems
1273 * @cfg {String} type type of menu
1277 * @param {Object} config The config object
1281 Roo.bootstrap.Menu = function(config){
1282 Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1283 if (this.registerMenu) {
1284 Roo.bootstrap.MenuMgr.register(this);
1289 * Fires before this menu is displayed
1290 * @param {Roo.menu.Menu} this
1295 * Fires before this menu is hidden
1296 * @param {Roo.menu.Menu} this
1301 * Fires after this menu is displayed
1302 * @param {Roo.menu.Menu} this
1307 * Fires after this menu is hidden
1308 * @param {Roo.menu.Menu} this
1313 * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1314 * @param {Roo.menu.Menu} this
1315 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1316 * @param {Roo.EventObject} e
1321 * Fires when the mouse is hovering over this menu
1322 * @param {Roo.menu.Menu} this
1323 * @param {Roo.EventObject} e
1324 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1329 * Fires when the mouse exits this menu
1330 * @param {Roo.menu.Menu} this
1331 * @param {Roo.EventObject} e
1332 * @param {Roo.menu.Item} menuItem The menu item that was clicked
1337 * Fires when a menu item contained in this menu is clicked
1338 * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1339 * @param {Roo.EventObject} e
1343 this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1346 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component, {
1350 triggerEl : false, // is this set by component builder? -- it should really be fetched from parent()???
1353 * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1355 registerMenu : true,
1357 menuItems :false, // stores the menu items..
1363 getChildContainer : function() {
1367 getAutoCreate : function(){
1369 //if (['right'].indexOf(this.align)!==-1) {
1370 // cfg.cn[1].cls += ' pull-right'
1374 cls : 'dropdown-menu' ,
1375 style : 'z-index:1000'
1379 if (this.type === 'submenu') {
1380 cfg.cls = 'submenu active'
1385 initEvents : function() {
1387 // Roo.log("ADD event");
1388 // Roo.log(this.triggerEl.dom);
1389 this.triggerEl.on('click', this.onTriggerPress, this);
1390 this.triggerEl.addClass('dropdown-toggle');
1391 this.el.on(Roo.isTouch ? 'touchstart' : 'click' , this.onClick, this);
1393 this.el.on("mouseover", this.onMouseOver, this);
1394 this.el.on("mouseout", this.onMouseOut, this);
1398 findTargetItem : function(e){
1399 var t = e.getTarget(".dropdown-menu-item", this.el, true);
1403 //Roo.log(t); Roo.log(t.id);
1405 //Roo.log(this.menuitems);
1406 return this.menuitems.get(t.id);
1408 //return this.items.get(t.menuItemId);
1413 onClick : function(e){
1414 Roo.log("menu.onClick");
1415 var t = this.findTargetItem(e);
1421 if (Roo.isTouch && e.type == 'touchstart' && t.menu && !t.disabled) {
1422 if(t == this.activeItem && t.shouldDeactivate(e)){
1423 this.activeItem.deactivate();
1424 delete this.activeItem;
1428 this.setActiveItem(t, true);
1435 Roo.log('pass click event');
1439 this.fireEvent("click", this, t, e);
1443 onMouseOver : function(e){
1444 var t = this.findTargetItem(e);
1447 // if(t.canActivate && !t.disabled){
1448 // this.setActiveItem(t, true);
1452 this.fireEvent("mouseover", this, e, t);
1454 isVisible : function(){
1455 return !this.hidden;
1457 onMouseOut : function(e){
1458 var t = this.findTargetItem(e);
1461 // if(t == this.activeItem && t.shouldDeactivate(e)){
1462 // this.activeItem.deactivate();
1463 // delete this.activeItem;
1466 this.fireEvent("mouseout", this, e, t);
1471 * Displays this menu relative to another element
1472 * @param {String/HTMLElement/Roo.Element} element The element to align to
1473 * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
1474 * the element (defaults to this.defaultAlign)
1475 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1477 show : function(el, pos, parentMenu){
1478 this.parentMenu = parentMenu;
1482 this.fireEvent("beforeshow", this);
1483 this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
1486 * Displays this menu at a specific xy position
1487 * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
1488 * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
1490 showAt : function(xy, parentMenu, /* private: */_e){
1491 this.parentMenu = parentMenu;
1496 this.fireEvent("beforeshow", this);
1498 //xy = this.el.adjustForConstraints(xy);
1500 //this.el.setXY(xy);
1502 this.hideMenuItems();
1503 this.hidden = false;
1504 this.triggerEl.addClass('open');
1506 this.fireEvent("show", this);
1512 this.doFocus.defer(50, this);
1516 doFocus : function(){
1518 this.focusEl.focus();
1523 * Hides this menu and optionally all parent menus
1524 * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
1526 hide : function(deep){
1528 this.hideMenuItems();
1529 if(this.el && this.isVisible()){
1530 this.fireEvent("beforehide", this);
1531 if(this.activeItem){
1532 this.activeItem.deactivate();
1533 this.activeItem = null;
1535 this.triggerEl.removeClass('open');;
1537 this.fireEvent("hide", this);
1539 if(deep === true && this.parentMenu){
1540 this.parentMenu.hide(true);
1544 onTriggerPress : function(e)
1547 Roo.log('trigger press');
1548 //Roo.log(e.getTarget());
1549 // Roo.log(this.triggerEl.dom);
1550 if (Roo.get(e.getTarget()).findParent('.dropdown-menu')) {
1553 if (this.isVisible()) {
1557 this.show(this.triggerEl, false, false);
1566 hideMenuItems : function()
1568 //$(backdrop).remove()
1569 Roo.select('.open',true).each(function(aa) {
1571 aa.removeClass('open');
1572 //var parent = getParent($(this))
1573 //var relatedTarget = { relatedTarget: this }
1575 //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
1576 //if (e.isDefaultPrevented()) return
1577 //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
1580 addxtypeChild : function (tree, cntr) {
1581 var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
1583 this.menuitems.add(comp);
1604 * @class Roo.bootstrap.MenuItem
1605 * @extends Roo.bootstrap.Component
1606 * Bootstrap MenuItem class
1607 * @cfg {String} html the menu label
1608 * @cfg {String} href the link
1609 * @cfg {Boolean} preventDefault (true | false) default true
1613 * Create a new MenuItem
1614 * @param {Object} config The config object
1618 Roo.bootstrap.MenuItem = function(config){
1619 Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
1624 * The raw click event for the entire grid.
1625 * @param {Roo.EventObject} e
1631 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component, {
1635 preventDefault: true,
1637 getAutoCreate : function(){
1640 cls: 'dropdown-menu-item',
1650 cfg.cn[0].href = this.href || cfg.cn[0].href ;
1651 cfg.cn[0].html = this.html || cfg.cn[0].html ;
1655 initEvents: function() {
1657 //this.el.select('a').on('click', this.onClick, this);
1660 onClick : function(e)
1662 Roo.log('item on click ');
1663 //if(this.preventDefault){
1664 // e.preventDefault();
1666 //this.parent().hideMenuItems();
1668 this.fireEvent('click', this, e);
1687 * @class Roo.bootstrap.MenuSeparator
1688 * @extends Roo.bootstrap.Component
1689 * Bootstrap MenuSeparator class
1692 * Create a new MenuItem
1693 * @param {Object} config The config object
1697 Roo.bootstrap.MenuSeparator = function(config){
1698 Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
1701 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component, {
1703 getAutoCreate : function(){
1718 <div class="modal fade">
1719 <div class="modal-dialog">
1720 <div class="modal-content">
1721 <div class="modal-header">
1722 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
1723 <h4 class="modal-title">Modal title</h4>
1725 <div class="modal-body">
1726 <p>One fine body…</p>
1728 <div class="modal-footer">
1729 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
1730 <button type="button" class="btn btn-primary">Save changes</button>
1732 </div><!-- /.modal-content -->
1733 </div><!-- /.modal-dialog -->
1734 </div><!-- /.modal -->
1744 * @class Roo.bootstrap.Modal
1745 * @extends Roo.bootstrap.Component
1746 * Bootstrap Modal class
1747 * @cfg {String} title Title of dialog
1748 * @cfg {Array} buttons Array of buttons or standard button set..
1751 * Create a new Modal Dialog
1752 * @param {Object} config The config object
1755 Roo.bootstrap.Modal = function(config){
1756 Roo.bootstrap.Modal.superclass.constructor.call(this, config);
1761 * The raw btnclick event for the button
1762 * @param {Roo.EventObject} e
1768 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component, {
1770 title : 'test dialog',
1774 onRender : function(ct, position)
1776 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
1779 var cfg = Roo.apply({}, this.getAutoCreate());
1782 // cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
1784 //if (!cfg.name.length) {
1788 cfg.cls += ' ' + this.cls;
1791 cfg.style = this.style;
1793 this.el = Roo.get(document.body).createChild(cfg, position);
1795 //var type = this.el.dom.type;
1797 if(this.tabIndex !== undefined){
1798 this.el.dom.setAttribute('tabIndex', this.tabIndex);
1803 this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
1804 this.maskEl.enableDisplayMode("block");
1806 //this.el.addClass("x-dlg-modal");
1809 Roo.each(this.buttons, function(bb) {
1810 b = Roo.apply({}, bb);
1811 b.xns = b.xns || Roo.bootstrap;
1812 b.xtype = b.xtype || 'Button';
1813 if (typeof(b.listeners) == 'undefined') {
1814 b.listeners = { click : this.onButtonClick.createDelegate(this) };
1817 var btn = Roo.factory(b);
1819 btn.onRender(this.el.select('.modal-footer').first());
1823 // render the children.
1826 if(typeof(this.items) != 'undefined'){
1827 var items = this.items;
1830 for(var i =0;i < items.length;i++) {
1831 nitems.push(this.addxtype(Roo.apply({}, items[i])));
1835 this.items = nitems;
1837 //this.el.addClass([this.fieldClass, this.cls]);
1840 getAutoCreate : function(){
1845 html : this.html || ''
1853 cls: "modal-dialog",
1856 cls : "modal-content",
1859 cls : 'modal-header',
1868 cls : 'modal-title',
1876 cls : 'modal-footer'
1892 getChildContainer : function() {
1894 return this.el.select('.modal-body',true).first();
1897 getButtonContainer : function() {
1898 return this.el.select('.modal-footer',true).first();
1901 initEvents : function()
1903 this.el.select('.modal-header .close').on('click', this.hide, this);
1905 // this.addxtype(this);
1909 if (!this.rendered) {
1913 this.el.addClass('on');
1914 this.el.removeClass('fade');
1915 this.el.setStyle('display', 'block');
1916 Roo.get(document.body).addClass("x-body-masked");
1917 this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
1919 this.el.setStyle('zIndex', '10001');
1920 this.fireEvent('show', this);
1926 Roo.log('Modal hide?!');
1928 Roo.get(document.body).removeClass("x-body-masked");
1929 this.el.removeClass('on');
1930 this.el.addClass('fade');
1931 this.el.setStyle('display', 'none');
1932 this.fireEvent('hide', this);
1934 onButtonClick: function(btn,e)
1937 this.fireEvent('btnclick', btn.name, e);
1942 Roo.apply(Roo.bootstrap.Modal, {
1944 * Button config that displays a single OK button
1953 * Button config that displays Yes and No buttons
1969 * Button config that displays OK and Cancel buttons
1984 * Button config that displays Yes, No and Cancel buttons
2011 * @class Roo.bootstrap.Navbar
2012 * @extends Roo.bootstrap.Component
2013 * Bootstrap Navbar class
2014 * @cfg {Boolean} sidebar has side bar
2015 * @cfg {Boolean} bar is a bar?
2016 * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
2017 * @cfg {String} brand what is brand
2018 * @cfg {Boolean} inverse is inverted color
2019 * @cfg {String} type (nav | pills | tabs)
2020 * @cfg {Boolean} arrangement stacked | justified
2021 * @cfg {String} align (left | right) alignment
2022 * @cfg {String} brand_href href of the brand
2023 * @cfg {Boolean} main (true|false) main nav bar? default false
2024 * @cfg {Boolean} loadMask (true|false) loadMask on the bar
2028 * Create a new Navbar
2029 * @param {Object} config The config object
2033 Roo.bootstrap.Navbar = function(config){
2034 Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
2037 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component, {
2052 getAutoCreate : function(){
2057 if (this.sidebar === true) {
2065 if (this.bar === true) {
2073 cls: 'navbar-header',
2078 cls: 'navbar-toggle',
2079 'data-toggle': 'collapse',
2084 html: 'Toggle navigation'
2104 cls: 'collapse navbar-collapse'
2109 cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
2111 if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
2112 cfg.cls += ' navbar-' + this.position;
2113 cfg.tag = this.position == 'fixed-bottom' ? 'footer' : 'header';
2116 if (this.brand !== '') {
2119 href: this.brand_href ? this.brand_href : '#',
2120 cls: 'navbar-brand',
2128 cfg.cls += ' main-nav';
2134 } else if (this.bar === false) {
2137 Roo.log('Property \'bar\' in of Navbar must be either true or false')
2147 if (['tabs','pills'].indexOf(this.type)!==-1) {
2148 cfg.cn[0].cls += ' nav-' + this.type
2150 if (this.type!=='nav') {
2151 Roo.log('nav type must be nav/tabs/pills')
2153 cfg.cn[0].cls += ' navbar-nav'
2156 if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
2157 cfg.cn[0].cls += ' nav-' + this.arrangement;
2160 if (this.align === 'right') {
2161 cfg.cn[0].cls += ' navbar-right';
2164 cfg.cls += ' navbar-inverse';
2172 initEvents :function ()
2174 //Roo.log(this.el.select('.navbar-toggle',true));
2175 this.el.select('.navbar-toggle',true).on('click', function() {
2176 // Roo.log('click');
2177 this.el.select('.navbar-collapse',true).toggleClass('in');
2185 this.maskEl = Roo.DomHelper.append(this.el, mark, true);
2187 var size = this.el.getSize();
2188 this.maskEl.setSize(size.width, size.height);
2189 this.maskEl.enableDisplayMode("block");
2198 getChildContainer : function()
2200 if (this.bar === true) {
2201 return this.el.select('.collapse',true).first();
2229 * @class Roo.bootstrap.NavGroup
2230 * @extends Roo.bootstrap.Component
2231 * Bootstrap NavGroup class
2232 * @cfg {String} align left | right
2233 * @cfg {Boolean} inverse false | true
2234 * @cfg {String} type (nav|pills|tab) default nav
2237 * Create a new nav group
2238 * @param {Object} config The config object
2241 Roo.bootstrap.NavGroup = function(config){
2242 Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
2245 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component, {
2252 getAutoCreate : function(){
2253 var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
2260 if (['tabs','pills'].indexOf(this.type)!==-1) {
2261 cfg.cls += ' nav-' + this.type
2263 if (this.type!=='nav') {
2264 Roo.log('nav type must be nav/tabs/pills')
2266 cfg.cls += ' navbar-nav'
2269 if (this.parent().sidebar === true) {
2272 cls: 'dashboard-menu'
2278 if (this.form === true) {
2284 if (this.align === 'right') {
2285 cfg.cls += ' navbar-right';
2287 cfg.cls += ' navbar-left';
2291 if (this.align === 'right') {
2292 cfg.cls += ' navbar-right';
2296 cfg.cls += ' navbar-inverse';
2316 * @class Roo.bootstrap.Navbar.Item
2317 * @extends Roo.bootstrap.Component
2318 * Bootstrap Navbar.Button class
2319 * @cfg {String} href link to
2320 * @cfg {String} html content of button
2321 * @cfg {String} badge text inside badge
2322 * @cfg {String} glyphicon name of glyphicon
2323 * @cfg {String} icon name of font awesome icon
2324 * @cfg {Boolena} active Is item active
2325 * @cfg {Boolean} preventDefault (true | false) default false
2328 * Create a new Navbar Button
2329 * @param {Object} config The config object
2331 Roo.bootstrap.Navbar.Item = function(config){
2332 Roo.bootstrap.Navbar.Item.superclass.constructor.call(this, config);
2337 * The raw click event for the entire grid.
2338 * @param {Roo.EventObject} e
2344 Roo.extend(Roo.bootstrap.Navbar.Item, Roo.bootstrap.Component, {
2353 preventDefault : false,
2355 getAutoCreate : function(){
2357 var cfg = Roo.apply({}, Roo.bootstrap.Navbar.Item.superclass.getAutoCreate.call(this));
2359 if (this.parent().parent().sidebar === true) {
2372 cfg.cn[0].html = this.html;
2376 this.cls += ' active';
2380 cfg.cn[0].cls += ' dropdown-toggle';
2381 cfg.cn[0].html = (cfg.cn[0].html || this.html) + '<span class="glyphicon glyphicon-chevron-down"></span>';
2385 cfg.cn[0].tag = 'a',
2386 cfg.cn[0].href = this.href;
2389 if (this.glyphicon) {
2390 cfg.cn[0].html = '<i class="glyphicon glyphicon-'+this.glyphicon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2394 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2406 cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
2416 if (this.glyphicon) {
2417 if(cfg.html){cfg.html = ' ' + this.html};
2421 cls: 'glyphicon glyphicon-' + this.glyphicon
2426 cfg.cn[0].html = this.html || cfg.cn[0].html ;
2431 cfg.cn[0].html += " <span class='caret'></span>";
2432 //}else if (!this.href) {
2433 // cfg.cn[0].tag='p';
2434 // cfg.cn[0].cls='navbar-text';
2437 cfg.cn[0].href=this.href||'#';
2438 cfg.cn[0].html=this.html;
2441 if (this.badge !== '') {
2444 cfg.cn[0].html + ' ',
2455 cfg.cn[0].html = '<i class="'+this.icon+'"></i><span>' + cfg.cn[0].html || this.html + '</span>'
2460 initEvents: function() {
2461 // Roo.log('init events?');
2462 // Roo.log(this.el.dom);
2463 this.el.select('a',true).on('click', this.onClick, this);
2466 onClick : function(e)
2468 if(this.preventDefault){
2472 if(this.fireEvent('click', this, e) === false){
2476 if (['tabs','pills'].indexOf(this.parent().type)!==-1) {
2477 this.onTabsClick(e);
2481 onTabsClick : function(e)
2483 Roo.each(this.parent().el.select('.active',true).elements, function(v){
2484 v.removeClass('active');
2487 this.el.addClass('active');
2489 if(this.href && this.href.substring(0,1) == '#'){
2490 var tab = Roo.select('[tabId=' + this.href + ']', true).first();
2492 Roo.each(tab.findParent('.tab-content', 0, true).select('.active', true).elements, function(v){
2493 v.removeClass('active');
2496 tab.addClass('active');
2511 * @class Roo.bootstrap.Row
2512 * @extends Roo.bootstrap.Component
2513 * Bootstrap Row class (contains columns...)
2517 * @param {Object} config The config object
2520 Roo.bootstrap.Row = function(config){
2521 Roo.bootstrap.Row.superclass.constructor.call(this, config);
2524 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component, {
2526 getAutoCreate : function(){
2545 * @class Roo.bootstrap.Element
2546 * @extends Roo.bootstrap.Component
2547 * Bootstrap Element class
2548 * @cfg {String} html contents of the element
2549 * @cfg {String} tag tag of the element
2550 * @cfg {String} cls class of the element
2553 * Create a new Element
2554 * @param {Object} config The config object
2557 Roo.bootstrap.Element = function(config){
2558 Roo.bootstrap.Element.superclass.constructor.call(this, config);
2561 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component, {
2568 getAutoCreate : function(){
2593 * @class Roo.bootstrap.Pagination
2594 * @extends Roo.bootstrap.Component
2595 * Bootstrap Pagination class
2596 * @cfg {String} size xs | sm | md | lg
2597 * @cfg {Boolean} inverse false | true
2600 * Create a new Pagination
2601 * @param {Object} config The config object
2604 Roo.bootstrap.Pagination = function(config){
2605 Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
2608 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component, {
2614 getAutoCreate : function(){
2620 cfg.cls += ' inverse';
2626 cfg.cls += " " + this.cls;
2644 * @class Roo.bootstrap.PaginationItem
2645 * @extends Roo.bootstrap.Component
2646 * Bootstrap PaginationItem class
2647 * @cfg {String} html text
2648 * @cfg {String} href the link
2649 * @cfg {Boolean} preventDefault (true | false) default true
2650 * @cfg {Boolean} active (true | false) default false
2654 * Create a new PaginationItem
2655 * @param {Object} config The config object
2659 Roo.bootstrap.PaginationItem = function(config){
2660 Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
2665 * The raw click event for the entire grid.
2666 * @param {Roo.EventObject} e
2672 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component, {
2676 preventDefault: true,
2680 getAutoCreate : function(){
2686 href : this.href ? this.href : '#',
2687 html : this.html ? this.html : ''
2697 cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
2703 initEvents: function() {
2705 this.el.on('click', this.onClick, this);
2708 onClick : function(e)
2710 Roo.log('PaginationItem on click ');
2711 if(this.preventDefault){
2715 this.fireEvent('click', this, e);
2731 * @class Roo.bootstrap.Slider
2732 * @extends Roo.bootstrap.Component
2733 * Bootstrap Slider class
2736 * Create a new Slider
2737 * @param {Object} config The config object
2740 Roo.bootstrap.Slider = function(config){
2741 Roo.bootstrap.Slider.superclass.constructor.call(this, config);
2744 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component, {
2746 getAutoCreate : function(){
2750 cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
2754 cls: 'ui-slider-handle ui-state-default ui-corner-all'
2772 * @class Roo.bootstrap.Table
2773 * @extends Roo.bootstrap.Component
2774 * Bootstrap Table class
2775 * @cfg {String} cls table class
2776 * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
2777 * @cfg {String} bgcolor Specifies the background color for a table
2778 * @cfg {Number} border Specifies whether the table cells should have borders or not
2779 * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
2780 * @cfg {Number} cellspacing Specifies the space between cells
2781 * @cfg {String} frame Specifies which parts of the outside borders that should be visible
2782 * @cfg {String} rules Specifies which parts of the inside borders that should be visible
2783 * @cfg {String} sortable Specifies that the table should be sortable
2784 * @cfg {String} summary Specifies a summary of the content of a table
2785 * @cfg {Number} width Specifies the width of a table
2787 * @cfg {boolean} striped Should the rows be alternative striped
2788 * @cfg {boolean} bordered Add borders to the table
2789 * @cfg {boolean} hover Add hover highlighting
2790 * @cfg {boolean} condensed Format condensed
2791 * @cfg {boolean} responsive Format condensed
2797 * Create a new Table
2798 * @param {Object} config The config object
2801 Roo.bootstrap.Table = function(config){
2802 Roo.bootstrap.Table.superclass.constructor.call(this, config);
2805 this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
2806 this.sm = this.selModel;
2807 this.sm.xmodule = this.xmodule || false;
2809 if (this.cm && typeof(this.cm.config) == 'undefined') {
2810 this.colModel = new Roo.bootstrap.Table.ColumnModel(this.cm);
2811 this.cm = this.colModel;
2812 this.cm.xmodule = this.xmodule || false;
2815 this.store= Roo.factory(this.store, Roo.data);
2816 this.ds = this.store;
2817 this.ds.xmodule = this.xmodule || false;
2822 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component, {
2844 getAutoCreate : function(){
2845 var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
2854 cfg.cls += ' table-striped';
2857 cfg.cls += ' table-hover';
2859 if (this.bordered) {
2860 cfg.cls += ' table-bordered';
2862 if (this.condensed) {
2863 cfg.cls += ' table-condensed';
2865 if (this.responsive) {
2866 cfg.cls += ' table-responsive';
2873 cfg.cls+= ' ' +this.cls;
2876 // this lot should be simplifed...
2879 cfg.align=this.align;
2882 cfg.bgcolor=this.bgcolor;
2885 cfg.border=this.border;
2887 if (this.cellpadding) {
2888 cfg.cellpadding=this.cellpadding;
2890 if (this.cellspacing) {
2891 cfg.cellspacing=this.cellspacing;
2894 cfg.frame=this.frame;
2897 cfg.rules=this.rules;
2899 if (this.sortable) {
2900 cfg.sortable=this.sortable;
2903 cfg.summary=this.summary;
2906 cfg.width=this.width;
2909 if(this.store || this.cm){
2910 cfg.cn.push(this.renderHeader());
2911 cfg.cn.push(this.renderBody());
2912 cfg.cn.push(this.renderFooter());
2914 cfg.cls+= ' TableGrid';
2920 // initTableGrid : function()
2929 // var cm = this.cm;
2931 // for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2934 // html: cm.getColumnHeader(i)
2938 // cfg.push(header);
2945 initEvents : function()
2947 if(!this.store || !this.cm){
2951 Roo.log('initEvents with ds!!!!');
2953 Roo.each(this.el.select('thead th.sortable').elements, function(e){
2956 // this.maskEl = Roo.DomHelper.append(this.el.select('.TableGrid', true).first(), {tag: "div", cls:"x-dlg-mask"}, true);
2957 // this.maskEl.enableDisplayMode("block");
2958 // this.maskEl.show();
2960 this.store.on('load', this.onLoad, this);
2961 this.store.on('beforeload', this.onBeforeLoad, this);
2969 renderHeader : function()
2978 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
2980 var config = cm.config[i];
2984 html: cm.getColumnHeader(i)
2987 if(typeof(config.dataIndex) != 'undefined'){
2988 c.sort = config.dataIndex;
2991 if(typeof(config.sortable) != 'undefined' && config.sortable){
3001 renderBody : function()
3011 renderFooter : function()
3023 Roo.log('ds onload');
3027 var tbody = this.el.select('tbody', true).first();
3031 if(this.store.getCount() > 0){
3032 this.store.data.each(function(d){
3038 for(var i = 0, len = cm.getColumnCount(); i < len; i++){
3039 var renderer = cm.getRenderer(i);
3040 var config = cm.config[i];
3044 if(typeof(renderer) !== 'undefined'){
3045 value = renderer(d.data[cm.getDataIndex(i)], false, d);
3048 if(typeof(value) === 'object'){
3058 html: (typeof(value) === 'object') ? '' : value
3061 if(typeof(config.width) != 'undefined'){
3062 td.width = config.width;
3069 tbody.createChild(row);
3077 Roo.each(renders, function(r){
3078 _this.renderColumn(r);
3082 // if(this.loadMask){
3083 // this.maskEl.hide();
3087 onBeforeLoad : function()
3089 Roo.log('ds onBeforeLoad');
3093 // if(this.loadMask){
3094 // this.maskEl.show();
3100 this.el.select('tbody', true).first().dom.innerHTML = '';
3103 getSelectionModel : function(){
3105 this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
3107 return this.selModel;
3110 renderColumn : function(r)
3113 r.cfg.render(Roo.get(r.id));
3116 Roo.each(r.cfg.cn, function(c){
3121 _this.renderColumn(child);
3138 * @class Roo.bootstrap.TableCell
3139 * @extends Roo.bootstrap.Component
3140 * Bootstrap TableCell class
3141 * @cfg {String} html cell contain text
3142 * @cfg {String} cls cell class
3143 * @cfg {String} tag cell tag (td|th) default td
3144 * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
3145 * @cfg {String} align Aligns the content in a cell
3146 * @cfg {String} axis Categorizes cells
3147 * @cfg {String} bgcolor Specifies the background color of a cell
3148 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3149 * @cfg {Number} colspan Specifies the number of columns a cell should span
3150 * @cfg {String} headers Specifies one or more header cells a cell is related to
3151 * @cfg {Number} height Sets the height of a cell
3152 * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
3153 * @cfg {Number} rowspan Sets the number of rows a cell should span
3154 * @cfg {String} scope Defines a way to associate header cells and data cells in a table
3155 * @cfg {String} valign Vertical aligns the content in a cell
3156 * @cfg {Number} width Specifies the width of a cell
3159 * Create a new TableCell
3160 * @param {Object} config The config object
3163 Roo.bootstrap.TableCell = function(config){
3164 Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
3167 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component, {
3187 getAutoCreate : function(){
3188 var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
3208 cfg.align=this.align
3214 cfg.bgcolor=this.bgcolor
3217 cfg.charoff=this.charoff
3220 cfg.colspan=this.colspan
3223 cfg.headers=this.headers
3226 cfg.height=this.height
3229 cfg.nowrap=this.nowrap
3232 cfg.rowspan=this.rowspan
3235 cfg.scope=this.scope
3238 cfg.valign=this.valign
3241 cfg.width=this.width
3260 * @class Roo.bootstrap.TableRow
3261 * @extends Roo.bootstrap.Component
3262 * Bootstrap TableRow class
3263 * @cfg {String} cls row class
3264 * @cfg {String} align Aligns the content in a table row
3265 * @cfg {String} bgcolor Specifies a background color for a table row
3266 * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
3267 * @cfg {String} valign Vertical aligns the content in a table row
3270 * Create a new TableRow
3271 * @param {Object} config The config object
3274 Roo.bootstrap.TableRow = function(config){
3275 Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
3278 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component, {
3286 getAutoCreate : function(){
3287 var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
3297 cfg.align = this.align;
3300 cfg.bgcolor = this.bgcolor;
3303 cfg.charoff = this.charoff;
3306 cfg.valign = this.valign;
3324 * @class Roo.bootstrap.TableBody
3325 * @extends Roo.bootstrap.Component
3326 * Bootstrap TableBody class
3327 * @cfg {String} cls element class
3328 * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
3329 * @cfg {String} align Aligns the content inside the element
3330 * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
3331 * @cfg {String} valign Vertical aligns the content inside the <tbody> element
3334 * Create a new TableBody
3335 * @param {Object} config The config object
3338 Roo.bootstrap.TableBody = function(config){
3339 Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
3342 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component, {
3350 getAutoCreate : function(){
3351 var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
3365 cfg.align = this.align;
3368 cfg.charoff = this.charoff;
3371 cfg.valign = this.valign;
3378 // initEvents : function()
3385 // this.store = Roo.factory(this.store, Roo.data);
3386 // this.store.on('load', this.onLoad, this);
3388 // this.store.load();
3392 // onLoad: function ()
3394 // this.fireEvent('load', this);
3404 * Ext JS Library 1.1.1
3405 * Copyright(c) 2006-2007, Ext JS, LLC.
3407 * Originally Released Under LGPL - original licence link has changed is not relivant.
3410 * <script type="text/javascript">
3413 // as we use this in bootstrap.
3414 Roo.namespace('Roo.form');
3416 * @class Roo.form.Action
3417 * Internal Class used to handle form actions
3419 * @param {Roo.form.BasicForm} el The form element or its id
3420 * @param {Object} config Configuration options
3425 // define the action interface
3426 Roo.form.Action = function(form, options){
3428 this.options = options || {};
3431 * Client Validation Failed
3434 Roo.form.Action.CLIENT_INVALID = 'client';
3436 * Server Validation Failed
3439 Roo.form.Action.SERVER_INVALID = 'server';
3441 * Connect to Server Failed
3444 Roo.form.Action.CONNECT_FAILURE = 'connect';
3446 * Reading Data from Server Failed
3449 Roo.form.Action.LOAD_FAILURE = 'load';
3451 Roo.form.Action.prototype = {
3453 failureType : undefined,
3454 response : undefined,
3458 run : function(options){
3463 success : function(response){
3468 handleResponse : function(response){
3472 // default connection failure
3473 failure : function(response){
3475 this.response = response;
3476 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3477 this.form.afterAction(this, false);
3480 processResponse : function(response){
3481 this.response = response;
3482 if(!response.responseText){
3485 this.result = this.handleResponse(response);
3489 // utility functions used internally
3490 getUrl : function(appendParams){
3491 var url = this.options.url || this.form.url || this.form.el.dom.action;
3493 var p = this.getParams();
3495 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
3501 getMethod : function(){
3502 return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
3505 getParams : function(){
3506 var bp = this.form.baseParams;
3507 var p = this.options.params;
3509 if(typeof p == "object"){
3510 p = Roo.urlEncode(Roo.applyIf(p, bp));
3511 }else if(typeof p == 'string' && bp){
3512 p += '&' + Roo.urlEncode(bp);
3515 p = Roo.urlEncode(bp);
3520 createCallback : function(){
3522 success: this.success,
3523 failure: this.failure,
3525 timeout: (this.form.timeout*1000),
3526 upload: this.form.fileUpload ? this.success : undefined
3531 Roo.form.Action.Submit = function(form, options){
3532 Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
3535 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
3538 haveProgress : false,
3539 uploadComplete : false,
3541 // uploadProgress indicator.
3542 uploadProgress : function()
3544 if (!this.form.progressUrl) {
3548 if (!this.haveProgress) {
3549 Roo.MessageBox.progress("Uploading", "Uploading");
3551 if (this.uploadComplete) {
3552 Roo.MessageBox.hide();
3556 this.haveProgress = true;
3558 var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
3560 var c = new Roo.data.Connection();
3562 url : this.form.progressUrl,
3567 success : function(req){
3568 //console.log(data);
3572 rdata = Roo.decode(req.responseText)
3574 Roo.log("Invalid data from server..");
3578 if (!rdata || !rdata.success) {
3580 Roo.MessageBox.alert(Roo.encode(rdata));
3583 var data = rdata.data;
3585 if (this.uploadComplete) {
3586 Roo.MessageBox.hide();
3591 Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
3592 Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
3595 this.uploadProgress.defer(2000,this);
3598 failure: function(data) {
3599 Roo.log('progress url failed ');
3610 // run get Values on the form, so it syncs any secondary forms.
3611 this.form.getValues();
3613 var o = this.options;
3614 var method = this.getMethod();
3615 var isPost = method == 'POST';
3616 if(o.clientValidation === false || this.form.isValid()){
3618 if (this.form.progressUrl) {
3619 this.form.findField('UPLOAD_IDENTIFIER').setValue(
3620 (new Date() * 1) + '' + Math.random());
3625 Roo.Ajax.request(Roo.apply(this.createCallback(), {
3626 form:this.form.el.dom,
3627 url:this.getUrl(!isPost),
3629 params:isPost ? this.getParams() : null,
3630 isUpload: this.form.fileUpload
3633 this.uploadProgress();
3635 }else if (o.clientValidation !== false){ // client validation failed
3636 this.failureType = Roo.form.Action.CLIENT_INVALID;
3637 this.form.afterAction(this, false);
3641 success : function(response)
3643 this.uploadComplete= true;
3644 if (this.haveProgress) {
3645 Roo.MessageBox.hide();
3649 var result = this.processResponse(response);
3650 if(result === true || result.success){
3651 this.form.afterAction(this, true);
3655 this.form.markInvalid(result.errors);
3656 this.failureType = Roo.form.Action.SERVER_INVALID;
3658 this.form.afterAction(this, false);
3660 failure : function(response)
3662 this.uploadComplete= true;
3663 if (this.haveProgress) {
3664 Roo.MessageBox.hide();
3667 this.response = response;
3668 this.failureType = Roo.form.Action.CONNECT_FAILURE;
3669 this.form.afterAction(this, false);
3672 handleResponse : function(response){
3673 if(this.form.errorReader){
3674 var rs = this.form.errorReader.read(response);
3677 for(var i = 0, len = rs.records.length; i < len; i++) {
3678 var r = rs.records[i];
3682 if(errors.length < 1){
3686 success : rs.success,
3692 ret = Roo.decode(response.responseText);
3696 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
3706 Roo.form.Action.Load = function(form, options){
3707 Roo.form.Action.Load.superclass.constructor.call(this, form, options);
3708 this.reader = this.form.reader;
3711 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
3716 Roo.Ajax.request(Roo.apply(
3717 this.createCallback(), {
3718 method:this.getMethod(),
3719 url:this.getUrl(false),
3720 params:this.getParams()
3724 success : function(response){
3726 var result = this.processResponse(response);
3727 if(result === true || !result.success || !result.data){
3728 this.failureType = Roo.form.Action.LOAD_FAILURE;
3729 this.form.afterAction(this, false);
3732 this.form.clearInvalid();
3733 this.form.setValues(result.data);
3734 this.form.afterAction(this, true);
3737 handleResponse : function(response){
3738 if(this.form.reader){
3739 var rs = this.form.reader.read(response);
3740 var data = rs.records && rs.records[0] ? rs.records[0].data : null;
3742 success : rs.success,
3746 return Roo.decode(response.responseText);
3750 Roo.form.Action.ACTION_TYPES = {
3751 'load' : Roo.form.Action.Load,
3752 'submit' : Roo.form.Action.Submit
3761 * @class Roo.bootstrap.Form
3762 * @extends Roo.bootstrap.Component
3763 * Bootstrap Form class
3764 * @cfg {String} method GET | POST (default POST)
3765 * @cfg {String} labelAlign top | left (default top)
3766 * @cfg {String} align left | right - for navbars
3771 * @param {Object} config The config object
3775 Roo.bootstrap.Form = function(config){
3776 Roo.bootstrap.Form.superclass.constructor.call(this, config);
3779 * @event clientvalidation
3780 * If the monitorValid config option is true, this event fires repetitively to notify of valid state
3781 * @param {Form} this
3782 * @param {Boolean} valid true if the form has passed client-side validation
3784 clientvalidation: true,
3786 * @event beforeaction
3787 * Fires before any action is performed. Return false to cancel the action.
3788 * @param {Form} this
3789 * @param {Action} action The action to be performed
3793 * @event actionfailed
3794 * Fires when an action fails.
3795 * @param {Form} this
3796 * @param {Action} action The action that failed
3798 actionfailed : true,
3800 * @event actioncomplete
3801 * Fires when an action is completed.
3802 * @param {Form} this
3803 * @param {Action} action The action that completed
3805 actioncomplete : true
3810 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component, {
3813 * @cfg {String} method
3814 * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
3819 * The URL to use for form actions if one isn't supplied in the action options.
3822 * @cfg {Boolean} fileUpload
3823 * Set to true if this form is a file upload.
3827 * @cfg {Object} baseParams
3828 * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
3832 * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
3836 * @cfg {Sting} align (left|right) for navbar forms
3841 activeAction : null,
3844 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3845 * element by passing it or its id or mask the form itself by passing in true.
3848 waitMsgTarget : false,
3853 * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
3854 * element by passing it or its id or mask the form itself by passing in true.
3858 getAutoCreate : function(){
3862 method : this.method || 'POST',
3863 id : this.id || Roo.id(),
3866 if (this.parent().xtype.match(/^Nav/)) {
3867 cfg.cls = 'navbar-form navbar-' + this.align;
3871 if (this.labelAlign == 'left' ) {
3872 cfg.cls += ' form-horizontal';
3878 initEvents : function()
3880 this.el.on('submit', this.onSubmit, this);
3885 onSubmit : function(e){
3890 * Returns true if client-side validation on the form is successful.
3893 isValid : function(){
3894 var items = this.getItems();
3896 items.each(function(f){
3905 * Returns true if any fields in this form have changed since their original load.
3908 isDirty : function(){
3910 var items = this.getItems();
3911 items.each(function(f){
3921 * Performs a predefined action (submit or load) or custom actions you define on this form.
3922 * @param {String} actionName The name of the action type
3923 * @param {Object} options (optional) The options to pass to the action. All of the config options listed
3924 * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
3925 * accept other config options):
3927 Property Type Description
3928 ---------------- --------------- ----------------------------------------------------------------------------------
3929 url String The url for the action (defaults to the form's url)
3930 method String The form method to use (defaults to the form's method, or POST if not defined)
3931 params String/Object The params to pass (defaults to the form's baseParams, or none if not defined)
3932 clientValidation Boolean Applies to submit only. Pass true to call form.isValid() prior to posting to
3933 validate the form on the client (defaults to false)
3935 * @return {BasicForm} this
3937 doAction : function(action, options){
3938 if(typeof action == 'string'){
3939 action = new Roo.form.Action.ACTION_TYPES[action](this, options);
3941 if(this.fireEvent('beforeaction', this, action) !== false){
3942 this.beforeAction(action);
3943 action.run.defer(100, action);
3949 beforeAction : function(action){
3950 var o = action.options;
3952 // not really supported yet.. ??
3954 //if(this.waitMsgTarget === true){
3955 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
3956 //}else if(this.waitMsgTarget){
3957 // this.waitMsgTarget = Roo.get(this.waitMsgTarget);
3958 // this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
3960 // Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
3966 afterAction : function(action, success){
3967 this.activeAction = null;
3968 var o = action.options;
3970 //if(this.waitMsgTarget === true){
3972 //}else if(this.waitMsgTarget){
3973 // this.waitMsgTarget.unmask();
3975 // Roo.MessageBox.updateProgress(1);
3976 // Roo.MessageBox.hide();
3983 Roo.callback(o.success, o.scope, [this, action]);
3984 this.fireEvent('actioncomplete', this, action);
3988 // failure condition..
3989 // we have a scenario where updates need confirming.
3990 // eg. if a locking scenario exists..
3991 // we look for { errors : { needs_confirm : true }} in the response.
3993 (typeof(action.result) != 'undefined') &&
3994 (typeof(action.result.errors) != 'undefined') &&
3995 (typeof(action.result.errors.needs_confirm) != 'undefined')
3998 Roo.log("not supported yet");
4001 Roo.MessageBox.confirm(
4002 "Change requires confirmation",
4003 action.result.errorMsg,
4008 _t.doAction('submit', { params : { _submit_confirmed : 1 } } );
4018 Roo.callback(o.failure, o.scope, [this, action]);
4019 // show an error message if no failed handler is set..
4020 if (!this.hasListener('actionfailed')) {
4021 Roo.log("need to add dialog support");
4023 Roo.MessageBox.alert("Error",
4024 (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
4025 action.result.errorMsg :
4026 "Saving Failed, please check your entries or try again"
4031 this.fireEvent('actionfailed', this, action);
4036 * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
4037 * @param {String} id The value to search for
4040 findField : function(id){
4041 var items = this.getItems();
4042 var field = items.get(id);
4044 items.each(function(f){
4045 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
4052 return field || null;
4055 * Mark fields in this form invalid in bulk.
4056 * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
4057 * @return {BasicForm} this
4059 markInvalid : function(errors){
4060 if(errors instanceof Array){
4061 for(var i = 0, len = errors.length; i < len; i++){
4062 var fieldError = errors[i];
4063 var f = this.findField(fieldError.id);
4065 f.markInvalid(fieldError.msg);
4071 if(typeof errors[id] != 'function' && (field = this.findField(id))){
4072 field.markInvalid(errors[id]);
4076 //Roo.each(this.childForms || [], function (f) {
4077 // f.markInvalid(errors);
4084 * Set values for fields in this form in bulk.
4085 * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
4086 * @return {BasicForm} this
4088 setValues : function(values){
4089 if(values instanceof Array){ // array of objects
4090 for(var i = 0, len = values.length; i < len; i++){
4092 var f = this.findField(v.id);
4094 f.setValue(v.value);
4095 if(this.trackResetOnLoad){
4096 f.originalValue = f.getValue();
4100 }else{ // object hash
4103 if(typeof values[id] != 'function' && (field = this.findField(id))){
4105 if (field.setFromData &&
4107 field.displayField &&
4108 // combos' with local stores can
4109 // be queried via setValue()
4110 // to set their value..
4111 (field.store && !field.store.isLocal)
4115 sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
4116 sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
4117 field.setFromData(sd);
4120 field.setValue(values[id]);
4124 if(this.trackResetOnLoad){
4125 field.originalValue = field.getValue();
4131 //Roo.each(this.childForms || [], function (f) {
4132 // f.setValues(values);
4139 * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
4140 * they are returned as an array.
4141 * @param {Boolean} asString
4144 getValues : function(asString){
4145 //if (this.childForms) {
4146 // copy values from the child forms
4147 // Roo.each(this.childForms, function (f) {
4148 // this.setValues(f.getValues());
4154 var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
4155 if(asString === true){
4158 return Roo.urlDecode(fs);
4162 * Returns the fields in this form as an object with key/value pairs.
4163 * This differs from getValues as it calls getValue on each child item, rather than using dom data.
4166 getFieldValues : function(with_hidden)
4168 var items = this.getItems();
4170 items.each(function(f){
4174 var v = f.getValue();
4175 if (f.inputType =='radio') {
4176 if (typeof(ret[f.getName()]) == 'undefined') {
4177 ret[f.getName()] = ''; // empty..
4180 if (!f.el.dom.checked) {
4188 // not sure if this supported any more..
4189 if ((typeof(v) == 'object') && f.getRawValue) {
4190 v = f.getRawValue() ; // dates..
4192 // combo boxes where name != hiddenName...
4193 if (f.name != f.getName()) {
4194 ret[f.name] = f.getRawValue();
4196 ret[f.getName()] = v;
4203 * Clears all invalid messages in this form.
4204 * @return {BasicForm} this
4206 clearInvalid : function(){
4207 var items = this.getItems();
4209 items.each(function(f){
4220 * @return {BasicForm} this
4223 var items = this.getItems();
4224 items.each(function(f){
4228 Roo.each(this.childForms || [], function (f) {
4235 getItems : function()
4237 var r=new Roo.util.MixedCollection(false, function(o){
4238 return o.id || (o.id = Roo.id());
4240 var iter = function(el) {
4247 Roo.each(el.items,function(e) {
4266 * Ext JS Library 1.1.1
4267 * Copyright(c) 2006-2007, Ext JS, LLC.
4269 * Originally Released Under LGPL - original licence link has changed is not relivant.
4272 * <script type="text/javascript">
4275 * @class Roo.form.VTypes
4276 * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
4279 Roo.form.VTypes = function(){
4280 // closure these in so they are only created once.
4281 var alpha = /^[a-zA-Z_]+$/;
4282 var alphanum = /^[a-zA-Z0-9_]+$/;
4283 var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
4284 var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
4286 // All these messages and functions are configurable
4289 * The function used to validate email addresses
4290 * @param {String} value The email address
4292 'email' : function(v){
4293 return email.test(v);
4296 * The error text to display when the email validation function returns false
4299 'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
4301 * The keystroke filter mask to be applied on email input
4304 'emailMask' : /[a-z0-9_\.\-@]/i,
4307 * The function used to validate URLs
4308 * @param {String} value The URL
4310 'url' : function(v){
4314 * The error text to display when the url validation function returns false
4317 'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
4320 * The function used to validate alpha values
4321 * @param {String} value The value
4323 'alpha' : function(v){
4324 return alpha.test(v);
4327 * The error text to display when the alpha validation function returns false
4330 'alphaText' : 'This field should only contain letters and _',
4332 * The keystroke filter mask to be applied on alpha input
4335 'alphaMask' : /[a-z_]/i,
4338 * The function used to validate alphanumeric values
4339 * @param {String} value The value
4341 'alphanum' : function(v){
4342 return alphanum.test(v);
4345 * The error text to display when the alphanumeric validation function returns false
4348 'alphanumText' : 'This field should only contain letters, numbers and _',
4350 * The keystroke filter mask to be applied on alphanumeric input
4353 'alphanumMask' : /[a-z0-9_]/i
4363 * @class Roo.bootstrap.Input
4364 * @extends Roo.bootstrap.Component
4365 * Bootstrap Input class
4366 * @cfg {Boolean} disabled is it disabled
4367 * @cfg {String} fieldLabel - the label associated
4368 * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
4369 * @cfg {String} name name of the input
4370 * @cfg {string} fieldLabel - the label associated
4371 * @cfg {string} inputType - input / file submit ...
4372 * @cfg {string} placeholder - placeholder to put in text.
4373 * @cfg {string} before - input group add on before
4374 * @cfg {string} after - input group add on after
4375 * @cfg {string} size - (lg|sm) or leave empty..
4376 * @cfg {Number} xs colspan out of 12 for mobile-sized screens
4377 * @cfg {Number} sm colspan out of 12 for tablet-sized screens
4378 * @cfg {Number} md colspan out of 12 for computer-sized screens
4379 * @cfg {Number} lg colspan out of 12 for large computer-sized screens
4380 * @cfg {string} value default value of the input
4381 * @cfg {Number} labelWidth set the width of label (0-12)
4382 * @cfg {String} labelAlign (top|left)
4383 * @cfg {Boolean} readOnly Specifies that the field should be read-only
4387 * Create a new Input
4388 * @param {Object} config The config object
4391 Roo.bootstrap.Input = function(config){
4392 Roo.bootstrap.Input.superclass.constructor.call(this, config);
4397 * Fires when this field receives input focus.
4398 * @param {Roo.form.Field} this
4403 * Fires when this field loses input focus.
4404 * @param {Roo.form.Field} this
4409 * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed. You can check
4410 * {@link Roo.EventObject#getKey} to determine which key was pressed.
4411 * @param {Roo.form.Field} this
4412 * @param {Roo.EventObject} e The event object
4417 * Fires just before the field blurs if the field value has changed.
4418 * @param {Roo.form.Field} this
4419 * @param {Mixed} newValue The new value
4420 * @param {Mixed} oldValue The original value
4425 * Fires after the field has been marked as invalid.
4426 * @param {Roo.form.Field} this
4427 * @param {String} msg The validation message
4432 * Fires after the field has been validated with no errors.
4433 * @param {Roo.form.Field} this
4438 * Fires after the key up
4439 * @param {Roo.form.Field} this
4440 * @param {Roo.EventObject} e The event Object
4446 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component, {
4448 * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
4449 automatic validation (defaults to "keyup").
4451 validationEvent : "keyup",
4453 * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
4455 validateOnBlur : true,
4457 * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
4459 validationDelay : 250,
4461 * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
4463 focusClass : "x-form-focus", // not needed???
4467 * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
4469 invalidClass : "has-error",
4472 * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
4474 selectOnFocus : false,
4477 * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
4481 * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
4486 * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
4488 disableKeyFilter : false,
4491 * @cfg {Boolean} disabled True to disable the field (defaults to false).
4495 * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
4499 * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
4501 blankText : "This field is required",
4504 * @cfg {Number} minLength Minimum input field length required (defaults to 0)
4508 * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
4510 maxLength : Number.MAX_VALUE,
4512 * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
4514 minLengthText : "The minimum length for this field is {0}",
4516 * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
4518 maxLengthText : "The maximum length for this field is {0}",
4522 * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
4523 * If available, this function will be called only after the basic validators all return true, and will be passed the
4524 * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
4528 * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
4529 * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
4530 * current field value. If the test fails, the field will be marked invalid using {@link #regexText}.
4534 * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
4557 parentLabelAlign : function()
4560 while (parent.parent()) {
4561 parent = parent.parent();
4562 if (typeof(parent.labelAlign) !='undefined') {
4563 return parent.labelAlign;
4570 getAutoCreate : function(){
4572 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
4578 if(this.inputType != 'hidden'){
4579 cfg.cls = 'form-group' //input-group
4585 type : this.inputType,
4587 cls : 'form-control',
4588 placeholder : this.placeholder || ''
4592 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
4593 input.maxLength = this.maxLength;
4596 if (this.disabled) {
4597 input.disabled=true;
4600 if (this.readOnly) {
4601 input.readonly=true;
4605 input.name = this.name;
4608 input.cls += ' input-' + this.size;
4611 ['xs','sm','md','lg'].map(function(size){
4612 if (settings[size]) {
4613 cfg.cls += ' col-' + size + '-' + settings[size];
4617 var inputblock = input;
4619 if (this.before || this.after) {
4622 cls : 'input-group',
4626 inputblock.cn.push({
4628 cls : 'input-group-addon',
4632 inputblock.cn.push(input);
4634 inputblock.cn.push({
4636 cls : 'input-group-addon',
4643 if (align ==='left' && this.fieldLabel.length) {
4644 Roo.log("left and has label");
4650 cls : 'control-label col-sm-' + this.labelWidth,
4651 html : this.fieldLabel
4655 cls : "col-sm-" + (12 - this.labelWidth),
4662 } else if ( this.fieldLabel.length) {
4668 //cls : 'input-group-addon',
4669 html : this.fieldLabel
4679 Roo.log(" no label && no align");
4688 Roo.log('input-parentType: ' + this.parentType);
4690 if (this.parentType === 'Navbar' && this.parent().bar) {
4691 cfg.cls += ' navbar-form';
4699 * return the real input element.
4701 inputEl: function ()
4703 return this.el.select('input.form-control',true).first();
4705 setDisabled : function(v)
4707 var i = this.inputEl().dom;
4709 i.removeAttribute('disabled');
4713 i.setAttribute('disabled','true');
4715 initEvents : function()
4718 this.inputEl().on("keydown" , this.fireKey, this);
4719 this.inputEl().on("focus", this.onFocus, this);
4720 this.inputEl().on("blur", this.onBlur, this);
4722 this.inputEl().relayEvent('keyup', this);
4724 // reference to original value for reset
4725 this.originalValue = this.getValue();
4726 //Roo.form.TextField.superclass.initEvents.call(this);
4727 if(this.validationEvent == 'keyup'){
4728 this.validationTask = new Roo.util.DelayedTask(this.validate, this);
4729 this.inputEl().on('keyup', this.filterValidation, this);
4731 else if(this.validationEvent !== false){
4732 this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
4735 if(this.selectOnFocus){
4736 this.on("focus", this.preFocus, this);
4739 if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
4740 this.inputEl().on("keypress", this.filterKeys, this);
4743 this.el.on("keyup", this.onKeyUp, this, {buffer:50});
4744 this.el.on("click", this.autoSize, this);
4747 if(this.inputEl().is('input[type=password]') && Roo.isSafari){
4748 this.inputEl().on('keydown', this.SafariOnKeyDown, this);
4752 filterValidation : function(e){
4753 if(!e.isNavKeyPress()){
4754 this.validationTask.delay(this.validationDelay);
4758 * Validates the field value
4759 * @return {Boolean} True if the value is valid, else false
4761 validate : function(){
4762 //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
4763 if(this.disabled || this.validateValue(this.getRawValue())){
4764 this.clearInvalid();
4772 * Validates a value according to the field's validation rules and marks the field as invalid
4773 * if the validation fails
4774 * @param {Mixed} value The value to validate
4775 * @return {Boolean} True if the value is valid, else false
4777 validateValue : function(value){
4778 if(value.length < 1) { // if it's blank
4779 if(this.allowBlank){
4780 this.clearInvalid();
4783 this.markInvalid(this.blankText);
4787 if(value.length < this.minLength){
4788 this.markInvalid(String.format(this.minLengthText, this.minLength));
4791 if(value.length > this.maxLength){
4792 this.markInvalid(String.format(this.maxLengthText, this.maxLength));
4796 var vt = Roo.form.VTypes;
4797 if(!vt[this.vtype](value, this)){
4798 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
4802 if(typeof this.validator == "function"){
4803 var msg = this.validator(value);
4805 this.markInvalid(msg);
4809 if(this.regex && !this.regex.test(value)){
4810 this.markInvalid(this.regexText);
4819 fireKey : function(e){
4820 //Roo.log('field ' + e.getKey());
4821 if(e.isNavKeyPress()){
4822 this.fireEvent("specialkey", this, e);
4825 focus : function (selectText){
4827 this.inputEl().focus();
4828 if(selectText === true){
4829 this.inputEl().dom.select();
4835 onFocus : function(){
4836 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4837 // this.el.addClass(this.focusClass);
4840 this.hasFocus = true;
4841 this.startValue = this.getValue();
4842 this.fireEvent("focus", this);
4846 beforeBlur : Roo.emptyFn,
4850 onBlur : function(){
4852 if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
4853 //this.el.removeClass(this.focusClass);
4855 this.hasFocus = false;
4856 if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
4859 var v = this.getValue();
4860 if(String(v) !== String(this.startValue)){
4861 this.fireEvent('change', this, v, this.startValue);
4863 this.fireEvent("blur", this);
4867 * Resets the current field value to the originally loaded value and clears any validation messages
4870 this.setValue(this.originalValue);
4871 this.clearInvalid();
4874 * Returns the name of the field
4875 * @return {Mixed} name The name field
4877 getName: function(){
4881 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
4882 * @return {Mixed} value The field value
4884 getValue : function(){
4885 return this.inputEl().getValue();
4888 * Returns the raw data value which may or may not be a valid, defined value. To return a normalized value see {@link #getValue}.
4889 * @return {Mixed} value The field value
4891 getRawValue : function(){
4892 var v = this.inputEl().getValue();
4898 * Sets the underlying DOM field's value directly, bypassing validation. To set the value with validation see {@link #setValue}.
4899 * @param {Mixed} value The value to set
4901 setRawValue : function(v){
4902 return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4905 selectText : function(start, end){
4906 var v = this.getRawValue();
4908 start = start === undefined ? 0 : start;
4909 end = end === undefined ? v.length : end;
4910 var d = this.inputEl().dom;
4911 if(d.setSelectionRange){
4912 d.setSelectionRange(start, end);
4913 }else if(d.createTextRange){
4914 var range = d.createTextRange();
4915 range.moveStart("character", start);
4916 range.moveEnd("character", v.length-end);
4923 * Sets a data value into the field and validates it. To set the value directly without validation see {@link #setRawValue}.
4924 * @param {Mixed} value The value to set
4926 setValue : function(v){
4929 this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
4935 processValue : function(value){
4936 if(this.stripCharsRe){
4937 var newValue = value.replace(this.stripCharsRe, '');
4938 if(newValue !== value){
4939 this.setRawValue(newValue);
4946 preFocus : function(){
4948 if(this.selectOnFocus){
4949 this.inputEl().dom.select();
4952 filterKeys : function(e){
4954 if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
4957 var c = e.getCharCode(), cc = String.fromCharCode(c);
4958 if(Roo.isIE && (e.isSpecialKey() || !cc)){
4961 if(!this.maskRe.test(cc)){
4966 * Clear any invalid styles/messages for this field
4968 clearInvalid : function(){
4970 if(!this.el || this.preventMark){ // not rendered
4973 this.el.removeClass(this.invalidClass);
4975 switch(this.msgTarget){
4977 this.el.dom.qtip = '';
4980 this.el.dom.title = '';
4984 Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
4989 this.errorIcon.dom.qtip = '';
4990 this.errorIcon.hide();
4991 this.un('resize', this.alignErrorIcon, this);
4995 var t = Roo.getDom(this.msgTarget);
4997 t.style.display = 'none';
5001 this.fireEvent('valid', this);
5004 * Mark this field as invalid
5005 * @param {String} msg The validation message
5007 markInvalid : function(msg){
5008 if(!this.el || this.preventMark){ // not rendered
5011 this.el.addClass(this.invalidClass);
5013 msg = msg || this.invalidText;
5014 switch(this.msgTarget){
5016 this.el.dom.qtip = msg;
5017 this.el.dom.qclass = 'x-form-invalid-tip';
5018 if(Roo.QuickTips){ // fix for floating editors interacting with DND
5019 Roo.QuickTips.enable();
5023 this.el.dom.title = msg;
5027 var elp = this.el.findParent('.x-form-element', 5, true);
5028 this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
5029 this.errorEl.setWidth(elp.getWidth(true)-20);
5031 this.errorEl.update(msg);
5032 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
5035 if(!this.errorIcon){
5036 var elp = this.el.findParent('.x-form-element', 5, true);
5037 this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
5039 this.alignErrorIcon();
5040 this.errorIcon.dom.qtip = msg;
5041 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
5042 this.errorIcon.show();
5043 this.on('resize', this.alignErrorIcon, this);
5046 var t = Roo.getDom(this.msgTarget);
5048 t.style.display = this.msgDisplay;
5052 this.fireEvent('invalid', this, msg);
5055 SafariOnKeyDown : function(event)
5057 // this is a workaround for a password hang bug on chrome/ webkit.
5059 var isSelectAll = false;
5061 if(this.inputEl().dom.selectionEnd > 0){
5062 isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
5064 if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
5065 event.preventDefault();
5070 if(isSelectAll){ // backspace and delete key
5072 event.preventDefault();
5073 // this is very hacky as keydown always get's upper case.
5075 var cc = String.fromCharCode(event.getCharCode());
5076 this.setValue( event.shiftKey ? cc : cc.toLowerCase());
5080 adjustWidth : function(tag, w){
5081 tag = tag.toLowerCase();
5082 if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
5083 if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
5087 if(tag == 'textarea'){
5090 }else if(Roo.isOpera){
5094 if(tag == 'textarea'){
5113 * @class Roo.bootstrap.TextArea
5114 * @extends Roo.bootstrap.Input
5115 * Bootstrap TextArea class
5116 * @cfg {Number} cols Specifies the visible width of a text area
5117 * @cfg {Number} rows Specifies the visible number of lines in a text area
5118 * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
5119 * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
5120 * @cfg {string} html text
5123 * Create a new TextArea
5124 * @param {Object} config The config object
5127 Roo.bootstrap.TextArea = function(config){
5128 Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
5132 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input, {
5142 getAutoCreate : function(){
5144 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
5155 value : this.value || '',
5156 html: this.html || '',
5157 cls : 'form-control',
5158 placeholder : this.placeholder || ''
5162 if(this.maxLength && this.maxLength != Number.MAX_VALUE){
5163 input.maxLength = this.maxLength;
5167 input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
5171 input.cols = this.cols;
5174 if (this.readOnly) {
5175 input.readonly = true;
5179 input.name = this.name;
5183 input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
5187 ['xs','sm','md','lg'].map(function(size){
5188 if (settings[size]) {
5189 cfg.cls += ' col-' + size + '-' + settings[size];
5193 var inputblock = input;
5195 if (this.before || this.after) {
5198 cls : 'input-group',
5202 inputblock.cn.push({
5204 cls : 'input-group-addon',
5208 inputblock.cn.push(input);
5210 inputblock.cn.push({
5212 cls : 'input-group-addon',
5219 if (align ==='left' && this.fieldLabel.length) {
5220 Roo.log("left and has label");
5226 cls : 'control-label col-sm-' + this.labelWidth,
5227 html : this.fieldLabel
5231 cls : "col-sm-" + (12 - this.labelWidth),
5238 } else if ( this.fieldLabel.length) {
5244 //cls : 'input-group-addon',
5245 html : this.fieldLabel
5255 Roo.log(" no label && no align");
5265 if (this.disabled) {
5266 input.disabled=true;
5273 * return the real textarea element.
5275 inputEl: function ()
5277 return this.el.select('textarea.form-control',true).first();
5285 * trigger field - base class for combo..
5290 * @class Roo.bootstrap.TriggerField
5291 * @extends Roo.bootstrap.Input
5292 * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
5293 * The trigger has no default action, so you must assign a function to implement the trigger click handler by
5294 * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
5295 * for which you can provide a custom implementation. For example:
5297 var trigger = new Roo.bootstrap.TriggerField();
5298 trigger.onTriggerClick = myTriggerFn;
5299 trigger.applyTo('my-field');
5302 * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
5303 * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
5304 * @cfg {String} triggerClass An additional CSS class used to style the trigger button. The trigger will always get the
5305 * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
5307 * Create a new TriggerField.
5308 * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
5309 * to the base TextField)
5311 Roo.bootstrap.TriggerField = function(config){
5312 this.mimicing = false;
5313 Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
5316 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input, {
5318 * @cfg {String} triggerClass A CSS class to apply to the trigger
5321 * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
5325 /** @cfg {Boolean} grow @hide */
5326 /** @cfg {Number} growMin @hide */
5327 /** @cfg {Number} growMax @hide */
5333 autoSize: Roo.emptyFn,
5340 actionMode : 'wrap',
5344 getAutoCreate : function(){
5346 var parent = this.parent();
5348 var align = this.parentLabelAlign();
5353 cls: 'form-group' //input-group
5360 type : this.inputType,
5361 cls : 'form-control',
5362 autocomplete: 'off',
5363 placeholder : this.placeholder || ''
5367 input.name = this.name;
5370 input.cls += ' input-' + this.size;
5373 if (this.disabled) {
5374 input.disabled=true;
5377 var inputblock = input;
5379 if (this.before || this.after) {
5382 cls : 'input-group',
5386 inputblock.cn.push({
5388 cls : 'input-group-addon',
5392 inputblock.cn.push(input);
5394 inputblock.cn.push({
5396 cls : 'input-group-addon',
5409 cls: 'form-hidden-field'
5417 Roo.log('multiple');
5425 cls: 'form-hidden-field'
5429 cls: 'select2-choices',
5433 cls: 'select2-search-field',
5446 cls: 'select2-container input-group',
5451 cls: 'typeahead typeahead-long dropdown-menu',
5452 style: 'display:none'
5460 cls : 'input-group-addon btn dropdown-toggle',
5468 cls: 'combobox-clear',
5482 combobox.cls += ' select2-container-multi';
5485 if (align ==='left' && this.fieldLabel.length) {
5487 Roo.log("left and has label");
5493 cls : 'control-label col-sm-' + this.labelWidth,
5494 html : this.fieldLabel
5498 cls : "col-sm-" + (12 - this.labelWidth),
5505 } else if ( this.fieldLabel.length) {
5511 //cls : 'input-group-addon',
5512 html : this.fieldLabel
5522 Roo.log(" no label && no align");
5529 ['xs','sm','md','lg'].map(function(size){
5530 if (settings[size]) {
5531 cfg.cls += ' col-' + size + '-' + settings[size];
5542 onResize : function(w, h){
5543 // Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
5544 // if(typeof w == 'number'){
5545 // var x = w - this.trigger.getWidth();
5546 // this.inputEl().setWidth(this.adjustWidth('input', x));
5547 // this.trigger.setStyle('left', x+'px');
5552 adjustSize : Roo.BoxComponent.prototype.adjustSize,
5555 getResizeEl : function(){
5556 return this.inputEl();
5560 getPositionEl : function(){
5561 return this.inputEl();
5565 alignErrorIcon : function(){
5566 this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
5570 initEvents : function(){
5572 Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
5573 //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
5575 this.trigger = this.el.select('span.dropdown-toggle',true).first();
5576 if(this.hideTrigger){
5577 this.trigger.setDisplayed(false);
5579 this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
5583 this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
5586 //this.trigger.addClassOnOver('x-form-trigger-over');
5587 //this.trigger.addClassOnClick('x-form-trigger-click');
5590 // this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
5595 initTrigger : function(){
5600 onDestroy : function(){
5602 this.trigger.removeAllListeners();
5603 // this.trigger.remove();
5606 // this.wrap.remove();
5608 Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
5612 onFocus : function(){
5613 Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
5616 this.wrap.addClass('x-trigger-wrap-focus');
5617 this.mimicing = true;
5618 Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
5619 if(this.monitorTab){
5620 this.el.on("keydown", this.checkTab, this);
5627 checkTab : function(e){
5628 if(e.getKey() == e.TAB){
5634 onBlur : function(){
5639 mimicBlur : function(e, t){
5641 if(!this.wrap.contains(t) && this.validateBlur()){
5648 triggerBlur : function(){
5649 this.mimicing = false;
5650 Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
5651 if(this.monitorTab){
5652 this.el.un("keydown", this.checkTab, this);
5654 //this.wrap.removeClass('x-trigger-wrap-focus');
5655 Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
5659 // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
5660 validateBlur : function(e, t){
5665 onDisable : function(){
5666 Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
5668 // this.wrap.addClass('x-item-disabled');
5673 onEnable : function(){
5674 Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
5676 // this.el.removeClass('x-item-disabled');
5681 onShow : function(){
5682 var ae = this.getActionEl();
5685 ae.dom.style.display = '';
5686 ae.dom.style.visibility = 'visible';
5692 onHide : function(){
5693 var ae = this.getActionEl();
5694 ae.dom.style.display = 'none';
5698 * The function that should handle the trigger's click event. This method does nothing by default until overridden
5699 * by an implementing function.
5701 * @param {EventObject} e
5703 onTriggerClick : Roo.emptyFn
5707 * Ext JS Library 1.1.1
5708 * Copyright(c) 2006-2007, Ext JS, LLC.
5710 * Originally Released Under LGPL - original licence link has changed is not relivant.
5713 * <script type="text/javascript">
5718 * @class Roo.data.SortTypes
5720 * Defines the default sorting (casting?) comparison functions used when sorting data.
5722 Roo.data.SortTypes = {
5724 * Default sort that does nothing
5725 * @param {Mixed} s The value being converted
5726 * @return {Mixed} The comparison value
5733 * The regular expression used to strip tags
5737 stripTagsRE : /<\/?[^>]+>/gi,
5740 * Strips all HTML tags to sort on text only
5741 * @param {Mixed} s The value being converted
5742 * @return {String} The comparison value
5744 asText : function(s){
5745 return String(s).replace(this.stripTagsRE, "");
5749 * Strips all HTML tags to sort on text only - Case insensitive
5750 * @param {Mixed} s The value being converted
5751 * @return {String} The comparison value
5753 asUCText : function(s){
5754 return String(s).toUpperCase().replace(this.stripTagsRE, "");
5758 * Case insensitive string
5759 * @param {Mixed} s The value being converted
5760 * @return {String} The comparison value
5762 asUCString : function(s) {
5763 return String(s).toUpperCase();
5768 * @param {Mixed} s The value being converted
5769 * @return {Number} The comparison value
5771 asDate : function(s) {
5775 if(s instanceof Date){
5778 return Date.parse(String(s));
5783 * @param {Mixed} s The value being converted
5784 * @return {Float} The comparison value
5786 asFloat : function(s) {
5787 var val = parseFloat(String(s).replace(/,/g, ""));
5788 if(isNaN(val)) val = 0;
5794 * @param {Mixed} s The value being converted
5795 * @return {Number} The comparison value
5797 asInt : function(s) {
5798 var val = parseInt(String(s).replace(/,/g, ""));
5799 if(isNaN(val)) val = 0;
5804 * Ext JS Library 1.1.1
5805 * Copyright(c) 2006-2007, Ext JS, LLC.
5807 * Originally Released Under LGPL - original licence link has changed is not relivant.
5810 * <script type="text/javascript">
5814 * @class Roo.data.Record
5815 * Instances of this class encapsulate both record <em>definition</em> information, and record
5816 * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
5817 * to access Records cached in an {@link Roo.data.Store} object.<br>
5819 * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
5820 * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
5823 * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
5825 * This constructor should not be used to create Record objects. Instead, use the constructor generated by
5826 * {@link #create}. The parameters are the same.
5827 * @param {Array} data An associative Array of data values keyed by the field name.
5828 * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
5829 * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
5830 * not specified an integer id is generated.
5832 Roo.data.Record = function(data, id){
5833 this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
5838 * Generate a constructor for a specific record layout.
5839 * @param {Array} o An Array of field definition objects which specify field names, and optionally,
5840 * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
5841 * Each field definition object may contain the following properties: <ul>
5842 * <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,
5843 * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
5844 * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
5845 * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
5846 * is being used, then this is a string containing the javascript expression to reference the data relative to
5847 * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
5848 * to the data item relative to the record element. If the mapping expression is the same as the field name,
5849 * this may be omitted.</p></li>
5850 * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
5851 * <ul><li>auto (Default, implies no conversion)</li>
5856 * <li>date</li></ul></p></li>
5857 * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
5858 * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
5859 * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
5860 * by the Reader into an object that will be stored in the Record. It is passed the
5861 * following parameters:<ul>
5862 * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
5864 * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
5866 * <br>usage:<br><pre><code>
5867 var TopicRecord = Roo.data.Record.create(
5868 {name: 'title', mapping: 'topic_title'},
5869 {name: 'author', mapping: 'username'},
5870 {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
5871 {name: 'lastPost', mapping: 'post_time', type: 'date'},
5872 {name: 'lastPoster', mapping: 'user2'},
5873 {name: 'excerpt', mapping: 'post_text'}
5876 var myNewRecord = new TopicRecord({
5877 title: 'Do my job please',
5880 lastPost: new Date(),
5881 lastPoster: 'Animal',
5882 excerpt: 'No way dude!'
5884 myStore.add(myNewRecord);
5889 Roo.data.Record.create = function(o){
5891 f.superclass.constructor.apply(this, arguments);
5893 Roo.extend(f, Roo.data.Record);
5894 var p = f.prototype;
5895 p.fields = new Roo.util.MixedCollection(false, function(field){
5898 for(var i = 0, len = o.length; i < len; i++){
5899 p.fields.add(new Roo.data.Field(o[i]));
5901 f.getField = function(name){
5902 return p.fields.get(name);
5907 Roo.data.Record.AUTO_ID = 1000;
5908 Roo.data.Record.EDIT = 'edit';
5909 Roo.data.Record.REJECT = 'reject';
5910 Roo.data.Record.COMMIT = 'commit';
5912 Roo.data.Record.prototype = {
5914 * Readonly flag - true if this record has been modified.
5923 join : function(store){
5928 * Set the named field to the specified value.
5929 * @param {String} name The name of the field to set.
5930 * @param {Object} value The value to set the field to.
5932 set : function(name, value){
5933 if(this.data[name] == value){
5940 if(typeof this.modified[name] == 'undefined'){
5941 this.modified[name] = this.data[name];
5943 this.data[name] = value;
5944 if(!this.editing && this.store){
5945 this.store.afterEdit(this);
5950 * Get the value of the named field.
5951 * @param {String} name The name of the field to get the value of.
5952 * @return {Object} The value of the field.
5954 get : function(name){
5955 return this.data[name];
5959 beginEdit : function(){
5960 this.editing = true;
5965 cancelEdit : function(){
5966 this.editing = false;
5967 delete this.modified;
5971 endEdit : function(){
5972 this.editing = false;
5973 if(this.dirty && this.store){
5974 this.store.afterEdit(this);
5979 * Usually called by the {@link Roo.data.Store} which owns the Record.
5980 * Rejects all changes made to the Record since either creation, or the last commit operation.
5981 * Modified fields are reverted to their original values.
5983 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
5984 * of reject operations.
5986 reject : function(){
5987 var m = this.modified;
5989 if(typeof m[n] != "function"){
5990 this.data[n] = m[n];
5994 delete this.modified;
5995 this.editing = false;
5997 this.store.afterReject(this);
6002 * Usually called by the {@link Roo.data.Store} which owns the Record.
6003 * Commits all changes made to the Record since either creation, or the last commit operation.
6005 * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
6006 * of commit operations.
6008 commit : function(){
6010 delete this.modified;
6011 this.editing = false;
6013 this.store.afterCommit(this);
6018 hasError : function(){
6019 return this.error != null;
6023 clearError : function(){
6028 * Creates a copy of this record.
6029 * @param {String} id (optional) A new record id if you don't want to use this record's id
6032 copy : function(newId) {
6033 return new this.constructor(Roo.apply({}, this.data), newId || this.id);
6037 * Ext JS Library 1.1.1
6038 * Copyright(c) 2006-2007, Ext JS, LLC.
6040 * Originally Released Under LGPL - original licence link has changed is not relivant.
6043 * <script type="text/javascript">
6049 * @class Roo.data.Store
6050 * @extends Roo.util.Observable
6051 * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
6052 * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
6054 * 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
6055 * has no knowledge of the format of the data returned by the Proxy.<br>
6057 * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
6058 * instances from the data object. These records are cached and made available through accessor functions.
6060 * Creates a new Store.
6061 * @param {Object} config A config object containing the objects needed for the Store to access data,
6062 * and read the data into Records.
6064 Roo.data.Store = function(config){
6065 this.data = new Roo.util.MixedCollection(false);
6066 this.data.getKey = function(o){
6069 this.baseParams = {};
6076 "multisort" : "_multisort"
6079 if(config && config.data){
6080 this.inlineData = config.data;
6084 Roo.apply(this, config);
6086 if(this.reader){ // reader passed
6087 this.reader = Roo.factory(this.reader, Roo.data);
6088 this.reader.xmodule = this.xmodule || false;
6089 if(!this.recordType){
6090 this.recordType = this.reader.recordType;
6092 if(this.reader.onMetaChange){
6093 this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
6097 if(this.recordType){
6098 this.fields = this.recordType.prototype.fields;
6104 * @event datachanged
6105 * Fires when the data cache has changed, and a widget which is using this Store
6106 * as a Record cache should refresh its view.
6107 * @param {Store} this
6112 * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
6113 * @param {Store} this
6114 * @param {Object} meta The JSON metadata
6119 * Fires when Records have been added to the Store
6120 * @param {Store} this
6121 * @param {Roo.data.Record[]} records The array of Records added
6122 * @param {Number} index The index at which the record(s) were added
6127 * Fires when a Record has been removed from the Store
6128 * @param {Store} this
6129 * @param {Roo.data.Record} record The Record that was removed
6130 * @param {Number} index The index at which the record was removed
6135 * Fires when a Record has been updated
6136 * @param {Store} this
6137 * @param {Roo.data.Record} record The Record that was updated
6138 * @param {String} operation The update operation being performed. Value may be one of:
6140 Roo.data.Record.EDIT
6141 Roo.data.Record.REJECT
6142 Roo.data.Record.COMMIT
6148 * Fires when the data cache has been cleared.
6149 * @param {Store} this
6154 * Fires before a request is made for a new data object. If the beforeload handler returns false
6155 * the load action will be canceled.
6156 * @param {Store} this
6157 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6161 * @event beforeloadadd
6162 * Fires after a new set of Records has been loaded.
6163 * @param {Store} this
6164 * @param {Roo.data.Record[]} records The Records that were loaded
6165 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6167 beforeloadadd : true,
6170 * Fires after a new set of Records has been loaded, before they are added to the store.
6171 * @param {Store} this
6172 * @param {Roo.data.Record[]} records The Records that were loaded
6173 * @param {Object} options The loading options that were specified (see {@link #load} for details)
6174 * @params {Object} return from reader
6178 * @event loadexception
6179 * Fires if an exception occurs in the Proxy during loading.
6180 * Called with the signature of the Proxy's "loadexception" event.
6181 * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
6184 * @param {Object} return from JsonData.reader() - success, totalRecords, records
6185 * @param {Object} load options
6186 * @param {Object} jsonData from your request (normally this contains the Exception)
6188 loadexception : true
6192 this.proxy = Roo.factory(this.proxy, Roo.data);
6193 this.proxy.xmodule = this.xmodule || false;
6194 this.relayEvents(this.proxy, ["loadexception"]);
6196 this.sortToggle = {};
6197 this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
6199 Roo.data.Store.superclass.constructor.call(this);
6201 if(this.inlineData){
6202 this.loadData(this.inlineData);
6203 delete this.inlineData;
6207 Roo.extend(Roo.data.Store, Roo.util.Observable, {
6209 * @cfg {boolean} isLocal flag if data is locally available (and can be always looked up
6210 * without a remote query - used by combo/forms at present.
6214 * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
6217 * @cfg {Array} data Inline data to be loaded when the store is initialized.
6220 * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
6221 * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
6224 * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
6225 * on any HTTP request
6228 * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
6231 * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
6235 * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
6236 * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
6241 * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
6242 * loaded or when a record is removed. (defaults to false).
6244 pruneModifiedRecords : false,
6250 * Add Records to the Store and fires the add event.
6251 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6253 add : function(records){
6254 records = [].concat(records);
6255 for(var i = 0, len = records.length; i < len; i++){
6256 records[i].join(this);
6258 var index = this.data.length;
6259 this.data.addAll(records);
6260 this.fireEvent("add", this, records, index);
6264 * Remove a Record from the Store and fires the remove event.
6265 * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
6267 remove : function(record){
6268 var index = this.data.indexOf(record);
6269 this.data.removeAt(index);
6270 if(this.pruneModifiedRecords){
6271 this.modified.remove(record);
6273 this.fireEvent("remove", this, record, index);
6277 * Remove all Records from the Store and fires the clear event.
6279 removeAll : function(){
6281 if(this.pruneModifiedRecords){
6284 this.fireEvent("clear", this);
6288 * Inserts Records to the Store at the given index and fires the add event.
6289 * @param {Number} index The start index at which to insert the passed Records.
6290 * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
6292 insert : function(index, records){
6293 records = [].concat(records);
6294 for(var i = 0, len = records.length; i < len; i++){
6295 this.data.insert(index, records[i]);
6296 records[i].join(this);
6298 this.fireEvent("add", this, records, index);
6302 * Get the index within the cache of the passed Record.
6303 * @param {Roo.data.Record} record The Roo.data.Record object to to find.
6304 * @return {Number} The index of the passed Record. Returns -1 if not found.
6306 indexOf : function(record){
6307 return this.data.indexOf(record);
6311 * Get the index within the cache of the Record with the passed id.
6312 * @param {String} id The id of the Record to find.
6313 * @return {Number} The index of the Record. Returns -1 if not found.
6315 indexOfId : function(id){
6316 return this.data.indexOfKey(id);
6320 * Get the Record with the specified id.
6321 * @param {String} id The id of the Record to find.
6322 * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
6324 getById : function(id){
6325 return this.data.key(id);
6329 * Get the Record at the specified index.
6330 * @param {Number} index The index of the Record to find.
6331 * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
6333 getAt : function(index){
6334 return this.data.itemAt(index);
6338 * Returns a range of Records between specified indices.
6339 * @param {Number} startIndex (optional) The starting index (defaults to 0)
6340 * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
6341 * @return {Roo.data.Record[]} An array of Records
6343 getRange : function(start, end){
6344 return this.data.getRange(start, end);
6348 storeOptions : function(o){
6349 o = Roo.apply({}, o);
6352 this.lastOptions = o;
6356 * Loads the Record cache from the configured Proxy using the configured Reader.
6358 * If using remote paging, then the first load call must specify the <em>start</em>
6359 * and <em>limit</em> properties in the options.params property to establish the initial
6360 * position within the dataset, and the number of Records to cache on each read from the Proxy.
6362 * <strong>It is important to note that for remote data sources, loading is asynchronous,
6363 * and this call will return before the new data has been loaded. Perform any post-processing
6364 * in a callback function, or in a "load" event handler.</strong>
6366 * @param {Object} options An object containing properties which control loading options:<ul>
6367 * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
6368 * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
6369 * passed the following arguments:<ul>
6370 * <li>r : Roo.data.Record[]</li>
6371 * <li>options: Options object from the load call</li>
6372 * <li>success: Boolean success indicator</li></ul></li>
6373 * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
6374 * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
6377 load : function(options){
6378 options = options || {};
6379 if(this.fireEvent("beforeload", this, options) !== false){
6380 this.storeOptions(options);
6381 var p = Roo.apply(options.params || {}, this.baseParams);
6382 // if meta was not loaded from remote source.. try requesting it.
6383 if (!this.reader.metaFromRemote) {
6386 if(this.sortInfo && this.remoteSort){
6387 var pn = this.paramNames;
6388 p[pn["sort"]] = this.sortInfo.field;
6389 p[pn["dir"]] = this.sortInfo.direction;
6391 if (this.multiSort) {
6392 var pn = this.paramNames;
6393 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
6396 this.proxy.load(p, this.reader, this.loadRecords, this, options);
6401 * Reloads the Record cache from the configured Proxy using the configured Reader and
6402 * the options from the last load operation performed.
6403 * @param {Object} options (optional) An object containing properties which may override the options
6404 * used in the last load operation. See {@link #load} for details (defaults to null, in which case
6405 * the most recently used options are reused).
6407 reload : function(options){
6408 this.load(Roo.applyIf(options||{}, this.lastOptions));
6412 // Called as a callback by the Reader during a load operation.
6413 loadRecords : function(o, options, success){
6414 if(!o || success === false){
6415 if(success !== false){
6416 this.fireEvent("load", this, [], options, o);
6418 if(options.callback){
6419 options.callback.call(options.scope || this, [], options, false);
6423 // if data returned failure - throw an exception.
6424 if (o.success === false) {
6425 // show a message if no listener is registered.
6426 if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
6427 Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
6429 // loadmask wil be hooked into this..
6430 this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
6433 var r = o.records, t = o.totalRecords || r.length;
6435 this.fireEvent("beforeloadadd", this, r, options, o);
6437 if(!options || options.add !== true){
6438 if(this.pruneModifiedRecords){
6441 for(var i = 0, len = r.length; i < len; i++){
6445 this.data = this.snapshot;
6446 delete this.snapshot;
6449 this.data.addAll(r);
6450 this.totalLength = t;
6452 this.fireEvent("datachanged", this);
6454 this.totalLength = Math.max(t, this.data.length+r.length);
6457 this.fireEvent("load", this, r, options, o);
6458 if(options.callback){
6459 options.callback.call(options.scope || this, r, options, true);
6465 * Loads data from a passed data block. A Reader which understands the format of the data
6466 * must have been configured in the constructor.
6467 * @param {Object} data The data block from which to read the Records. The format of the data expected
6468 * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
6469 * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
6471 loadData : function(o, append){
6472 var r = this.reader.readRecords(o);
6473 this.loadRecords(r, {add: append}, true);
6477 * Gets the number of cached records.
6479 * <em>If using paging, this may not be the total size of the dataset. If the data object
6480 * used by the Reader contains the dataset size, then the getTotalCount() function returns
6481 * the data set size</em>
6483 getCount : function(){
6484 return this.data.length || 0;
6488 * Gets the total number of records in the dataset as returned by the server.
6490 * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
6491 * the dataset size</em>
6493 getTotalCount : function(){
6494 return this.totalLength || 0;
6498 * Returns the sort state of the Store as an object with two properties:
6500 field {String} The name of the field by which the Records are sorted
6501 direction {String} The sort order, "ASC" or "DESC"
6504 getSortState : function(){
6505 return this.sortInfo;
6509 applySort : function(){
6510 if(this.sortInfo && !this.remoteSort){
6511 var s = this.sortInfo, f = s.field;
6512 var st = this.fields.get(f).sortType;
6513 var fn = function(r1, r2){
6514 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
6515 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
6517 this.data.sort(s.direction, fn);
6518 if(this.snapshot && this.snapshot != this.data){
6519 this.snapshot.sort(s.direction, fn);
6525 * Sets the default sort column and order to be used by the next load operation.
6526 * @param {String} fieldName The name of the field to sort by.
6527 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6529 setDefaultSort : function(field, dir){
6530 this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
6535 * If remote sorting is used, the sort is performed on the server, and the cache is
6536 * reloaded. If local sorting is used, the cache is sorted internally.
6537 * @param {String} fieldName The name of the field to sort by.
6538 * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
6540 sort : function(fieldName, dir){
6541 var f = this.fields.get(fieldName);
6543 this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
6545 if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
6546 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
6551 this.sortToggle[f.name] = dir;
6552 this.sortInfo = {field: f.name, direction: dir};
6553 if(!this.remoteSort){
6555 this.fireEvent("datachanged", this);
6557 this.load(this.lastOptions);
6562 * Calls the specified function for each of the Records in the cache.
6563 * @param {Function} fn The function to call. The Record is passed as the first parameter.
6564 * Returning <em>false</em> aborts and exits the iteration.
6565 * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
6567 each : function(fn, scope){
6568 this.data.each(fn, scope);
6572 * Gets all records modified since the last commit. Modified records are persisted across load operations
6573 * (e.g., during paging).
6574 * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
6576 getModifiedRecords : function(){
6577 return this.modified;
6581 createFilterFn : function(property, value, anyMatch){
6582 if(!value.exec){ // not a regex
6583 value = String(value);
6584 if(value.length == 0){
6587 value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
6590 return value.test(r.data[property]);
6595 * Sums the value of <i>property</i> for each record between start and end and returns the result.
6596 * @param {String} property A field on your records
6597 * @param {Number} start The record index to start at (defaults to 0)
6598 * @param {Number} end The last record index to include (defaults to length - 1)
6599 * @return {Number} The sum
6601 sum : function(property, start, end){
6602 var rs = this.data.items, v = 0;
6604 end = (end || end === 0) ? end : rs.length-1;
6606 for(var i = start; i <= end; i++){
6607 v += (rs[i].data[property] || 0);
6613 * Filter the records by a specified property.
6614 * @param {String} field A field on your records
6615 * @param {String/RegExp} value Either a string that the field
6616 * should start with or a RegExp to test against the field
6617 * @param {Boolean} anyMatch True to match any part not just the beginning
6619 filter : function(property, value, anyMatch){
6620 var fn = this.createFilterFn(property, value, anyMatch);
6621 return fn ? this.filterBy(fn) : this.clearFilter();
6625 * Filter by a function. The specified function will be called with each
6626 * record in this data source. If the function returns true the record is included,
6627 * otherwise it is filtered.
6628 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6629 * @param {Object} scope (optional) The scope of the function (defaults to this)
6631 filterBy : function(fn, scope){
6632 this.snapshot = this.snapshot || this.data;
6633 this.data = this.queryBy(fn, scope||this);
6634 this.fireEvent("datachanged", this);
6638 * Query the records by a specified property.
6639 * @param {String} field A field on your records
6640 * @param {String/RegExp} value Either a string that the field
6641 * should start with or a RegExp to test against the field
6642 * @param {Boolean} anyMatch True to match any part not just the beginning
6643 * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6645 query : function(property, value, anyMatch){
6646 var fn = this.createFilterFn(property, value, anyMatch);
6647 return fn ? this.queryBy(fn) : this.data.clone();
6651 * Query by a function. The specified function will be called with each
6652 * record in this data source. If the function returns true the record is included
6654 * @param {Function} fn The function to be called, it will receive 2 args (record, id)
6655 * @param {Object} scope (optional) The scope of the function (defaults to this)
6656 @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
6658 queryBy : function(fn, scope){
6659 var data = this.snapshot || this.data;
6660 return data.filterBy(fn, scope||this);
6664 * Collects unique values for a particular dataIndex from this store.
6665 * @param {String} dataIndex The property to collect
6666 * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
6667 * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
6668 * @return {Array} An array of the unique values
6670 collect : function(dataIndex, allowNull, bypassFilter){
6671 var d = (bypassFilter === true && this.snapshot) ?
6672 this.snapshot.items : this.data.items;
6673 var v, sv, r = [], l = {};
6674 for(var i = 0, len = d.length; i < len; i++){
6675 v = d[i].data[dataIndex];
6677 if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
6686 * Revert to a view of the Record cache with no filtering applied.
6687 * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
6689 clearFilter : function(suppressEvent){
6690 if(this.snapshot && this.snapshot != this.data){
6691 this.data = this.snapshot;
6692 delete this.snapshot;
6693 if(suppressEvent !== true){
6694 this.fireEvent("datachanged", this);
6700 afterEdit : function(record){
6701 if(this.modified.indexOf(record) == -1){
6702 this.modified.push(record);
6704 this.fireEvent("update", this, record, Roo.data.Record.EDIT);
6708 afterReject : function(record){
6709 this.modified.remove(record);
6710 this.fireEvent("update", this, record, Roo.data.Record.REJECT);
6714 afterCommit : function(record){
6715 this.modified.remove(record);
6716 this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
6720 * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
6721 * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
6723 commitChanges : function(){
6724 var m = this.modified.slice(0);
6726 for(var i = 0, len = m.length; i < len; i++){
6732 * Cancel outstanding changes on all changed records.
6734 rejectChanges : function(){
6735 var m = this.modified.slice(0);
6737 for(var i = 0, len = m.length; i < len; i++){
6742 onMetaChange : function(meta, rtype, o){
6743 this.recordType = rtype;
6744 this.fields = rtype.prototype.fields;
6745 delete this.snapshot;
6746 this.sortInfo = meta.sortInfo || this.sortInfo;
6748 this.fireEvent('metachange', this, this.reader.meta);
6751 moveIndex : function(data, type)
6753 var index = this.indexOf(data);
6755 var newIndex = index + type;
6759 this.insert(newIndex, data);
6764 * Ext JS Library 1.1.1
6765 * Copyright(c) 2006-2007, Ext JS, LLC.
6767 * Originally Released Under LGPL - original licence link has changed is not relivant.
6770 * <script type="text/javascript">
6774 * @class Roo.data.SimpleStore
6775 * @extends Roo.data.Store
6776 * Small helper class to make creating Stores from Array data easier.
6777 * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
6778 * @cfg {Array} fields An array of field definition objects, or field name strings.
6779 * @cfg {Array} data The multi-dimensional array of data
6781 * @param {Object} config
6783 Roo.data.SimpleStore = function(config){
6784 Roo.data.SimpleStore.superclass.constructor.call(this, {
6786 reader: new Roo.data.ArrayReader({
6789 Roo.data.Record.create(config.fields)
6791 proxy : new Roo.data.MemoryProxy(config.data)
6795 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
6797 * Ext JS Library 1.1.1
6798 * Copyright(c) 2006-2007, Ext JS, LLC.
6800 * Originally Released Under LGPL - original licence link has changed is not relivant.
6803 * <script type="text/javascript">
6808 * @extends Roo.data.Store
6809 * @class Roo.data.JsonStore
6810 * Small helper class to make creating Stores for JSON data easier. <br/>
6812 var store = new Roo.data.JsonStore({
6813 url: 'get-images.php',
6815 fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
6818 * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
6819 * JsonReader and HttpProxy (unless inline data is provided).</b>
6820 * @cfg {Array} fields An array of field definition objects, or field name strings.
6822 * @param {Object} config
6824 Roo.data.JsonStore = function(c){
6825 Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
6826 proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
6827 reader: new Roo.data.JsonReader(c, c.fields)
6830 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
6832 * Ext JS Library 1.1.1
6833 * Copyright(c) 2006-2007, Ext JS, LLC.
6835 * Originally Released Under LGPL - original licence link has changed is not relivant.
6838 * <script type="text/javascript">
6842 Roo.data.Field = function(config){
6843 if(typeof config == "string"){
6844 config = {name: config};
6846 Roo.apply(this, config);
6852 var st = Roo.data.SortTypes;
6853 // named sortTypes are supported, here we look them up
6854 if(typeof this.sortType == "string"){
6855 this.sortType = st[this.sortType];
6858 // set default sortType for strings and dates
6862 this.sortType = st.asUCString;
6865 this.sortType = st.asDate;
6868 this.sortType = st.none;
6873 var stripRe = /[\$,%]/g;
6875 // prebuilt conversion function for this field, instead of
6876 // switching every time we're reading a value
6878 var cv, dateFormat = this.dateFormat;
6883 cv = function(v){ return v; };
6886 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
6890 return v !== undefined && v !== null && v !== '' ?
6891 parseInt(String(v).replace(stripRe, ""), 10) : '';
6896 return v !== undefined && v !== null && v !== '' ?
6897 parseFloat(String(v).replace(stripRe, ""), 10) : '';
6902 cv = function(v){ return v === true || v === "true" || v == 1; };
6909 if(v instanceof Date){
6913 if(dateFormat == "timestamp"){
6914 return new Date(v*1000);
6916 return Date.parseDate(v, dateFormat);
6918 var parsed = Date.parse(v);
6919 return parsed ? new Date(parsed) : null;
6928 Roo.data.Field.prototype = {
6936 * Ext JS Library 1.1.1
6937 * Copyright(c) 2006-2007, Ext JS, LLC.
6939 * Originally Released Under LGPL - original licence link has changed is not relivant.
6942 * <script type="text/javascript">
6945 // Base class for reading structured data from a data source. This class is intended to be
6946 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
6949 * @class Roo.data.DataReader
6950 * Base class for reading structured data from a data source. This class is intended to be
6951 * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
6954 Roo.data.DataReader = function(meta, recordType){
6958 this.recordType = recordType instanceof Array ?
6959 Roo.data.Record.create(recordType) : recordType;
6962 Roo.data.DataReader.prototype = {
6964 * Create an empty record
6965 * @param {Object} data (optional) - overlay some values
6966 * @return {Roo.data.Record} record created.
6968 newRow : function(d) {
6970 this.recordType.prototype.fields.each(function(c) {
6972 case 'int' : da[c.name] = 0; break;
6973 case 'date' : da[c.name] = new Date(); break;
6974 case 'float' : da[c.name] = 0.0; break;
6975 case 'boolean' : da[c.name] = false; break;
6976 default : da[c.name] = ""; break;
6980 return new this.recordType(Roo.apply(da, d));
6985 * Ext JS Library 1.1.1
6986 * Copyright(c) 2006-2007, Ext JS, LLC.
6988 * Originally Released Under LGPL - original licence link has changed is not relivant.
6991 * <script type="text/javascript">
6995 * @class Roo.data.DataProxy
6996 * @extends Roo.data.Observable
6997 * This class is an abstract base class for implementations which provide retrieval of
6998 * unformatted data objects.<br>
7000 * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
7001 * (of the appropriate type which knows how to parse the data object) to provide a block of
7002 * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
7004 * Custom implementations must implement the load method as described in
7005 * {@link Roo.data.HttpProxy#load}.
7007 Roo.data.DataProxy = function(){
7011 * Fires before a network request is made to retrieve a data object.
7012 * @param {Object} This DataProxy object.
7013 * @param {Object} params The params parameter to the load function.
7018 * Fires before the load method's callback is called.
7019 * @param {Object} This DataProxy object.
7020 * @param {Object} o The data object.
7021 * @param {Object} arg The callback argument object passed to the load function.
7025 * @event loadexception
7026 * Fires if an Exception occurs during data retrieval.
7027 * @param {Object} This DataProxy object.
7028 * @param {Object} o The data object.
7029 * @param {Object} arg The callback argument object passed to the load function.
7030 * @param {Object} e The Exception.
7032 loadexception : true
7034 Roo.data.DataProxy.superclass.constructor.call(this);
7037 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
7040 * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
7044 * Ext JS Library 1.1.1
7045 * Copyright(c) 2006-2007, Ext JS, LLC.
7047 * Originally Released Under LGPL - original licence link has changed is not relivant.
7050 * <script type="text/javascript">
7053 * @class Roo.data.MemoryProxy
7054 * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
7055 * to the Reader when its load method is called.
7057 * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
7059 Roo.data.MemoryProxy = function(data){
7063 Roo.data.MemoryProxy.superclass.constructor.call(this);
7067 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
7069 * Load data from the requested source (in this case an in-memory
7070 * data object passed to the constructor), read the data object into
7071 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7072 * process that block using the passed callback.
7073 * @param {Object} params This parameter is not used by the MemoryProxy class.
7074 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7075 * object into a block of Roo.data.Records.
7076 * @param {Function} callback The function into which to pass the block of Roo.data.records.
7077 * The function must be passed <ul>
7078 * <li>The Record block object</li>
7079 * <li>The "arg" argument from the load function</li>
7080 * <li>A boolean success indicator</li>
7082 * @param {Object} scope The scope in which to call the callback
7083 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7085 load : function(params, reader, callback, scope, arg){
7086 params = params || {};
7089 result = reader.readRecords(this.data);
7091 this.fireEvent("loadexception", this, arg, null, e);
7092 callback.call(scope, null, arg, false);
7095 callback.call(scope, result, arg, true);
7099 update : function(params, records){
7104 * Ext JS Library 1.1.1
7105 * Copyright(c) 2006-2007, Ext JS, LLC.
7107 * Originally Released Under LGPL - original licence link has changed is not relivant.
7110 * <script type="text/javascript">
7113 * @class Roo.data.HttpProxy
7114 * @extends Roo.data.DataProxy
7115 * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
7116 * configured to reference a certain URL.<br><br>
7118 * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
7119 * from which the running page was served.<br><br>
7121 * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
7123 * Be aware that to enable the browser to parse an XML document, the server must set
7124 * the Content-Type header in the HTTP response to "text/xml".
7126 * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
7127 * an {@link Roo.data.Connection} object. If a Connection config is passed, the singleton {@link Roo.Ajax} object
7128 * will be used to make the request.
7130 Roo.data.HttpProxy = function(conn){
7131 Roo.data.HttpProxy.superclass.constructor.call(this);
7132 // is conn a conn config or a real conn?
7134 this.useAjax = !conn || !conn.events;
7138 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
7139 // thse are take from connection...
7142 * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
7145 * @cfg {Object} extraParams (Optional) An object containing properties which are used as
7146 * extra parameters to each request made by this object. (defaults to undefined)
7149 * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
7150 * to each request made by this object. (defaults to undefined)
7153 * @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)
7156 * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
7159 * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
7165 * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
7169 * Return the {@link Roo.data.Connection} object being used by this Proxy.
7170 * @return {Connection} The Connection object. This object may be used to subscribe to events on
7171 * a finer-grained basis than the DataProxy events.
7173 getConnection : function(){
7174 return this.useAjax ? Roo.Ajax : this.conn;
7178 * Load data from the configured {@link Roo.data.Connection}, read the data object into
7179 * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
7180 * process that block using the passed callback.
7181 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7182 * for the request to the remote server.
7183 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7184 * object into a block of Roo.data.Records.
7185 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7186 * The function must be passed <ul>
7187 * <li>The Record block object</li>
7188 * <li>The "arg" argument from the load function</li>
7189 * <li>A boolean success indicator</li>
7191 * @param {Object} scope The scope in which to call the callback
7192 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7194 load : function(params, reader, callback, scope, arg){
7195 if(this.fireEvent("beforeload", this, params) !== false){
7197 params : params || {},
7199 callback : callback,
7204 callback : this.loadResponse,
7208 Roo.applyIf(o, this.conn);
7209 if(this.activeRequest){
7210 Roo.Ajax.abort(this.activeRequest);
7212 this.activeRequest = Roo.Ajax.request(o);
7214 this.conn.request(o);
7217 callback.call(scope||this, null, arg, false);
7222 loadResponse : function(o, success, response){
7223 delete this.activeRequest;
7225 this.fireEvent("loadexception", this, o, response);
7226 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7231 result = o.reader.read(response);
7233 this.fireEvent("loadexception", this, o, response, e);
7234 o.request.callback.call(o.request.scope, null, o.request.arg, false);
7238 this.fireEvent("load", this, o, o.request.arg);
7239 o.request.callback.call(o.request.scope, result, o.request.arg, true);
7243 update : function(dataSet){
7248 updateResponse : function(dataSet){
7253 * Ext JS Library 1.1.1
7254 * Copyright(c) 2006-2007, Ext JS, LLC.
7256 * Originally Released Under LGPL - original licence link has changed is not relivant.
7259 * <script type="text/javascript">
7263 * @class Roo.data.ScriptTagProxy
7264 * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
7265 * other than the originating domain of the running page.<br><br>
7267 * <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
7268 * of the running page, you must use this class, rather than DataProxy.</em><br><br>
7270 * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
7271 * source code that is used as the source inside a <script> tag.<br><br>
7273 * In order for the browser to process the returned data, the server must wrap the data object
7274 * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
7275 * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
7276 * depending on whether the callback name was passed:
7279 boolean scriptTag = false;
7280 String cb = request.getParameter("callback");
7283 response.setContentType("text/javascript");
7285 response.setContentType("application/x-json");
7287 Writer out = response.getWriter();
7289 out.write(cb + "(");
7291 out.print(dataBlock.toJsonString());
7298 * @param {Object} config A configuration object.
7300 Roo.data.ScriptTagProxy = function(config){
7301 Roo.data.ScriptTagProxy.superclass.constructor.call(this);
7302 Roo.apply(this, config);
7303 this.head = document.getElementsByTagName("head")[0];
7306 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
7308 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
7310 * @cfg {String} url The URL from which to request the data object.
7313 * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
7317 * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
7318 * the server the name of the callback function set up by the load call to process the returned data object.
7319 * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
7320 * javascript output which calls this named function passing the data object as its only parameter.
7322 callbackParam : "callback",
7324 * @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
7325 * name to the request.
7330 * Load data from the configured URL, read the data object into
7331 * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
7332 * process that block using the passed callback.
7333 * @param {Object} params An object containing properties which are to be used as HTTP parameters
7334 * for the request to the remote server.
7335 * @param {Roo.data.DataReader} reader The Reader object which converts the data
7336 * object into a block of Roo.data.Records.
7337 * @param {Function} callback The function into which to pass the block of Roo.data.Records.
7338 * The function must be passed <ul>
7339 * <li>The Record block object</li>
7340 * <li>The "arg" argument from the load function</li>
7341 * <li>A boolean success indicator</li>
7343 * @param {Object} scope The scope in which to call the callback
7344 * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
7346 load : function(params, reader, callback, scope, arg){
7347 if(this.fireEvent("beforeload", this, params) !== false){
7349 var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
7352 url += (url.indexOf("?") != -1 ? "&" : "?") + p;
7354 url += "&_dc=" + (new Date().getTime());
7356 var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
7359 cb : "stcCallback"+transId,
7360 scriptId : "stcScript"+transId,
7364 callback : callback,
7370 window[trans.cb] = function(o){
7371 conn.handleResponse(o, trans);
7374 url += String.format("&{0}={1}", this.callbackParam, trans.cb);
7376 if(this.autoAbort !== false){
7380 trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
7382 var script = document.createElement("script");
7383 script.setAttribute("src", url);
7384 script.setAttribute("type", "text/javascript");
7385 script.setAttribute("id", trans.scriptId);
7386 this.head.appendChild(script);
7390 callback.call(scope||this, null, arg, false);
7395 isLoading : function(){
7396 return this.trans ? true : false;
7400 * Abort the current server request.
7403 if(this.isLoading()){
7404 this.destroyTrans(this.trans);
7409 destroyTrans : function(trans, isLoaded){
7410 this.head.removeChild(document.getElementById(trans.scriptId));
7411 clearTimeout(trans.timeoutId);
7413 window[trans.cb] = undefined;
7415 delete window[trans.cb];
7418 // if hasn't been loaded, wait for load to remove it to prevent script error
7419 window[trans.cb] = function(){
7420 window[trans.cb] = undefined;
7422 delete window[trans.cb];
7429 handleResponse : function(o, trans){
7431 this.destroyTrans(trans, true);
7434 result = trans.reader.readRecords(o);
7436 this.fireEvent("loadexception", this, o, trans.arg, e);
7437 trans.callback.call(trans.scope||window, null, trans.arg, false);
7440 this.fireEvent("load", this, o, trans.arg);
7441 trans.callback.call(trans.scope||window, result, trans.arg, true);
7445 handleFailure : function(trans){
7447 this.destroyTrans(trans, false);
7448 this.fireEvent("loadexception", this, null, trans.arg);
7449 trans.callback.call(trans.scope||window, null, trans.arg, false);
7453 * Ext JS Library 1.1.1
7454 * Copyright(c) 2006-2007, Ext JS, LLC.
7456 * Originally Released Under LGPL - original licence link has changed is not relivant.
7459 * <script type="text/javascript">
7463 * @class Roo.data.JsonReader
7464 * @extends Roo.data.DataReader
7465 * Data reader class to create an Array of Roo.data.Record objects from a JSON response
7466 * based on mappings in a provided Roo.data.Record constructor.
7468 * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
7469 * in the reply previously.
7474 var RecordDef = Roo.data.Record.create([
7475 {name: 'name', mapping: 'name'}, // "mapping" property not needed if it's the same as "name"
7476 {name: 'occupation'} // This field will use "occupation" as the mapping.
7478 var myReader = new Roo.data.JsonReader({
7479 totalProperty: "results", // The property which contains the total dataset size (optional)
7480 root: "rows", // The property which contains an Array of row objects
7481 id: "id" // The property within each row object that provides an ID for the record (optional)
7485 * This would consume a JSON file like this:
7487 { 'results': 2, 'rows': [
7488 { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
7489 { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
7492 * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
7493 * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
7494 * paged from the remote server.
7495 * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
7496 * @cfg {String} root name of the property which contains the Array of row objects.
7497 * @cfg {String} id Name of the property within a row object that contains a record identifier value.
7499 * Create a new JsonReader
7500 * @param {Object} meta Metadata configuration options
7501 * @param {Object} recordType Either an Array of field definition objects,
7502 * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
7504 Roo.data.JsonReader = function(meta, recordType){
7507 // set some defaults:
7509 totalProperty: 'total',
7510 successProperty : 'success',
7515 Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
7517 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
7520 * @prop {Boolean} metaFromRemote - if the meta data was loaded from the remote source.
7521 * Used by Store query builder to append _requestMeta to params.
7524 metaFromRemote : false,
7526 * This method is only used by a DataProxy which has retrieved data from a remote server.
7527 * @param {Object} response The XHR object which contains the JSON data in its responseText.
7528 * @return {Object} data A data block which is used by an Roo.data.Store object as
7529 * a cache of Roo.data.Records.
7531 read : function(response){
7532 var json = response.responseText;
7534 var o = /* eval:var:o */ eval("("+json+")");
7536 throw {message: "JsonReader.read: Json object not found"};
7542 this.metaFromRemote = true;
7543 this.meta = o.metaData;
7544 this.recordType = Roo.data.Record.create(o.metaData.fields);
7545 this.onMetaChange(this.meta, this.recordType, o);
7547 return this.readRecords(o);
7550 // private function a store will implement
7551 onMetaChange : function(meta, recordType, o){
7558 simpleAccess: function(obj, subsc) {
7565 getJsonAccessor: function(){
7567 return function(expr) {
7569 return(re.test(expr))
7570 ? new Function("obj", "return obj." + expr)
7580 * Create a data block containing Roo.data.Records from an XML document.
7581 * @param {Object} o An object which contains an Array of row objects in the property specified
7582 * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
7583 * which contains the total size of the dataset.
7584 * @return {Object} data A data block which is used by an Roo.data.Store object as
7585 * a cache of Roo.data.Records.
7587 readRecords : function(o){
7589 * After any data loads, the raw JSON data is available for further custom processing.
7593 var s = this.meta, Record = this.recordType,
7594 f = Record.prototype.fields, fi = f.items, fl = f.length;
7596 // Generate extraction functions for the totalProperty, the root, the id, and for each field
7598 if(s.totalProperty) {
7599 this.getTotal = this.getJsonAccessor(s.totalProperty);
7601 if(s.successProperty) {
7602 this.getSuccess = this.getJsonAccessor(s.successProperty);
7604 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
7606 var g = this.getJsonAccessor(s.id);
7607 this.getId = function(rec) {
7609 return (r === undefined || r === "") ? null : r;
7612 this.getId = function(){return null;};
7615 for(var jj = 0; jj < fl; jj++){
7617 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
7618 this.ef[jj] = this.getJsonAccessor(map);
7622 var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
7623 if(s.totalProperty){
7624 var vt = parseInt(this.getTotal(o), 10);
7629 if(s.successProperty){
7630 var vs = this.getSuccess(o);
7631 if(vs === false || vs === 'false'){
7636 for(var i = 0; i < c; i++){
7639 var id = this.getId(n);
7640 for(var j = 0; j < fl; j++){
7642 var v = this.ef[j](n);
7644 Roo.log('missing convert for ' + f.name);
7648 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
7650 var record = new Record(values, id);
7652 records[i] = record;
7658 totalRecords : totalRecords
7663 * Ext JS Library 1.1.1
7664 * Copyright(c) 2006-2007, Ext JS, LLC.
7666 * Originally Released Under LGPL - original licence link has changed is not relivant.
7669 * <script type="text/javascript">
7673 * @class Roo.data.ArrayReader
7674 * @extends Roo.data.DataReader
7675 * Data reader class to create an Array of Roo.data.Record objects from an Array.
7676 * Each element of that Array represents a row of data fields. The
7677 * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
7678 * of the field definition if it exists, or the field's ordinal position in the definition.<br>
7682 var RecordDef = Roo.data.Record.create([
7683 {name: 'name', mapping: 1}, // "mapping" only needed if an "id" field is present which
7684 {name: 'occupation', mapping: 2} // precludes using the ordinal position as the index.
7686 var myReader = new Roo.data.ArrayReader({
7687 id: 0 // The subscript within row Array that provides an ID for the Record (optional)
7691 * This would consume an Array like this:
7693 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
7695 * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
7697 * Create a new JsonReader
7698 * @param {Object} meta Metadata configuration options.
7699 * @param {Object} recordType Either an Array of field definition objects
7700 * as specified to {@link Roo.data.Record#create},
7701 * or an {@link Roo.data.Record} object
7702 * created using {@link Roo.data.Record#create}.
7704 Roo.data.ArrayReader = function(meta, recordType){
7705 Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
7708 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
7710 * Create a data block containing Roo.data.Records from an XML document.
7711 * @param {Object} o An Array of row objects which represents the dataset.
7712 * @return {Object} data A data block which is used by an Roo.data.Store object as
7713 * a cache of Roo.data.Records.
7715 readRecords : function(o){
7716 var sid = this.meta ? this.meta.id : null;
7717 var recordType = this.recordType, fields = recordType.prototype.fields;
7720 for(var i = 0; i < root.length; i++){
7723 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
7724 for(var j = 0, jlen = fields.length; j < jlen; j++){
7725 var f = fields.items[j];
7726 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
7727 var v = n[k] !== undefined ? n[k] : f.defaultValue;
7731 var record = new recordType(values, id);
7733 records[records.length] = record;
7737 totalRecords : records.length
7746 * @class Roo.bootstrap.ComboBox
7747 * @extends Roo.bootstrap.TriggerField
7748 * A combobox control with support for autocomplete, remote-loading, paging and many other features.
7749 * @cfg {Boolean} append (true|false) default false
7751 * Create a new ComboBox.
7752 * @param {Object} config Configuration options
7754 Roo.bootstrap.ComboBox = function(config){
7755 Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
7759 * Fires when the dropdown list is expanded
7760 * @param {Roo.bootstrap.ComboBox} combo This combo box
7765 * Fires when the dropdown list is collapsed
7766 * @param {Roo.bootstrap.ComboBox} combo This combo box
7770 * @event beforeselect
7771 * Fires before a list item is selected. Return false to cancel the selection.
7772 * @param {Roo.bootstrap.ComboBox} combo This combo box
7773 * @param {Roo.data.Record} record The data record returned from the underlying store
7774 * @param {Number} index The index of the selected item in the dropdown list
7776 'beforeselect' : true,
7779 * Fires when a list item is selected
7780 * @param {Roo.bootstrap.ComboBox} combo This combo box
7781 * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
7782 * @param {Number} index The index of the selected item in the dropdown list
7786 * @event beforequery
7787 * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
7788 * The event object passed has these properties:
7789 * @param {Roo.bootstrap.ComboBox} combo This combo box
7790 * @param {String} query The query
7791 * @param {Boolean} forceAll true to force "all" query
7792 * @param {Boolean} cancel true to cancel the query
7793 * @param {Object} e The query event object
7795 'beforequery': true,
7798 * Fires when the 'add' icon is pressed (add a listener to enable add button)
7799 * @param {Roo.bootstrap.ComboBox} combo This combo box
7804 * Fires when the 'edit' icon is pressed (add a listener to enable add button)
7805 * @param {Roo.bootstrap.ComboBox} combo This combo box
7806 * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
7811 * Fires when the remove value from the combobox array
7812 * @param {Roo.bootstrap.ComboBox} combo This combo box
7819 this.selectedIndex = -1;
7820 if(this.mode == 'local'){
7821 if(config.queryDelay === undefined){
7822 this.queryDelay = 10;
7824 if(config.minChars === undefined){
7830 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
7833 * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
7834 * rendering into an Roo.Editor, defaults to false)
7837 * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
7838 * {tag: "input", type: "text", size: "24", autocomplete: "off"})
7841 * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
7844 * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
7845 * the dropdown list (defaults to undefined, with no header element)
7849 * @cfg {String/Roo.Template} tpl The template to use to render the output
7853 * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
7855 listWidth: undefined,
7857 * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
7858 * mode = 'remote' or 'text' if mode = 'local')
7860 displayField: undefined,
7862 * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
7863 * mode = 'remote' or 'value' if mode = 'local').
7864 * Note: use of a valueField requires the user make a selection
7865 * in order for a value to be mapped.
7867 valueField: undefined,
7871 * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
7872 * field's data value (defaults to the underlying DOM element's name)
7874 hiddenName: undefined,
7876 * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
7880 * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
7882 selectedClass: 'active',
7885 * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
7889 * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
7890 * anchor positions (defaults to 'tl-bl')
7892 listAlign: 'tl-bl?',
7894 * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
7898 * @cfg {String} triggerAction The action to execute when the trigger field is activated. Use 'all' to run the
7899 * query specified by the allQuery config option (defaults to 'query')
7901 triggerAction: 'query',
7903 * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
7904 * (defaults to 4, does not apply if editable = false)
7908 * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
7909 * delay (typeAheadDelay) if it matches a known value (defaults to false)
7913 * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
7914 * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
7918 * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
7919 * filter queries will execute with page start and limit parameters. Only applies when mode = 'remote' (defaults to 0)
7923 * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus. Only applies
7924 * when editable = true (defaults to false)
7926 selectOnFocus:false,
7928 * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
7930 queryParam: 'query',
7932 * @cfg {String} loadingText The text to display in the dropdown list while data is loading. Only applies
7933 * when mode = 'remote' (defaults to 'Loading...')
7935 loadingText: 'Loading...',
7937 * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
7941 * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
7945 * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
7946 * traditional select (defaults to true)
7950 * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
7954 * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
7958 * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
7959 * listWidth has a higher value)
7963 * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
7964 * allow the user to set arbitrary text into the field (defaults to false)
7966 forceSelection:false,
7968 * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
7969 * if typeAhead = true (defaults to 250)
7971 typeAheadDelay : 250,
7973 * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
7974 * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
7976 valueNotFoundText : undefined,
7978 * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
7983 * @cfg {Boolean} disableClear Disable showing of clear button.
7985 disableClear : false,
7987 * @cfg {Boolean} alwaysQuery Disable caching of results, and always send query
7989 alwaysQuery : false,
7992 * @cfg {Boolean} multiple (true|false) ComboBobArray, default false
8006 // element that contains real text value.. (when hidden is used..)
8009 initEvents: function(){
8012 throw "can not find store for combo";
8014 this.store = Roo.factory(this.store, Roo.data);
8018 Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
8021 if(this.hiddenName){
8023 this.hiddenField = this.el.select('input.form-hidden-field',true).first();
8025 this.hiddenField.dom.value =
8026 this.hiddenValue !== undefined ? this.hiddenValue :
8027 this.value !== undefined ? this.value : '';
8029 // prevent input submission
8030 this.el.dom.removeAttribute('name');
8031 this.hiddenField.dom.setAttribute('name', this.hiddenName);
8036 // this.el.dom.setAttribute('autocomplete', 'off');
8039 var cls = 'x-combo-list';
8040 this.list = this.el.select('ul.dropdown-menu',true).first();
8042 //this.list = new Roo.Layer({
8043 // shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
8046 var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
8047 this.list.setWidth(lw);
8049 this.list.on('mouseover', this.onViewOver, this);
8050 this.list.on('mousemove', this.onViewMove, this);
8052 this.list.on('scroll', this.onViewScroll, this);
8055 this.list.swallowEvent('mousewheel');
8056 this.assetHeight = 0;
8059 this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
8060 this.assetHeight += this.header.getHeight();
8063 this.innerList = this.list.createChild({cls:cls+'-inner'});
8064 this.innerList.on('mouseover', this.onViewOver, this);
8065 this.innerList.on('mousemove', this.onViewMove, this);
8066 this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8068 if(this.allowBlank && !this.pageSize && !this.disableClear){
8069 this.footer = this.list.createChild({cls:cls+'-ft'});
8070 this.pageTb = new Roo.Toolbar(this.footer);
8074 this.footer = this.list.createChild({cls:cls+'-ft'});
8075 this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
8076 {pageSize: this.pageSize});
8080 if (this.pageTb && this.allowBlank && !this.disableClear) {
8082 this.pageTb.add(new Roo.Toolbar.Fill(), {
8083 cls: 'x-btn-icon x-btn-clear',
8089 _this.onSelect(false, -1);
8094 this.assetHeight += this.footer.getHeight();
8099 this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
8102 this.view = new Roo.View(this.el.select('ul.dropdown-menu',true).first(), this.tpl, {
8103 singleSelect:true, store: this.store, selectedClass: this.selectedClass
8105 //this.view.wrapEl.setDisplayed(false);
8106 this.view.on('click', this.onViewClick, this);
8110 this.store.on('beforeload', this.onBeforeLoad, this);
8111 this.store.on('load', this.onLoad, this);
8112 this.store.on('loadexception', this.onLoadException, this);
8115 this.resizer = new Roo.Resizable(this.list, {
8116 pinned:true, handles:'se'
8118 this.resizer.on('resize', function(r, w, h){
8119 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
8121 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
8122 this.restrictHeight();
8124 this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
8128 this.editable = true;
8129 this.setEditable(false);
8134 if (typeof(this.events.add.listeners) != 'undefined') {
8136 this.addicon = this.wrap.createChild(
8137 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });
8139 this.addicon.on('click', function(e) {
8140 this.fireEvent('add', this);
8143 if (typeof(this.events.edit.listeners) != 'undefined') {
8145 this.editicon = this.wrap.createChild(
8146 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });
8148 this.editicon.setStyle('margin-left', '40px');
8150 this.editicon.on('click', function(e) {
8152 // we fire even if inothing is selected..
8153 this.fireEvent('edit', this, this.lastData );
8159 this.keyNav = new Roo.KeyNav(this.inputEl(), {
8161 this.inKeyMode = true;
8165 "down" : function(e){
8166 if(!this.isExpanded()){
8167 this.onTriggerClick();
8169 this.inKeyMode = true;
8174 "enter" : function(e){
8179 "esc" : function(e){
8183 "tab" : function(e){
8186 if(this.fireEvent("specialkey", this, e)){
8187 this.onViewClick(false);
8195 doRelay : function(foo, bar, hname){
8196 if(hname == 'down' || this.scope.isExpanded()){
8197 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
8206 this.queryDelay = Math.max(this.queryDelay || 10,
8207 this.mode == 'local' ? 10 : 250);
8210 this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
8213 this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
8215 if(this.editable !== false){
8216 this.inputEl().on("keyup", this.onKeyUp, this);
8218 if(this.forceSelection){
8219 this.on('blur', this.doForce, this);
8223 this.choices = this.el.select('ul.select2-choices', true).first();
8224 this.searchField = this.el.select('ul li.select2-search-field', true).first();
8228 onDestroy : function(){
8230 this.view.setStore(null);
8231 this.view.el.removeAllListeners();
8232 this.view.el.remove();
8233 this.view.purgeListeners();
8236 this.list.dom.innerHTML = '';
8239 this.store.un('beforeload', this.onBeforeLoad, this);
8240 this.store.un('load', this.onLoad, this);
8241 this.store.un('loadexception', this.onLoadException, this);
8243 Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
8247 fireKey : function(e){
8248 if(e.isNavKeyPress() && !this.list.isVisible()){
8249 this.fireEvent("specialkey", this, e);
8254 onResize: function(w, h){
8255 // Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
8257 // if(typeof w != 'number'){
8258 // // we do not handle it!?!?
8261 // var tw = this.trigger.getWidth();
8262 // // tw += this.addicon ? this.addicon.getWidth() : 0;
8263 // // tw += this.editicon ? this.editicon.getWidth() : 0;
8265 // this.inputEl().setWidth( this.adjustWidth('input', x));
8267 // //this.trigger.setStyle('left', x+'px');
8269 // if(this.list && this.listWidth === undefined){
8270 // var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
8271 // this.list.setWidth(lw);
8272 // this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
8280 * Allow or prevent the user from directly editing the field text. If false is passed,
8281 * the user will only be able to select from the items defined in the dropdown list. This method
8282 * is the runtime equivalent of setting the 'editable' config option at config time.
8283 * @param {Boolean} value True to allow the user to directly edit the field text
8285 setEditable : function(value){
8286 if(value == this.editable){
8289 this.editable = value;
8291 this.inputEl().dom.setAttribute('readOnly', true);
8292 this.inputEl().on('mousedown', this.onTriggerClick, this);
8293 this.inputEl().addClass('x-combo-noedit');
8295 this.inputEl().dom.setAttribute('readOnly', false);
8296 this.inputEl().un('mousedown', this.onTriggerClick, this);
8297 this.inputEl().removeClass('x-combo-noedit');
8303 onBeforeLoad : function(combo,opts){
8308 this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
8310 this.restrictHeight();
8311 this.selectedIndex = -1;
8315 onLoad : function(){
8317 this.hasQuery = false;
8323 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8324 this.loading.hide();
8327 if(this.store.getCount() > 0){
8329 this.restrictHeight();
8330 if(this.lastQuery == this.allQuery){
8332 this.inputEl().dom.select();
8334 if(!this.selectByValue(this.value, true)){
8335 this.select(0, true);
8339 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
8340 this.taTask.delay(this.typeAheadDelay);
8344 this.onEmptyResults();
8350 onLoadException : function()
8352 this.hasQuery = false;
8354 if(typeof(this.loading) !== 'undefined' && this.loading !== null){
8355 this.loading.hide();
8359 Roo.log(this.store.reader.jsonData);
8360 if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8362 //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8368 onTypeAhead : function(){
8369 if(this.store.getCount() > 0){
8370 var r = this.store.getAt(0);
8371 var newValue = r.data[this.displayField];
8372 var len = newValue.length;
8373 var selStart = this.getRawValue().length;
8375 if(selStart != len){
8376 this.setRawValue(newValue);
8377 this.selectText(selStart, newValue.length);
8383 onSelect : function(record, index){
8385 if(this.fireEvent('beforeselect', this, record, index) !== false){
8387 this.setFromData(index > -1 ? record.data : false);
8390 this.fireEvent('select', this, record, index);
8395 * Returns the currently selected field value or empty string if no value is set.
8396 * @return {String} value The selected value
8398 getValue : function(){
8401 return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
8404 if(this.valueField){
8405 return typeof this.value != 'undefined' ? this.value : '';
8407 return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
8412 * Clears any text/value currently set in the field
8414 clearValue : function(){
8415 if(this.hiddenField){
8416 this.hiddenField.dom.value = '';
8419 this.setRawValue('');
8420 this.lastSelectionText = '';
8425 * Sets the specified value into the field. If the value finds a match, the corresponding record text
8426 * will be displayed in the field. If the value does not match the data value of an existing item,
8427 * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
8428 * Otherwise the field will be blank (although the value will still be set).
8429 * @param {String} value The value to match
8431 setValue : function(v){
8438 if(this.valueField){
8439 var r = this.findRecord(this.valueField, v);
8441 text = r.data[this.displayField];
8442 }else if(this.valueNotFoundText !== undefined){
8443 text = this.valueNotFoundText;
8446 this.lastSelectionText = text;
8447 if(this.hiddenField){
8448 this.hiddenField.dom.value = v;
8450 Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
8454 * @property {Object} the last set data for the element
8459 * Sets the value of the field based on a object which is related to the record format for the store.
8460 * @param {Object} value the value to set as. or false on reset?
8462 setFromData : function(o){
8469 var dv = ''; // display value
8470 var vv = ''; // value value..
8472 if (this.displayField) {
8473 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8475 // this is an error condition!!!
8476 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8479 if(this.valueField){
8480 vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
8483 if(this.hiddenField){
8484 this.hiddenField.dom.value = vv;
8486 this.lastSelectionText = dv;
8487 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8491 // no hidden field.. - we store the value in 'value', but still display
8492 // display field!!!!
8493 this.lastSelectionText = dv;
8494 Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
8501 // overridden so that last data is reset..
8502 this.setValue(this.originalValue);
8503 this.clearInvalid();
8504 this.lastData = false;
8506 this.view.clearSelections();
8510 findRecord : function(prop, value){
8512 if(this.store.getCount() > 0){
8513 this.store.each(function(r){
8514 if(r.data[prop] == value){
8526 // returns hidden if it's set..
8527 if (!this.rendered) {return ''};
8528 return !this.hiddenName && this.inputEl().dom.name ? this.inputEl().dom.name : (this.hiddenName || '');
8532 onViewMove : function(e, t){
8533 this.inKeyMode = false;
8537 onViewOver : function(e, t){
8538 if(this.inKeyMode){ // prevent key nav and mouse over conflicts
8541 var item = this.view.findItemFromChild(t);
8543 var index = this.view.indexOf(item);
8544 this.select(index, false);
8549 onViewClick : function(doFocus)
8551 var index = this.view.getSelectedIndexes()[0];
8552 var r = this.store.getAt(index);
8554 this.onSelect(r, index);
8556 if(doFocus !== false && !this.blockFocus){
8557 this.inputEl().focus();
8562 restrictHeight : function(){
8563 //this.innerList.dom.style.height = '';
8564 //var inner = this.innerList.dom;
8565 //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
8566 //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
8567 //this.list.beginUpdate();
8568 //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
8569 this.list.alignTo(this.inputEl(), this.listAlign);
8570 //this.list.endUpdate();
8574 onEmptyResults : function(){
8579 * Returns true if the dropdown list is expanded, else false.
8581 isExpanded : function(){
8582 return this.list.isVisible();
8586 * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
8587 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8588 * @param {String} value The data value of the item to select
8589 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8590 * selected item if it is not currently in view (defaults to true)
8591 * @return {Boolean} True if the value matched an item in the list, else false
8593 selectByValue : function(v, scrollIntoView){
8594 if(v !== undefined && v !== null){
8595 var r = this.findRecord(this.valueField || this.displayField, v);
8597 this.select(this.store.indexOf(r), scrollIntoView);
8605 * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
8606 * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
8607 * @param {Number} index The zero-based index of the list item to select
8608 * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
8609 * selected item if it is not currently in view (defaults to true)
8611 select : function(index, scrollIntoView){
8612 this.selectedIndex = index;
8613 this.view.select(index);
8614 if(scrollIntoView !== false){
8615 var el = this.view.getNode(index);
8617 //this.innerList.scrollChildIntoView(el, false);
8624 selectNext : function(){
8625 var ct = this.store.getCount();
8627 if(this.selectedIndex == -1){
8629 }else if(this.selectedIndex < ct-1){
8630 this.select(this.selectedIndex+1);
8636 selectPrev : function(){
8637 var ct = this.store.getCount();
8639 if(this.selectedIndex == -1){
8641 }else if(this.selectedIndex != 0){
8642 this.select(this.selectedIndex-1);
8648 onKeyUp : function(e){
8649 if(this.editable !== false && !e.isSpecialKey()){
8650 this.lastKey = e.getKey();
8651 this.dqTask.delay(this.queryDelay);
8656 validateBlur : function(){
8657 return !this.list || !this.list.isVisible();
8661 initQuery : function(){
8662 this.doQuery(this.getRawValue());
8666 doForce : function(){
8667 if(this.el.dom.value.length > 0){
8669 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
8675 * Execute a query to filter the dropdown list. Fires the beforequery event prior to performing the
8676 * query allowing the query action to be canceled if needed.
8677 * @param {String} query The SQL query to execute
8678 * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
8679 * in the field than the minimum specified by the minChars config option. It also clears any filter previously
8680 * saved in the current store (defaults to false)
8682 doQuery : function(q, forceAll){
8684 if(q === undefined || q === null){
8693 if(this.fireEvent('beforequery', qe)===false || qe.cancel){
8698 forceAll = qe.forceAll;
8699 if(forceAll === true || (q.length >= this.minChars)){
8701 this.hasQuery = true;
8703 if(this.lastQuery != q || this.alwaysQuery){
8705 if(this.mode == 'local'){
8706 this.selectedIndex = -1;
8708 this.store.clearFilter();
8710 this.store.filter(this.displayField, q);
8714 this.store.baseParams[this.queryParam] = q;
8716 var options = {params : this.getParams(q)};
8720 options.params.start = this.page * this.pageSize;
8723 this.store.load(options);
8727 this.selectedIndex = -1;
8732 this.loadNext = false;
8736 getParams : function(q){
8738 //p[this.queryParam] = q;
8742 p.limit = this.pageSize;
8748 * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
8750 collapse : function(){
8751 if(!this.isExpanded()){
8756 Roo.get(document).un('mousedown', this.collapseIf, this);
8757 Roo.get(document).un('mousewheel', this.collapseIf, this);
8758 if (!this.editable) {
8759 Roo.get(document).un('keydown', this.listKeyPress, this);
8761 this.fireEvent('collapse', this);
8765 collapseIf : function(e){
8766 var in_combo = e.within(this.el);
8767 var in_list = e.within(this.list);
8769 if (in_combo || in_list) {
8770 //e.stopPropagation();
8779 * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
8781 expand : function(){
8783 if(this.isExpanded() || !this.hasFocus){
8787 this.list.alignTo(this.inputEl(), this.listAlign);
8789 Roo.get(document).on('mousedown', this.collapseIf, this);
8790 Roo.get(document).on('mousewheel', this.collapseIf, this);
8791 if (!this.editable) {
8792 Roo.get(document).on('keydown', this.listKeyPress, this);
8795 this.fireEvent('expand', this);
8799 // Implements the default empty TriggerField.onTriggerClick function
8800 onTriggerClick : function()
8802 Roo.log('trigger click');
8809 this.loadNext = false;
8811 if(this.isExpanded()){
8813 if (!this.blockFocus) {
8814 this.inputEl().focus();
8818 this.hasFocus = true;
8819 if(this.triggerAction == 'all') {
8820 this.doQuery(this.allQuery, true);
8822 this.doQuery(this.getRawValue());
8824 if (!this.blockFocus) {
8825 this.inputEl().focus();
8829 listKeyPress : function(e)
8831 //Roo.log('listkeypress');
8832 // scroll to first matching element based on key pres..
8833 if (e.isSpecialKey()) {
8836 var k = String.fromCharCode(e.getKey()).toUpperCase();
8839 var csel = this.view.getSelectedNodes();
8840 var cselitem = false;
8842 var ix = this.view.indexOf(csel[0]);
8843 cselitem = this.store.getAt(ix);
8844 if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
8850 this.store.each(function(v) {
8852 // start at existing selection.
8853 if (cselitem.id == v.id) {
8859 if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
8860 match = this.store.indexOf(v);
8866 if (match === false) {
8867 return true; // no more action?
8870 this.view.select(match);
8871 var sn = Roo.get(this.view.getSelectedNodes()[0])
8872 //sn.scrollIntoView(sn.dom.parentNode, false);
8875 onViewScroll : function(e, t){
8877 if(this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
8881 this.hasQuery = true;
8883 this.loading = this.list.select('.loading', true).first();
8885 if(this.loading === null){
8886 this.list.createChild({
8888 cls: 'loading select2-more-results select2-active',
8889 html: 'Loading more results...'
8892 this.loading = this.list.select('.loading', true).first();
8894 this.loading.setVisibilityMode(Roo.Element.DISPLAY);
8896 this.loading.hide();
8899 this.loading.show();
8904 this.loadNext = true;
8906 (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
8911 addItem : function(o)
8913 var dv = ''; // display value
8915 if (this.displayField) {
8916 dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
8918 // this is an error condition!!!
8919 Roo.log('no displayField value set for '+ (this.name ? this.name : this.id));
8926 var choice = this.choices.createChild({
8928 cls: 'select2-search-choice',
8937 cls: 'select2-search-choice-close',
8942 }, this.searchField);
8944 var close = choice.select('a.select2-search-choice-close', true).first()
8946 close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
8953 this.inputEl().dom.value = '';
8957 onRemoveItem : function(e, _self, o)
8959 Roo.log('remove item');
8960 var index = this.item.indexOf(o.data) * 1;
8963 Roo.log('not this item?!');
8967 this.item.splice(index, 1);
8972 this.fireEvent('remove', this);
8976 syncValue : function()
8978 if(!this.item.length){
8985 Roo.each(this.item, function(i){
8986 if(_this.valueField){
8987 value.push(i[_this.valueField]);
8994 this.value = value.join(',');
8996 if(this.hiddenField){
8997 this.hiddenField.dom.value = this.value;
9001 clearItem : function()
9009 Roo.each(this.choices.select('>li.select2-search-choice', true).elements, function(c){
9019 * @cfg {Boolean} grow
9023 * @cfg {Number} growMin
9027 * @cfg {Number} growMax
9037 * Ext JS Library 1.1.1
9038 * Copyright(c) 2006-2007, Ext JS, LLC.
9040 * Originally Released Under LGPL - original licence link has changed is not relivant.
9043 * <script type="text/javascript">
9048 * @extends Roo.util.Observable
9049 * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template.
9050 * This class also supports single and multi selection modes. <br>
9051 * Create a data model bound view:
9053 var store = new Roo.data.Store(...);
9055 var view = new Roo.View({
9057 tpl : '<div id="{0}">{2} - {1}</div>', // auto create template
9060 selectedClass: "ydataview-selected",
9064 // listen for node click?
9065 view.on("click", function(vw, index, node, e){
9066 alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
9070 dataModel.load("foobar.xml");
9072 For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
9074 * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
9075 * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
9077 * Note: old style constructor is still suported (container, template, config)
9081 * @param {Object} config The config object
9084 Roo.View = function(config, depreciated_tpl, depreciated_config){
9086 if (typeof(depreciated_tpl) == 'undefined') {
9087 // new way.. - universal constructor.
9088 Roo.apply(this, config);
9089 this.el = Roo.get(this.el);
9092 this.el = Roo.get(config);
9093 this.tpl = depreciated_tpl;
9094 Roo.apply(this, depreciated_config);
9096 this.wrapEl = this.el.wrap().wrap();
9097 ///this.el = this.wrapEla.appendChild(document.createElement("div"));
9100 if(typeof(this.tpl) == "string"){
9101 this.tpl = new Roo.Template(this.tpl);
9103 // support xtype ctors..
9104 this.tpl = new Roo.factory(this.tpl, Roo);
9116 * @event beforeclick
9117 * Fires before a click is processed. Returns false to cancel the default action.
9118 * @param {Roo.View} this
9119 * @param {Number} index The index of the target node
9120 * @param {HTMLElement} node The target node
9121 * @param {Roo.EventObject} e The raw event object
9123 "beforeclick" : true,
9126 * Fires when a template node is clicked.
9127 * @param {Roo.View} this
9128 * @param {Number} index The index of the target node
9129 * @param {HTMLElement} node The target node
9130 * @param {Roo.EventObject} e The raw event object
9135 * Fires when a template node is double clicked.
9136 * @param {Roo.View} this
9137 * @param {Number} index The index of the target node
9138 * @param {HTMLElement} node The target node
9139 * @param {Roo.EventObject} e The raw event object
9143 * @event contextmenu
9144 * Fires when a template node is right clicked.
9145 * @param {Roo.View} this
9146 * @param {Number} index The index of the target node
9147 * @param {HTMLElement} node The target node
9148 * @param {Roo.EventObject} e The raw event object
9150 "contextmenu" : true,
9152 * @event selectionchange
9153 * Fires when the selected nodes change.
9154 * @param {Roo.View} this
9155 * @param {Array} selections Array of the selected nodes
9157 "selectionchange" : true,
9160 * @event beforeselect
9161 * Fires before a selection is made. If any handlers return false, the selection is cancelled.
9162 * @param {Roo.View} this
9163 * @param {HTMLElement} node The node to be selected
9164 * @param {Array} selections Array of currently selected nodes
9166 "beforeselect" : true,
9168 * @event preparedata
9169 * Fires on every row to render, to allow you to change the data.
9170 * @param {Roo.View} this
9171 * @param {Object} data to be rendered (change this)
9173 "preparedata" : true
9181 "click": this.onClick,
9182 "dblclick": this.onDblClick,
9183 "contextmenu": this.onContextMenu,
9187 this.selections = [];
9189 this.cmp = new Roo.CompositeElementLite([]);
9191 this.store = Roo.factory(this.store, Roo.data);
9192 this.setStore(this.store, true);
9195 if ( this.footer && this.footer.xtype) {
9197 var fctr = this.wrapEl.appendChild(document.createElement("div"));
9199 this.footer.dataSource = this.store
9200 this.footer.container = fctr;
9201 this.footer = Roo.factory(this.footer, Roo);
9202 fctr.insertFirst(this.el);
9204 // this is a bit insane - as the paging toolbar seems to detach the el..
9205 // dom.parentNode.parentNode.parentNode
9206 // they get detached?
9210 Roo.View.superclass.constructor.call(this);
9215 Roo.extend(Roo.View, Roo.util.Observable, {
9218 * @cfg {Roo.data.Store} store Data store to load data from.
9223 * @cfg {String|Roo.Element} el The container element.
9228 * @cfg {String|Roo.Template} tpl The template used by this View
9232 * @cfg {String} dataName the named area of the template to use as the data area
9233 * Works with domtemplates roo-name="name"
9237 * @cfg {String} selectedClass The css class to add to selected nodes
9239 selectedClass : "x-view-selected",
9241 * @cfg {String} emptyText The empty text to show when nothing is loaded.
9246 * @cfg {String} text to display on mask (default Loading)
9250 * @cfg {Boolean} multiSelect Allow multiple selection
9252 multiSelect : false,
9254 * @cfg {Boolean} singleSelect Allow single selection
9256 singleSelect: false,
9259 * @cfg {Boolean} toggleSelect - selecting
9261 toggleSelect : false,
9264 * Returns the element this view is bound to.
9265 * @return {Roo.Element}
9274 * Refreshes the view. - called by datachanged on the store. - do not call directly.
9276 refresh : function(){
9280 // if we are using something like 'domtemplate', then
9281 // the what gets used is:
9282 // t.applySubtemplate(NAME, data, wrapping data..)
9283 // the outer template then get' applied with
9284 // the store 'extra data'
9285 // and the body get's added to the
9286 // roo-name="data" node?
9287 // <span class='roo-tpl-{name}'></span> ?????
9291 this.clearSelections();
9294 var records = this.store.getRange();
9295 if(records.length < 1) {
9297 // is this valid?? = should it render a template??
9299 this.el.update(this.emptyText);
9303 if (this.dataName) {
9304 this.el.update(t.apply(this.store.meta)); //????
9305 el = this.el.child('.roo-tpl-' + this.dataName);
9308 for(var i = 0, len = records.length; i < len; i++){
9309 var data = this.prepareData(records[i].data, i, records[i]);
9310 this.fireEvent("preparedata", this, data, i, records[i]);
9311 html[html.length] = Roo.util.Format.trim(
9313 t.applySubtemplate(this.dataName, data, this.store.meta) :
9320 el.update(html.join(""));
9321 this.nodes = el.dom.childNodes;
9322 this.updateIndexes(0);
9327 * Function to override to reformat the data that is sent to
9328 * the template for each node.
9329 * DEPRICATED - use the preparedata event handler.
9330 * @param {Array/Object} data The raw data (array of colData for a data model bound view or
9331 * a JSON object for an UpdateManager bound view).
9333 prepareData : function(data, index, record)
9335 this.fireEvent("preparedata", this, data, index, record);
9339 onUpdate : function(ds, record){
9340 Roo.log('on update');
9341 this.clearSelections();
9342 var index = this.store.indexOf(record);
9343 var n = this.nodes[index];
9344 this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
9345 n.parentNode.removeChild(n);
9346 this.updateIndexes(index, index);
9352 onAdd : function(ds, records, index)
9354 Roo.log(['on Add', ds, records, index] );
9355 this.clearSelections();
9356 if(this.nodes.length == 0){
9360 var n = this.nodes[index];
9361 for(var i = 0, len = records.length; i < len; i++){
9362 var d = this.prepareData(records[i].data, i, records[i]);
9364 this.tpl.insertBefore(n, d);
9367 this.tpl.append(this.el, d);
9370 this.updateIndexes(index);
9373 onRemove : function(ds, record, index){
9374 Roo.log('onRemove');
9375 this.clearSelections();
9376 var el = this.dataName ?
9377 this.el.child('.roo-tpl-' + this.dataName) :
9380 el.dom.removeChild(this.nodes[index]);
9381 this.updateIndexes(index);
9385 * Refresh an individual node.
9386 * @param {Number} index
9388 refreshNode : function(index){
9389 this.onUpdate(this.store, this.store.getAt(index));
9392 updateIndexes : function(startIndex, endIndex){
9393 var ns = this.nodes;
9394 startIndex = startIndex || 0;
9395 endIndex = endIndex || ns.length - 1;
9396 for(var i = startIndex; i <= endIndex; i++){
9397 ns[i].nodeIndex = i;
9402 * Changes the data store this view uses and refresh the view.
9403 * @param {Store} store
9405 setStore : function(store, initial){
9406 if(!initial && this.store){
9407 this.store.un("datachanged", this.refresh);
9408 this.store.un("add", this.onAdd);
9409 this.store.un("remove", this.onRemove);
9410 this.store.un("update", this.onUpdate);
9411 this.store.un("clear", this.refresh);
9412 this.store.un("beforeload", this.onBeforeLoad);
9413 this.store.un("load", this.onLoad);
9414 this.store.un("loadexception", this.onLoad);
9418 store.on("datachanged", this.refresh, this);
9419 store.on("add", this.onAdd, this);
9420 store.on("remove", this.onRemove, this);
9421 store.on("update", this.onUpdate, this);
9422 store.on("clear", this.refresh, this);
9423 store.on("beforeload", this.onBeforeLoad, this);
9424 store.on("load", this.onLoad, this);
9425 store.on("loadexception", this.onLoad, this);
9433 * onbeforeLoad - masks the loading area.
9436 onBeforeLoad : function(store,opts)
9438 Roo.log('onBeforeLoad');
9442 this.el.mask(this.mask ? this.mask : "Loading" );
9444 onLoad : function ()
9451 * Returns the template node the passed child belongs to or null if it doesn't belong to one.
9452 * @param {HTMLElement} node
9453 * @return {HTMLElement} The template node
9455 findItemFromChild : function(node){
9456 var el = this.dataName ?
9457 this.el.child('.roo-tpl-' + this.dataName,true) :
9460 if(!node || node.parentNode == el){
9463 var p = node.parentNode;
9464 while(p && p != el){
9465 if(p.parentNode == el){
9474 onClick : function(e){
9475 var item = this.findItemFromChild(e.getTarget());
9477 var index = this.indexOf(item);
9478 if(this.onItemClick(item, index, e) !== false){
9479 this.fireEvent("click", this, index, item, e);
9482 this.clearSelections();
9487 onContextMenu : function(e){
9488 var item = this.findItemFromChild(e.getTarget());
9490 this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
9495 onDblClick : function(e){
9496 var item = this.findItemFromChild(e.getTarget());
9498 this.fireEvent("dblclick", this, this.indexOf(item), item, e);
9502 onItemClick : function(item, index, e)
9504 if(this.fireEvent("beforeclick", this, index, item, e) === false){
9507 if (this.toggleSelect) {
9508 var m = this.isSelected(item) ? 'unselect' : 'select';
9511 _t[m](item, true, false);
9514 if(this.multiSelect || this.singleSelect){
9515 if(this.multiSelect && e.shiftKey && this.lastSelection){
9516 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
9518 this.select(item, this.multiSelect && e.ctrlKey);
9519 this.lastSelection = item;
9527 * Get the number of selected nodes.
9530 getSelectionCount : function(){
9531 return this.selections.length;
9535 * Get the currently selected nodes.
9536 * @return {Array} An array of HTMLElements
9538 getSelectedNodes : function(){
9539 return this.selections;
9543 * Get the indexes of the selected nodes.
9546 getSelectedIndexes : function(){
9547 var indexes = [], s = this.selections;
9548 for(var i = 0, len = s.length; i < len; i++){
9549 indexes.push(s[i].nodeIndex);
9555 * Clear all selections
9556 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
9558 clearSelections : function(suppressEvent){
9559 if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
9560 this.cmp.elements = this.selections;
9561 this.cmp.removeClass(this.selectedClass);
9562 this.selections = [];
9564 this.fireEvent("selectionchange", this, this.selections);
9570 * Returns true if the passed node is selected
9571 * @param {HTMLElement/Number} node The node or node index
9574 isSelected : function(node){
9575 var s = this.selections;
9579 node = this.getNode(node);
9580 return s.indexOf(node) !== -1;
9585 * @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
9586 * @param {Boolean} keepExisting (optional) true to keep existing selections
9587 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9589 select : function(nodeInfo, keepExisting, suppressEvent){
9590 if(nodeInfo instanceof Array){
9592 this.clearSelections(true);
9594 for(var i = 0, len = nodeInfo.length; i < len; i++){
9595 this.select(nodeInfo[i], true, true);
9599 var node = this.getNode(nodeInfo);
9600 if(!node || this.isSelected(node)){
9601 return; // already selected.
9604 this.clearSelections(true);
9606 if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
9607 Roo.fly(node).addClass(this.selectedClass);
9608 this.selections.push(node);
9610 this.fireEvent("selectionchange", this, this.selections);
9618 * @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
9619 * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
9620 * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
9622 unselect : function(nodeInfo, keepExisting, suppressEvent)
9624 if(nodeInfo instanceof Array){
9625 Roo.each(this.selections, function(s) {
9626 this.unselect(s, nodeInfo);
9630 var node = this.getNode(nodeInfo);
9631 if(!node || !this.isSelected(node)){
9632 Roo.log("not selected");
9633 return; // not selected.
9637 Roo.each(this.selections, function(s) {
9639 Roo.fly(node).removeClass(this.selectedClass);
9646 this.selections= ns;
9647 this.fireEvent("selectionchange", this, this.selections);
9651 * Gets a template node.
9652 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9653 * @return {HTMLElement} The node or null if it wasn't found
9655 getNode : function(nodeInfo){
9656 if(typeof nodeInfo == "string"){
9657 return document.getElementById(nodeInfo);
9658 }else if(typeof nodeInfo == "number"){
9659 return this.nodes[nodeInfo];
9665 * Gets a range template nodes.
9666 * @param {Number} startIndex
9667 * @param {Number} endIndex
9668 * @return {Array} An array of nodes
9670 getNodes : function(start, end){
9671 var ns = this.nodes;
9673 end = typeof end == "undefined" ? ns.length - 1 : end;
9676 for(var i = start; i <= end; i++){
9680 for(var i = start; i >= end; i--){
9688 * Finds the index of the passed node
9689 * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
9690 * @return {Number} The index of the node or -1
9692 indexOf : function(node){
9693 node = this.getNode(node);
9694 if(typeof node.nodeIndex == "number"){
9695 return node.nodeIndex;
9697 var ns = this.nodes;
9698 for(var i = 0, len = ns.length; i < len; i++){
9709 * based on jquery fullcalendar
9713 Roo.bootstrap = Roo.bootstrap || {};
9715 * @class Roo.bootstrap.Calendar
9716 * @extends Roo.bootstrap.Component
9717 * Bootstrap Calendar class
9718 * @cfg {Boolean} loadMask (true|false) default false
9721 * Create a new Container
9722 * @param {Object} config The config object
9727 Roo.bootstrap.Calendar = function(config){
9728 Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
9732 * Fires when a date is selected
9733 * @param {DatePicker} this
9734 * @param {Date} date The selected date
9738 * @event monthchange
9739 * Fires when the displayed month changes
9740 * @param {DatePicker} this
9741 * @param {Date} date The selected month
9743 'monthchange': true,
9746 * Fires when mouse over an event
9747 * @param {Calendar} this
9748 * @param {event} Event
9753 * Fires when the mouse leaves an
9754 * @param {Calendar} this
9760 * Fires when the mouse click an
9761 * @param {Calendar} this
9770 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component, {
9773 * @cfg {Number} startDay
9774 * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
9780 getAutoCreate : function(){
9783 var fc_button = function(name, corner, style, content ) {
9784 return Roo.apply({},{
9786 cls : 'fc-button fc-button-'+name+' fc-state-default ' +
9788 'fc-corner-' + corner.split(' ').join(' fc-corner-') :
9791 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
9799 style : 'width:100%',
9806 cls : 'fc-header-left',
9808 fc_button('prev', 'left', 'arrow', '‹' ),
9809 fc_button('next', 'right', 'arrow', '›' ),
9810 { tag: 'span', cls: 'fc-header-space' },
9811 fc_button('today', 'left right', '', 'today' ) // neds state disabled..
9819 cls : 'fc-header-center',
9823 cls: 'fc-header-title',
9826 html : 'month / year'
9834 cls : 'fc-header-right',
9836 /* fc_button('month', 'left', '', 'month' ),
9837 fc_button('week', '', '', 'week' ),
9838 fc_button('day', 'right', '', 'day' )
9850 var cal_heads = function() {
9852 // fixme - handle this.
9854 for (var i =0; i < Date.dayNames.length; i++) {
9855 var d = Date.dayNames[i];
9858 cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
9859 html : d.substring(0,3)
9863 ret[0].cls += ' fc-first';
9864 ret[6].cls += ' fc-last';
9867 var cal_cell = function(n) {
9870 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
9875 cls: 'fc-day-number',
9879 cls: 'fc-day-content',
9883 style: 'position: relative;' // height: 17px;
9895 var cal_rows = function() {
9898 for (var r = 0; r < 6; r++) {
9905 for (var i =0; i < Date.dayNames.length; i++) {
9906 var d = Date.dayNames[i];
9907 row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
9910 row.cn[0].cls+=' fc-first';
9911 row.cn[0].cn[0].style = 'min-height:90px';
9912 row.cn[6].cls+=' fc-last';
9916 ret[0].cls += ' fc-first';
9917 ret[4].cls += ' fc-prev-last';
9918 ret[5].cls += ' fc-last';
9925 cls: 'fc-border-separate',
9926 style : 'width:100%',
9934 cls : 'fc-first fc-last',
9953 style : "position: relative;",
9956 cls : 'fc-view fc-view-month fc-grid',
9957 style : 'position: relative',
9958 unselectable : 'on',
9961 cls : 'fc-event-container',
9962 style : 'position:absolute;z-index:8;top:0;left:0;'
9980 initEvents : function()
9983 throw "can not find store for calendar";
9989 style: "text-align:center",
9993 style: "background-color:white;width:50%;margin:250 auto",
9997 src: rootURL + '/roojs1/images/ux/lightbox/loading.gif'
10008 this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
10010 var size = this.el.select('.fc-content', true).first().getSize();
10011 this.maskEl.setSize(size.width, size.height);
10012 this.maskEl.enableDisplayMode("block");
10013 if(!this.loadMask){
10014 this.maskEl.hide();
10017 this.store = Roo.factory(this.store, Roo.data);
10018 this.store.on('load', this.onLoad, this);
10019 this.store.on('beforeload', this.onBeforeLoad, this);
10023 this.cells = this.el.select('.fc-day',true);
10024 //Roo.log(this.cells);
10025 this.textNodes = this.el.query('.fc-day-number');
10026 this.cells.addClassOnOver('fc-state-hover');
10028 this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
10029 this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
10030 this.el.select('.fc-button-today',true).on('click', this.showToday, this);
10031 this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
10033 this.on('monthchange', this.onMonthChange, this);
10035 this.update(new Date().clearTime());
10038 resize : function() {
10039 var sz = this.el.getSize();
10041 this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
10042 this.el.select('.fc-day-content div',true).setHeight(34);
10047 showPrevMonth : function(e){
10048 this.update(this.activeDate.add("mo", -1));
10050 showToday : function(e){
10051 this.update(new Date().clearTime());
10054 showNextMonth : function(e){
10055 this.update(this.activeDate.add("mo", 1));
10059 showPrevYear : function(){
10060 this.update(this.activeDate.add("y", -1));
10064 showNextYear : function(){
10065 this.update(this.activeDate.add("y", 1));
10070 update : function(date)
10072 var vd = this.activeDate;
10073 this.activeDate = date;
10074 // if(vd && this.el){
10075 // var t = date.getTime();
10076 // if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
10077 // Roo.log('using add remove');
10079 // this.fireEvent('monthchange', this, date);
10081 // this.cells.removeClass("fc-state-highlight");
10082 // this.cells.each(function(c){
10083 // if(c.dateValue == t){
10084 // c.addClass("fc-state-highlight");
10085 // setTimeout(function(){
10086 // try{c.dom.firstChild.focus();}catch(e){}
10096 var days = date.getDaysInMonth();
10098 var firstOfMonth = date.getFirstDateOfMonth();
10099 var startingPos = firstOfMonth.getDay()-this.startDay;
10101 if(startingPos < this.startDay){
10105 var pm = date.add(Date.MONTH, -1);
10106 var prevStart = pm.getDaysInMonth()-startingPos;
10108 this.cells = this.el.select('.fc-day',true);
10109 this.textNodes = this.el.query('.fc-day-number');
10110 this.cells.addClassOnOver('fc-state-hover');
10112 var cells = this.cells.elements;
10113 var textEls = this.textNodes;
10115 Roo.each(cells, function(cell){
10116 cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
10119 days += startingPos;
10121 // convert everything to numbers so it's fast
10122 var day = 86400000;
10123 var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
10126 //Roo.log(prevStart);
10128 var today = new Date().clearTime().getTime();
10129 var sel = date.clearTime().getTime();
10130 var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
10131 var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
10132 var ddMatch = this.disabledDatesRE;
10133 var ddText = this.disabledDatesText;
10134 var ddays = this.disabledDays ? this.disabledDays.join("") : false;
10135 var ddaysText = this.disabledDaysText;
10136 var format = this.format;
10138 var setCellClass = function(cal, cell){
10140 //Roo.log('set Cell Class');
10142 var t = d.getTime();
10146 cell.dateValue = t;
10148 cell.className += " fc-today";
10149 cell.className += " fc-state-highlight";
10150 cell.title = cal.todayText;
10153 // disable highlight in other month..
10154 //cell.className += " fc-state-highlight";
10159 cell.className = " fc-state-disabled";
10160 cell.title = cal.minText;
10164 cell.className = " fc-state-disabled";
10165 cell.title = cal.maxText;
10169 if(ddays.indexOf(d.getDay()) != -1){
10170 cell.title = ddaysText;
10171 cell.className = " fc-state-disabled";
10174 if(ddMatch && format){
10175 var fvalue = d.dateFormat(format);
10176 if(ddMatch.test(fvalue)){
10177 cell.title = ddText.replace("%0", fvalue);
10178 cell.className = " fc-state-disabled";
10182 if (!cell.initialClassName) {
10183 cell.initialClassName = cell.dom.className;
10186 cell.dom.className = cell.initialClassName + ' ' + cell.className;
10191 for(; i < startingPos; i++) {
10192 textEls[i].innerHTML = (++prevStart);
10193 d.setDate(d.getDate()+1);
10195 cells[i].className = "fc-past fc-other-month";
10196 setCellClass(this, cells[i]);
10201 for(; i < days; i++){
10202 intDay = i - startingPos + 1;
10203 textEls[i].innerHTML = (intDay);
10204 d.setDate(d.getDate()+1);
10206 cells[i].className = ''; // "x-date-active";
10207 setCellClass(this, cells[i]);
10211 for(; i < 42; i++) {
10212 textEls[i].innerHTML = (++extraDays);
10213 d.setDate(d.getDate()+1);
10215 cells[i].className = "fc-future fc-other-month";
10216 setCellClass(this, cells[i]);
10219 this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
10221 var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
10223 this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
10224 this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
10226 if(totalRows != 6){
10227 this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
10228 this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
10231 this.fireEvent('monthchange', this, date);
10235 if(!this.internalRender){
10236 var main = this.el.dom.firstChild;
10237 var w = main.offsetWidth;
10238 this.el.setWidth(w + this.el.getBorderWidth("lr"));
10239 Roo.fly(main).setWidth(w);
10240 this.internalRender = true;
10241 // opera does not respect the auto grow header center column
10242 // then, after it gets a width opera refuses to recalculate
10243 // without a second pass
10244 if(Roo.isOpera && !this.secondPass){
10245 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
10246 this.secondPass = true;
10247 this.update.defer(10, this, [date]);
10254 findCell : function(dt) {
10255 dt = dt.clearTime().getTime();
10257 this.cells.each(function(c){
10258 //Roo.log("check " +c.dateValue + '?=' + dt);
10259 if(c.dateValue == dt){
10269 findCells : function(ev) {
10270 var s = ev.start.clone().clearTime().getTime();
10272 var e= ev.end.clone().clearTime().getTime();
10275 this.cells.each(function(c){
10276 ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
10278 if(c.dateValue > e){
10281 if(c.dateValue < s){
10290 findBestRow: function(cells)
10294 for (var i =0 ; i < cells.length;i++) {
10295 ret = Math.max(cells[i].rows || 0,ret);
10302 addItem : function(ev)
10304 // look for vertical location slot in
10305 var cells = this.findCells(ev);
10307 ev.row = this.findBestRow(cells);
10309 // work out the location.
10313 for(var i =0; i < cells.length; i++) {
10321 if (crow.start.getY() == cells[i].getY()) {
10323 crow.end = cells[i];
10339 for (var i = 0; i < cells.length;i++) {
10340 cells[i].rows = Math.max(cells[i].rows || 0 , ev.row + 1 );
10344 this.calevents.push(ev);
10347 clearEvents: function() {
10349 if(!this.calevents){
10353 Roo.each(this.cells.elements, function(c){
10357 Roo.each(this.calevents, function(e) {
10358 Roo.each(e.els, function(el) {
10359 el.un('mouseenter' ,this.onEventEnter, this);
10360 el.un('mouseleave' ,this.onEventLeave, this);
10367 renderEvents: function()
10369 // first make sure there is enough space..
10371 this.cells.each(function(c) {
10373 c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.rows * 20));
10376 for (var e = 0; e < this.calevents.length; e++) {
10377 var ev = this.calevents[e];
10378 var cells = ev.cells;
10379 var rows = ev.rows;
10381 for(var i =0; i < rows.length; i++) {
10384 // how many rows should it span..
10387 cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
10388 style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
10390 unselectable : "on",
10393 cls: 'fc-event-inner',
10397 // cls: 'fc-event-time',
10398 // html : cells.length > 1 ? '' : ev.time
10402 cls: 'fc-event-title',
10403 html : String.format('{0}', ev.title)
10410 cls: 'ui-resizable-handle ui-resizable-e',
10411 html : '  '
10417 cfg.cls += ' fc-event-start';
10419 if ((i+1) == rows.length) {
10420 cfg.cls += ' fc-event-end';
10423 var ctr = this.el.select('.fc-event-container',true).first();
10424 var cg = ctr.createChild(cfg);
10426 cg.on('mouseenter' ,this.onEventEnter, this, ev);
10427 cg.on('mouseleave' ,this.onEventLeave, this, ev);
10428 cg.on('click', this.onEventClick, this, ev);
10432 var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
10433 var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
10435 cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);
10436 cg.setWidth(ebox.right - sbox.x -2);
10444 onEventEnter: function (e, el,event,d) {
10445 this.fireEvent('evententer', this, el, event);
10448 onEventLeave: function (e, el,event,d) {
10449 this.fireEvent('eventleave', this, el, event);
10452 onEventClick: function (e, el,event,d) {
10453 this.fireEvent('eventclick', this, el, event);
10456 onMonthChange: function () {
10460 onLoad: function ()
10462 this.calevents = [];
10465 if(this.store.getCount() > 0){
10466 this.store.data.each(function(d){
10469 start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
10470 end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
10471 time : d.data.start_time,
10472 title : d.data.title,
10473 description : d.data.description,
10474 venue : d.data.venue
10479 this.renderEvents();
10482 this.maskEl.hide();
10486 onBeforeLoad: function()
10488 this.clearEvents();
10491 this.maskEl.show();
10505 * @class Roo.bootstrap.Popover
10506 * @extends Roo.bootstrap.Component
10507 * Bootstrap Popover class
10508 * @cfg {String} html contents of the popover (or false to use children..)
10509 * @cfg {String} title of popover (or false to hide)
10510 * @cfg {String} placement how it is placed
10511 * @cfg {String} trigger click || hover (or false to trigger manually)
10512 * @cfg {String} over what (parent or false to trigger manually.)
10515 * Create a new Popover
10516 * @param {Object} config The config object
10519 Roo.bootstrap.Popover = function(config){
10520 Roo.bootstrap.Popover.superclass.constructor.call(this, config);
10523 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component, {
10525 title: 'Fill in a title',
10528 placement : 'right',
10529 trigger : 'hover', // hover
10533 can_build_overlaid : false,
10535 getChildContainer : function()
10537 return this.el.select('.popover-content',true).first();
10540 getAutoCreate : function(){
10541 Roo.log('make popover?');
10543 cls : 'popover roo-dynamic',
10544 style: 'display:block',
10550 cls : 'popover-inner',
10554 cls: 'popover-title',
10558 cls : 'popover-content',
10569 setTitle: function(str)
10571 this.el.select('.popover-title',true).first().dom.innerHTML = str;
10573 setContent: function(str)
10575 this.el.select('.popover-content',true).first().dom.innerHTML = str;
10577 // as it get's added to the bottom of the page.
10578 onRender : function(ct, position)
10580 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
10582 var cfg = Roo.apply({}, this.getAutoCreate());
10586 cfg.cls += ' ' + this.cls;
10589 cfg.style = this.style;
10591 Roo.log("adding to ")
10592 this.el = Roo.get(document.body).createChild(cfg, position);
10598 initEvents : function()
10600 this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
10601 this.el.enableDisplayMode('block');
10603 if (this.over === false) {
10606 if (this.triggers === false) {
10609 var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10610 var triggers = this.trigger ? this.trigger.split(' ') : [];
10611 Roo.each(triggers, function(trigger) {
10613 if (trigger == 'click') {
10614 on_el.on('click', this.toggle, this);
10615 } else if (trigger != 'manual') {
10616 var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin'
10617 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
10619 on_el.on(eventIn ,this.enter, this);
10620 on_el.on(eventOut, this.leave, this);
10631 toggle : function () {
10632 this.hoverState == 'in' ? this.leave() : this.enter();
10635 enter : function () {
10638 clearTimeout(this.timeout);
10640 this.hoverState = 'in'
10642 if (!this.delay || !this.delay.show) {
10647 this.timeout = setTimeout(function () {
10648 if (_t.hoverState == 'in') {
10651 }, this.delay.show)
10653 leave : function() {
10654 clearTimeout(this.timeout);
10656 this.hoverState = 'out'
10658 if (!this.delay || !this.delay.hide) {
10663 this.timeout = setTimeout(function () {
10664 if (_t.hoverState == 'out') {
10667 }, this.delay.hide)
10670 show : function (on_el)
10673 on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
10676 this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
10677 if (this.html !== false) {
10678 this.el.select('.popover-content',true).first().dom.innerHtml = this.title;
10680 this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
10681 if (!this.title.length) {
10682 this.el.select('.popover-title',true).hide();
10685 var placement = typeof this.placement == 'function' ?
10686 this.placement.call(this, this.el, on_el) :
10689 var autoToken = /\s?auto?\s?/i;
10690 var autoPlace = autoToken.test(placement);
10692 placement = placement.replace(autoToken, '') || 'top';
10696 //this.el.setXY([0,0]);
10698 this.el.dom.style.display='block';
10699 this.el.addClass(placement);
10701 //this.el.appendTo(on_el);
10703 var p = this.getPosition();
10704 var box = this.el.getBox();
10709 var align = Roo.bootstrap.Popover.alignment[placement]
10710 this.el.alignTo(on_el, align[0],align[1]);
10711 //var arrow = this.el.select('.arrow',true).first();
10712 //arrow.set(align[2],
10714 this.el.addClass('in');
10715 this.hoverState = null;
10717 if (this.el.hasClass('fade')) {
10724 this.el.setXY([0,0]);
10725 this.el.removeClass('in');
10732 Roo.bootstrap.Popover.alignment = {
10733 'left' : ['r-l', [-10,0], 'right'],
10734 'right' : ['l-r', [10,0], 'left'],
10735 'bottom' : ['t-b', [0,10], 'top'],
10736 'top' : [ 'b-t', [0,-10], 'bottom']
10747 * @class Roo.bootstrap.Progress
10748 * @extends Roo.bootstrap.Component
10749 * Bootstrap Progress class
10750 * @cfg {Boolean} striped striped of the progress bar
10751 * @cfg {Boolean} active animated of the progress bar
10755 * Create a new Progress
10756 * @param {Object} config The config object
10759 Roo.bootstrap.Progress = function(config){
10760 Roo.bootstrap.Progress.superclass.constructor.call(this, config);
10763 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component, {
10768 getAutoCreate : function(){
10776 cfg.cls += ' progress-striped';
10780 cfg.cls += ' active';
10799 * @class Roo.bootstrap.ProgressBar
10800 * @extends Roo.bootstrap.Component
10801 * Bootstrap ProgressBar class
10802 * @cfg {Number} aria_valuenow aria-value now
10803 * @cfg {Number} aria_valuemin aria-value min
10804 * @cfg {Number} aria_valuemax aria-value max
10805 * @cfg {String} label label for the progress bar
10806 * @cfg {String} panel (success | info | warning | danger )
10807 * @cfg {String} role role of the progress bar
10808 * @cfg {String} sr_only text
10812 * Create a new ProgressBar
10813 * @param {Object} config The config object
10816 Roo.bootstrap.ProgressBar = function(config){
10817 Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
10820 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component, {
10824 aria_valuemax : 100,
10830 getAutoCreate : function()
10835 cls: 'progress-bar',
10836 style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
10848 cfg.role = this.role;
10851 if(this.aria_valuenow){
10852 cfg['aria-valuenow'] = this.aria_valuenow;
10855 if(this.aria_valuemin){
10856 cfg['aria-valuemin'] = this.aria_valuemin;
10859 if(this.aria_valuemax){
10860 cfg['aria-valuemax'] = this.aria_valuemax;
10863 if(this.label && !this.sr_only){
10864 cfg.html = this.label;
10868 cfg.cls += ' progress-bar-' + this.panel;
10874 update : function(aria_valuenow)
10876 this.aria_valuenow = aria_valuenow;
10878 this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
10893 * @class Roo.bootstrap.TabPanel
10894 * @extends Roo.bootstrap.Component
10895 * Bootstrap TabPanel class
10896 * @cfg {Boolean} active panel active
10897 * @cfg {String} html panel content
10898 * @cfg {String} tabId tab relate id
10902 * Create a new TabPanel
10903 * @param {Object} config The config object
10906 Roo.bootstrap.TabPanel = function(config){
10907 Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
10910 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component, {
10916 getAutoCreate : function(){
10920 html: this.html || ''
10924 cfg.cls += ' active';
10928 cfg.tabId = this.tabId;
10946 * @class Roo.bootstrap.DateField
10947 * @extends Roo.bootstrap.Input
10948 * Bootstrap DateField class
10949 * @cfg {Number} weekStart default 0
10950 * @cfg {Number} weekStart default 0
10951 * @cfg {Number} viewMode default empty, (months|years)
10952 * @cfg {Number} minViewMode default empty, (months|years)
10953 * @cfg {Number} startDate default -Infinity
10954 * @cfg {Number} endDate default Infinity
10955 * @cfg {Boolean} todayHighlight default false
10956 * @cfg {Boolean} todayBtn default false
10957 * @cfg {Boolean} calendarWeeks default false
10958 * @cfg {Object} daysOfWeekDisabled default empty
10960 * @cfg {Boolean} keyboardNavigation default true
10961 * @cfg {String} language default en
10964 * Create a new DateField
10965 * @param {Object} config The config object
10968 Roo.bootstrap.DateField = function(config){
10969 Roo.bootstrap.DateField.superclass.constructor.call(this, config);
10973 * Fires when this field show.
10974 * @param {Roo.bootstrap.DateField} this
10975 * @param {Mixed} date The date value
10980 * Fires when this field hide.
10981 * @param {Roo.bootstrap.DateField} this
10982 * @param {Mixed} date The date value
10987 * Fires when select a date.
10988 * @param {Roo.bootstrap.DateField} this
10989 * @param {Mixed} date The date value
10995 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input, {
10998 * @cfg {String} format
10999 * The default date format string which can be overriden for localization support. The format must be
11000 * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
11004 * @cfg {String} altFormats
11005 * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
11006 * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
11008 altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
11016 todayHighlight : false,
11022 keyboardNavigation: true,
11024 calendarWeeks: false,
11026 startDate: -Infinity,
11030 daysOfWeekDisabled: [],
11034 UTCDate: function()
11036 return new Date(Date.UTC.apply(Date, arguments));
11039 UTCToday: function()
11041 var today = new Date();
11042 return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
11045 getDate: function() {
11046 var d = this.getUTCDate();
11047 return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
11050 getUTCDate: function() {
11054 setDate: function(d) {
11055 this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
11058 setUTCDate: function(d) {
11060 this.setValue(this.formatDate(this.date));
11063 onRender: function(ct, position)
11066 Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
11068 this.language = this.language || 'en';
11069 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
11070 this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
11072 this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
11073 this.format = this.format || 'm/d/y';
11074 this.isInline = false;
11075 this.isInput = true;
11076 this.component = this.el.select('.add-on', true).first() || false;
11077 this.component = (this.component && this.component.length === 0) ? false : this.component;
11078 this.hasInput = this.component && this.inputEL().length;
11080 if (typeof(this.minViewMode === 'string')) {
11081 switch (this.minViewMode) {
11083 this.minViewMode = 1;
11086 this.minViewMode = 2;
11089 this.minViewMode = 0;
11094 if (typeof(this.viewMode === 'string')) {
11095 switch (this.viewMode) {
11108 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
11110 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11112 this.picker().on('mousedown', this.onMousedown, this);
11113 this.picker().on('click', this.onClick, this);
11115 this.picker().addClass('datepicker-dropdown');
11117 this.startViewMode = this.viewMode;
11120 Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
11121 if(!this.calendarWeeks){
11126 v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today
11127 v.attr('colspan', function(i, val){
11128 return parseInt(val) + 1;
11133 this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
11135 this.setStartDate(this.startDate);
11136 this.setEndDate(this.endDate);
11138 this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
11145 if(this.isInline) {
11150 picker : function()
11152 return this.el.select('.datepicker', true).first();
11155 fillDow: function()
11157 var dowCnt = this.weekStart;
11166 if(this.calendarWeeks){
11174 while (dowCnt < this.weekStart + 7) {
11178 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
11182 this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
11185 fillMonths: function()
11188 var months = this.picker().select('>.datepicker-months td', true).first();
11190 months.dom.innerHTML = '';
11196 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
11199 months.createChild(month);
11204 update: function(){
11206 this.date = (typeof(this.date) === 'undefined') ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
11208 if (this.date < this.startDate) {
11209 this.viewDate = new Date(this.startDate);
11210 } else if (this.date > this.endDate) {
11211 this.viewDate = new Date(this.endDate);
11213 this.viewDate = new Date(this.date);
11220 var d = new Date(this.viewDate),
11221 year = d.getUTCFullYear(),
11222 month = d.getUTCMonth(),
11223 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
11224 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
11225 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
11226 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
11227 currentDate = this.date && this.date.valueOf(),
11228 today = this.UTCToday();
11230 this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
11232 // this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
11234 // this.picker.select('>tfoot th.today').
11235 // .text(dates[this.language].today)
11236 // .toggle(this.todayBtn !== false);
11238 this.updateNavArrows();
11241 var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
11243 day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
11245 prevMonth.setUTCDate(day);
11247 prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
11249 var nextMonth = new Date(prevMonth);
11251 nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
11253 nextMonth = nextMonth.valueOf();
11255 var fillMonths = false;
11257 this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
11259 while(prevMonth.valueOf() < nextMonth) {
11262 if (prevMonth.getUTCDay() === this.weekStart) {
11264 this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
11272 if(this.calendarWeeks){
11273 // ISO 8601: First week contains first thursday.
11274 // ISO also states week starts on Monday, but we can be more abstract here.
11276 // Start of current week: based on weekstart/current date
11277 ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
11278 // Thursday of this week
11279 th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
11280 // First Thursday of year, year from thursday
11281 yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
11282 // Calendar week: ms between thursdays, div ms per day, div 7 days
11283 calWeek = (th - yth) / 864e5 / 7 + 1;
11285 fillMonths.cn.push({
11293 if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
11295 } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
11298 if (this.todayHighlight &&
11299 prevMonth.getUTCFullYear() == today.getFullYear() &&
11300 prevMonth.getUTCMonth() == today.getMonth() &&
11301 prevMonth.getUTCDate() == today.getDate()) {
11302 clsName += ' today';
11305 if (currentDate && prevMonth.valueOf() === currentDate) {
11306 clsName += ' active';
11309 if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
11310 this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
11311 clsName += ' disabled';
11314 fillMonths.cn.push({
11316 cls: 'day ' + clsName,
11317 html: prevMonth.getDate()
11320 prevMonth.setDate(prevMonth.getDate()+1);
11323 var currentYear = this.date && this.date.getUTCFullYear();
11324 var currentMonth = this.date && this.date.getUTCMonth();
11326 this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
11328 Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
11329 v.removeClass('active');
11331 if(currentYear === year && k === currentMonth){
11332 v.addClass('active');
11335 if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
11336 v.addClass('disabled');
11342 year = parseInt(year/10, 10) * 10;
11344 this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
11346 this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
11349 for (var i = -1; i < 11; i++) {
11350 this.picker().select('>.datepicker-years tbody td',true).first().createChild({
11352 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
11360 showMode: function(dir) {
11362 this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
11364 Roo.each(this.picker().select('>div',true).elements, function(v){
11365 v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11368 this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
11373 if(this.isInline) return;
11375 this.picker().removeClass(['bottom', 'top']);
11377 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
11379 * place to the top of element!
11383 this.picker().addClass('top');
11384 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11389 this.picker().addClass('bottom');
11391 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
11394 parseDate : function(value){
11395 if(!value || value instanceof Date){
11398 var v = Date.parseDate(value, this.format);
11399 if (!v && this.useIso) {
11400 v = Date.parseDate(value, 'Y-m-d');
11402 if(!v && this.altFormats){
11403 if(!this.altFormatsArray){
11404 this.altFormatsArray = this.altFormats.split("|");
11406 for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
11407 v = Date.parseDate(value, this.altFormatsArray[i]);
11413 formatDate : function(date, fmt){
11414 return (!date || !(date instanceof Date)) ?
11415 date : date.dateFormat(fmt || this.format);
11418 onFocus : function()
11420 Roo.bootstrap.DateField.superclass.onFocus.call(this);
11424 onBlur : function()
11426 Roo.bootstrap.DateField.superclass.onBlur.call(this);
11432 this.picker().show();
11436 this.fireEvent('show', this, this.date);
11441 if(this.isInline) return;
11442 this.picker().hide();
11443 this.viewMode = this.startViewMode;
11446 this.fireEvent('hide', this, this.date);
11450 onMousedown: function(e){
11451 e.stopPropagation();
11452 e.preventDefault();
11455 keyup: function(e){
11456 Roo.bootstrap.DateField.superclass.keyup.call(this);
11461 setValue: function(v){
11462 Roo.bootstrap.DateField.superclass.setValue.call(this, v);
11464 this.fireEvent('select', this, this.date);
11468 fireKey: function(e){
11469 if (!this.picker().isVisible()){
11470 if (e.keyCode == 27) // allow escape to hide and re-show picker
11474 var dateChanged = false,
11476 newDate, newViewDate;
11480 e.preventDefault();
11484 if (!this.keyboardNavigation) break;
11485 dir = e.keyCode == 37 ? -1 : 1;
11488 newDate = this.moveYear(this.date, dir);
11489 newViewDate = this.moveYear(this.viewDate, dir);
11490 } else if (e.shiftKey){
11491 newDate = this.moveMonth(this.date, dir);
11492 newViewDate = this.moveMonth(this.viewDate, dir);
11494 newDate = new Date(this.date);
11495 newDate.setUTCDate(this.date.getUTCDate() + dir);
11496 newViewDate = new Date(this.viewDate);
11497 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
11499 if (this.dateWithinRange(newDate)){
11500 this.date = newDate;
11501 this.viewDate = newViewDate;
11502 this.setValue(this.formatDate(this.date));
11504 e.preventDefault();
11505 dateChanged = true;
11510 if (!this.keyboardNavigation) break;
11511 dir = e.keyCode == 38 ? -1 : 1;
11513 newDate = this.moveYear(this.date, dir);
11514 newViewDate = this.moveYear(this.viewDate, dir);
11515 } else if (e.shiftKey){
11516 newDate = this.moveMonth(this.date, dir);
11517 newViewDate = this.moveMonth(this.viewDate, dir);
11519 newDate = new Date(this.date);
11520 newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
11521 newViewDate = new Date(this.viewDate);
11522 newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
11524 if (this.dateWithinRange(newDate)){
11525 this.date = newDate;
11526 this.viewDate = newViewDate;
11527 this.setValue(this.formatDate(this.date));
11529 e.preventDefault();
11530 dateChanged = true;
11534 this.setValue(this.formatDate(this.date));
11536 e.preventDefault();
11539 this.setValue(this.formatDate(this.date));
11546 onClick: function(e) {
11547 e.stopPropagation();
11548 e.preventDefault();
11550 var target = e.getTarget();
11552 if(target.nodeName.toLowerCase() === 'i'){
11553 target = Roo.get(target).dom.parentNode;
11556 var nodeName = target.nodeName;
11557 var className = target.className;
11558 var html = target.innerHTML;
11560 switch(nodeName.toLowerCase()) {
11562 switch(className) {
11568 var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
11569 switch(this.viewMode){
11571 this.viewDate = this.moveMonth(this.viewDate, dir);
11575 this.viewDate = this.moveYear(this.viewDate, dir);
11581 var date = new Date();
11582 this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
11584 this.setValue(this.formatDate(this.date));
11590 if (className.indexOf('disabled') === -1) {
11591 this.viewDate.setUTCDate(1);
11592 if (className.indexOf('month') !== -1) {
11593 this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
11595 var year = parseInt(html, 10) || 0;
11596 this.viewDate.setUTCFullYear(year);
11605 if (className.indexOf('day') !== -1 && className.indexOf('disabled') === -1){
11606 var day = parseInt(html, 10) || 1;
11607 var year = this.viewDate.getUTCFullYear(),
11608 month = this.viewDate.getUTCMonth();
11610 if (className.indexOf('old') !== -1) {
11617 } else if (className.indexOf('new') !== -1) {
11625 this.date = this.UTCDate(year, month, day,0,0,0,0);
11626 this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
11628 this.setValue(this.formatDate(this.date));
11635 setStartDate: function(startDate){
11636 this.startDate = startDate || -Infinity;
11637 if (this.startDate !== -Infinity) {
11638 this.startDate = this.parseDate(this.startDate);
11641 this.updateNavArrows();
11644 setEndDate: function(endDate){
11645 this.endDate = endDate || Infinity;
11646 if (this.endDate !== Infinity) {
11647 this.endDate = this.parseDate(this.endDate);
11650 this.updateNavArrows();
11653 setDaysOfWeekDisabled: function(daysOfWeekDisabled){
11654 this.daysOfWeekDisabled = daysOfWeekDisabled || [];
11655 if (typeof(this.daysOfWeekDisabled) !== 'object') {
11656 this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
11658 this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
11659 return parseInt(d, 10);
11662 this.updateNavArrows();
11665 updateNavArrows: function() {
11666 var d = new Date(this.viewDate),
11667 year = d.getUTCFullYear(),
11668 month = d.getUTCMonth();
11670 Roo.each(this.picker().select('.prev', true).elements, function(v){
11672 switch (this.viewMode) {
11675 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
11681 if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
11688 Roo.each(this.picker().select('.next', true).elements, function(v){
11690 switch (this.viewMode) {
11693 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
11699 if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
11707 moveMonth: function(date, dir){
11708 if (!dir) return date;
11709 var new_date = new Date(date.valueOf()),
11710 day = new_date.getUTCDate(),
11711 month = new_date.getUTCMonth(),
11712 mag = Math.abs(dir),
11714 dir = dir > 0 ? 1 : -1;
11717 // If going back one month, make sure month is not current month
11718 // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
11720 return new_date.getUTCMonth() == month;
11722 // If going forward one month, make sure month is as expected
11723 // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
11725 return new_date.getUTCMonth() != new_month;
11727 new_month = month + dir;
11728 new_date.setUTCMonth(new_month);
11729 // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
11730 if (new_month < 0 || new_month > 11)
11731 new_month = (new_month + 12) % 12;
11733 // For magnitudes >1, move one month at a time...
11734 for (var i=0; i<mag; i++)
11735 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
11736 new_date = this.moveMonth(new_date, dir);
11737 // ...then reset the day, keeping it in the new month
11738 new_month = new_date.getUTCMonth();
11739 new_date.setUTCDate(day);
11741 return new_month != new_date.getUTCMonth();
11744 // Common date-resetting loop -- if date is beyond end of month, make it
11747 new_date.setUTCDate(--day);
11748 new_date.setUTCMonth(new_month);
11753 moveYear: function(date, dir){
11754 return this.moveMonth(date, dir*12);
11757 dateWithinRange: function(date){
11758 return date >= this.startDate && date <= this.endDate;
11762 remove: function() {
11763 this.picker().remove();
11768 Roo.apply(Roo.bootstrap.DateField, {
11779 html: '<i class="icon-arrow-left"/>'
11789 html: '<i class="icon-arrow-right"/>'
11831 days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
11832 daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
11833 daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
11834 months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
11835 monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
11848 navFnc: 'FullYear',
11853 navFnc: 'FullYear',
11858 Roo.apply(Roo.bootstrap.DateField, {
11862 cls: 'datepicker dropdown-menu',
11866 cls: 'datepicker-days',
11870 cls: 'table-condensed',
11872 Roo.bootstrap.DateField.head,
11876 Roo.bootstrap.DateField.footer
11883 cls: 'datepicker-months',
11887 cls: 'table-condensed',
11889 Roo.bootstrap.DateField.head,
11890 Roo.bootstrap.DateField.content,
11891 Roo.bootstrap.DateField.footer
11898 cls: 'datepicker-years',
11902 cls: 'table-condensed',
11904 Roo.bootstrap.DateField.head,
11905 Roo.bootstrap.DateField.content,
11906 Roo.bootstrap.DateField.footer
11925 * @class Roo.bootstrap.TimeField
11926 * @extends Roo.bootstrap.Input
11927 * Bootstrap DateField class
11931 * Create a new TimeField
11932 * @param {Object} config The config object
11935 Roo.bootstrap.TimeField = function(config){
11936 Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
11940 * Fires when this field show.
11941 * @param {Roo.bootstrap.DateField} this
11942 * @param {Mixed} date The date value
11947 * Fires when this field hide.
11948 * @param {Roo.bootstrap.DateField} this
11949 * @param {Mixed} date The date value
11954 * Fires when select a date.
11955 * @param {Roo.bootstrap.DateField} this
11956 * @param {Mixed} date The date value
11962 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input, {
11965 * @cfg {String} format
11966 * The default time format string which can be overriden for localization support. The format must be
11967 * valid according to {@link Date#parseDate} (defaults to 'H:i').
11971 onRender: function(ct, position)
11974 Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
11976 this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
11978 this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
11980 this.pop = this.picker().select('>.datepicker-time',true).first();
11981 this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block'
11983 this.picker().on('mousedown', this.onMousedown, this);
11984 this.picker().on('click', this.onClick, this);
11986 this.picker().addClass('datepicker-dropdown');
11991 this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
11992 this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
11993 this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
11994 this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
11995 this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
11996 this.pop.select('button.ok', true).first().on('click', this.setTime, this);
12000 fireKey: function(e){
12001 if (!this.picker().isVisible()){
12002 if (e.keyCode == 27) // allow escape to hide and re-show picker
12007 e.preventDefault();
12015 this.onTogglePeriod();
12018 this.onIncrementMinutes();
12021 this.onDecrementMinutes();
12030 onClick: function(e) {
12031 e.stopPropagation();
12032 e.preventDefault();
12035 picker : function()
12037 return this.el.select('.datepicker', true).first();
12040 fillTime: function()
12042 var time = this.pop.select('tbody', true).first();
12044 time.dom.innerHTML = '';
12059 cls: 'hours-up glyphicon glyphicon-chevron-up'
12079 cls: 'minutes-up glyphicon glyphicon-chevron-up'
12100 cls: 'timepicker-hour',
12115 cls: 'timepicker-minute',
12130 cls: 'btn btn-primary period',
12152 cls: 'hours-down glyphicon glyphicon-chevron-down'
12172 cls: 'minutes-down glyphicon glyphicon-chevron-down'
12190 this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
12197 var hours = this.time.getHours();
12198 var minutes = this.time.getMinutes();
12211 hours = hours - 12;
12215 hours = '0' + hours;
12219 minutes = '0' + minutes;
12222 this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
12223 this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
12224 this.pop.select('button', true).first().dom.innerHTML = period;
12230 this.picker().removeClass(['bottom', 'top']);
12232 if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
12234 * place to the top of element!
12238 this.picker().addClass('top');
12239 this.picker().setTop(0 - this.picker().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12244 this.picker().addClass('bottom');
12246 this.picker().setTop(this.inputEl().getHeight()).setLeft(this.inputEl().getLeft() - this.el.getLeft());
12249 onFocus : function()
12251 Roo.bootstrap.TimeField.superclass.onFocus.call(this);
12255 onBlur : function()
12257 Roo.bootstrap.TimeField.superclass.onBlur.call(this);
12263 this.picker().show();
12268 this.fireEvent('show', this, this.date);
12273 this.picker().hide();
12276 this.fireEvent('hide', this, this.date);
12279 setTime : function()
12282 this.setValue(this.time.format(this.format));
12284 this.fireEvent('select', this, this.date);
12289 onMousedown: function(e){
12290 e.stopPropagation();
12291 e.preventDefault();
12294 onIncrementHours: function()
12296 Roo.log('onIncrementHours');
12297 this.time = this.time.add(Date.HOUR, 1);
12302 onDecrementHours: function()
12304 Roo.log('onDecrementHours');
12305 this.time = this.time.add(Date.HOUR, -1);
12309 onIncrementMinutes: function()
12311 Roo.log('onIncrementMinutes');
12312 this.time = this.time.add(Date.MINUTE, 1);
12316 onDecrementMinutes: function()
12318 Roo.log('onDecrementMinutes');
12319 this.time = this.time.add(Date.MINUTE, -1);
12323 onTogglePeriod: function()
12325 Roo.log('onTogglePeriod');
12326 this.time = this.time.add(Date.HOUR, 12);
12333 Roo.apply(Roo.bootstrap.TimeField, {
12363 cls: 'btn btn-info ok',
12375 Roo.apply(Roo.bootstrap.TimeField, {
12379 cls: 'datepicker dropdown-menu',
12383 cls: 'datepicker-time',
12387 cls: 'table-condensed',
12389 Roo.bootstrap.TimeField.content,
12390 Roo.bootstrap.TimeField.footer
12409 * @class Roo.bootstrap.CheckBox
12410 * @extends Roo.bootstrap.Input
12411 * Bootstrap CheckBox class
12413 * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
12414 * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
12415 * @cfg {String} boxLabel The text that appears beside the checkbox
12416 * @cfg {Boolean} checked initnal the element
12419 * Create a new CheckBox
12420 * @param {Object} config The config object
12423 Roo.bootstrap.CheckBox = function(config){
12424 Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
12429 * Fires when the element is checked or unchecked.
12430 * @param {Roo.bootstrap.CheckBox} this This input
12431 * @param {Boolean} checked The new checked value
12437 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input, {
12439 inputType: 'checkbox',
12445 getAutoCreate : function()
12447 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12453 cfg.cls = 'form-group' //input-group
12458 type : this.inputType,
12459 value : (!this.checked) ? this.valueOff : this.inputValue,
12461 placeholder : this.placeholder || ''
12465 if (this.disabled) {
12466 input.disabled=true;
12470 input.checked = this.checked;
12474 input.name = this.name;
12478 input.cls += ' input-' + this.size;
12482 ['xs','sm','md','lg'].map(function(size){
12483 if (settings[size]) {
12484 cfg.cls += ' col-' + size + '-' + settings[size];
12488 var inputblock = input;
12490 if (this.before || this.after) {
12493 cls : 'input-group',
12497 inputblock.cn.push({
12499 cls : 'input-group-addon',
12503 inputblock.cn.push(input);
12505 inputblock.cn.push({
12507 cls : 'input-group-addon',
12514 if (align ==='left' && this.fieldLabel.length) {
12515 Roo.log("left and has label");
12521 cls : 'control-label col-md-' + this.labelWidth,
12522 html : this.fieldLabel
12526 cls : "col-md-" + (12 - this.labelWidth),
12533 } else if ( this.fieldLabel.length) {
12538 tag: this.boxLabel ? 'span' : 'label',
12540 cls: 'control-label box-input-label',
12541 //cls : 'input-group-addon',
12542 html : this.fieldLabel
12552 Roo.log(" no label && no align");
12567 html: this.boxLabel
12576 * return the real input element.
12578 inputEl: function ()
12580 return this.el.select('input.form-box',true).first();
12583 initEvents : function()
12585 // Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
12587 this.inputEl().on('click', this.onClick, this);
12591 onClick : function()
12593 this.setChecked(!this.checked);
12596 setChecked : function(state,suppressEvent)
12598 this.checked = state;
12600 this.inputEl().dom.checked = state;
12602 if(suppressEvent !== true){
12603 this.fireEvent('check', this, state);
12606 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12610 setValue : function(v,suppressEvent)
12612 this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
12626 * @class Roo.bootstrap.Radio
12627 * @extends Roo.bootstrap.CheckBox
12628 * Bootstrap Radio class
12631 * Create a new Radio
12632 * @param {Object} config The config object
12635 Roo.bootstrap.Radio = function(config){
12636 Roo.bootstrap.Radio.superclass.constructor.call(this, config);
12640 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox, {
12642 inputType: 'radio',
12646 getAutoCreate : function()
12648 var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12654 cfg.cls = 'form-group' //input-group
12659 type : this.inputType,
12660 value : (!this.checked) ? this.valueOff : this.inputValue,
12662 placeholder : this.placeholder || ''
12666 if (this.disabled) {
12667 input.disabled=true;
12671 input.checked = this.checked;
12675 input.name = this.name;
12679 input.cls += ' input-' + this.size;
12683 ['xs','sm','md','lg'].map(function(size){
12684 if (settings[size]) {
12685 cfg.cls += ' col-' + size + '-' + settings[size];
12689 var inputblock = input;
12691 if (this.before || this.after) {
12694 cls : 'input-group',
12698 inputblock.cn.push({
12700 cls : 'input-group-addon',
12704 inputblock.cn.push(input);
12706 inputblock.cn.push({
12708 cls : 'input-group-addon',
12715 if (align ==='left' && this.fieldLabel.length) {
12716 Roo.log("left and has label");
12722 cls : 'control-label col-md-' + this.labelWidth,
12723 html : this.fieldLabel
12727 cls : "col-md-" + (12 - this.labelWidth),
12734 } else if ( this.fieldLabel.length) {
12741 cls: 'control-label box-input-label',
12742 //cls : 'input-group-addon',
12743 html : this.fieldLabel
12753 Roo.log(" no label && no align");
12768 html: this.boxLabel
12776 onClick : function()
12778 this.setChecked(true);
12781 setChecked : function(state,suppressEvent)
12784 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12785 v.dom.checked = false;
12789 this.checked = state;
12790 this.inputEl().dom.checked = state;
12792 if(suppressEvent !== true){
12793 this.fireEvent('check', this, state);
12796 this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
12800 getGroupValue : function()
12803 Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
12804 if(v.dom.checked == true){
12805 value = v.dom.value;
12813 * Returns the normalized data value (undefined or emptyText will be returned as ''). To return the raw value see {@link #getRawValue}.
12814 * @return {Mixed} value The field value
12816 getValue : function(){
12817 return this.getGroupValue();
12823 //<script type="text/javascript">
12826 * Based Ext JS Library 1.1.1
12827 * Copyright(c) 2006-2007, Ext JS, LLC.
12833 * @class Roo.HtmlEditorCore
12834 * @extends Roo.Component
12835 * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
12837 * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
12840 Roo.HtmlEditorCore = function(config){
12843 Roo.HtmlEditorCore.superclass.constructor.call(this, config);
12846 * @event initialize
12847 * Fires when the editor is fully initialized (including the iframe)
12848 * @param {Roo.HtmlEditorCore} this
12853 * Fires when the editor is first receives the focus. Any insertion must wait
12854 * until after this event.
12855 * @param {Roo.HtmlEditorCore} this
12859 * @event beforesync
12860 * Fires before the textarea is updated with content from the editor iframe. Return false
12861 * to cancel the sync.
12862 * @param {Roo.HtmlEditorCore} this
12863 * @param {String} html
12867 * @event beforepush
12868 * Fires before the iframe editor is updated with content from the textarea. Return false
12869 * to cancel the push.
12870 * @param {Roo.HtmlEditorCore} this
12871 * @param {String} html
12876 * Fires when the textarea is updated with content from the editor iframe.
12877 * @param {Roo.HtmlEditorCore} this
12878 * @param {String} html
12883 * Fires when the iframe editor is updated with content from the textarea.
12884 * @param {Roo.HtmlEditorCore} this
12885 * @param {String} html
12890 * @event editorevent
12891 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
12892 * @param {Roo.HtmlEditorCore} this
12900 Roo.extend(Roo.HtmlEditorCore, Roo.Component, {
12904 * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field
12910 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
12915 * @cfg {Number} height (in pixels)
12919 * @cfg {Number} width (in pixels)
12924 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
12927 stylesheets: false,
12932 // private properties
12933 validationEvent : false,
12935 initialized : false,
12937 sourceEditMode : false,
12938 onFocus : Roo.emptyFn,
12940 hideMode:'offsets',
12948 * Protected method that will not generally be called directly. It
12949 * is called when the editor initializes the iframe with HTML contents. Override this method if you
12950 * want to change the initialization markup of the iframe (e.g. to add stylesheets).
12952 getDocMarkup : function(){
12955 Roo.log(this.stylesheets);
12957 // inherit styels from page...??
12958 if (this.stylesheets === false) {
12960 Roo.get(document.head).select('style').each(function(node) {
12961 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12964 Roo.get(document.head).select('link').each(function(node) {
12965 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
12968 } else if (!this.stylesheets.length) {
12970 st = '<style type="text/css">' +
12971 'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12974 Roo.each(this.stylesheets, function(s) {
12975 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
12980 st += '<style type="text/css">' +
12981 'IMG { cursor: pointer } ' +
12985 return '<html><head>' + st +
12986 //<style type="text/css">' +
12987 //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
12989 ' </head><body class="roo-htmleditor-body"></body></html>';
12993 onRender : function(ct, position)
12996 //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
12997 this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
13000 this.el.dom.style.border = '0 none';
13001 this.el.dom.setAttribute('tabIndex', -1);
13002 this.el.addClass('x-hidden hide');
13006 if(Roo.isIE){ // fix IE 1px bogus margin
13007 this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
13011 this.frameId = Roo.id();
13015 var iframe = this.owner.wrap.createChild({
13017 cls: 'form-control', // bootstrap..
13019 name: this.frameId,
13020 frameBorder : 'no',
13021 'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL : "javascript:false"
13026 this.iframe = iframe.dom;
13028 this.assignDocWin();
13030 this.doc.designMode = 'on';
13033 this.doc.write(this.getDocMarkup());
13037 var task = { // must defer to wait for browser to be ready
13039 //console.log("run task?" + this.doc.readyState);
13040 this.assignDocWin();
13041 if(this.doc.body || this.doc.readyState == 'complete'){
13043 this.doc.designMode="on";
13047 Roo.TaskMgr.stop(task);
13048 this.initEditor.defer(10, this);
13055 Roo.TaskMgr.start(task);
13062 onResize : function(w, h)
13064 Roo.log('resize: ' +w + ',' + h );
13065 //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
13069 if(typeof w == 'number'){
13071 this.iframe.style.width = w + 'px';
13073 if(typeof h == 'number'){
13075 this.iframe.style.height = h + 'px';
13077 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
13084 * Toggles the editor between standard and source edit mode.
13085 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
13087 toggleSourceEdit : function(sourceEditMode){
13089 this.sourceEditMode = sourceEditMode === true;
13091 if(this.sourceEditMode){
13093 Roo.get(this.iframe).addClass(['x-hidden','hide']); //FIXME - what's the BS styles for these
13096 Roo.get(this.iframe).removeClass(['x-hidden','hide']);
13097 //this.iframe.className = '';
13100 //this.setSize(this.owner.wrap.getSize());
13101 //this.fireEvent('editmodechange', this, this.sourceEditMode);
13108 * Protected method that will not generally be called directly. If you need/want
13109 * custom HTML cleanup, this is the method you should override.
13110 * @param {String} html The HTML to be cleaned
13111 * return {String} The cleaned HTML
13113 cleanHtml : function(html){
13114 html = String(html);
13115 if(html.length > 5){
13116 if(Roo.isSafari){ // strip safari nonsense
13117 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
13120 if(html == ' '){
13127 * HTML Editor -> Textarea
13128 * Protected method that will not generally be called directly. Syncs the contents
13129 * of the editor iframe with the textarea.
13131 syncValue : function(){
13132 if(this.initialized){
13133 var bd = (this.doc.body || this.doc.documentElement);
13134 //this.cleanUpPaste(); -- this is done else where and causes havoc..
13135 var html = bd.innerHTML;
13137 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
13138 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
13140 html = '<div style="'+m[0]+'">' + html + '</div>';
13143 html = this.cleanHtml(html);
13144 // fix up the special chars.. normaly like back quotes in word...
13145 // however we do not want to do this with chinese..
13146 html = html.replace(/([\x80-\uffff])/g, function (a, b) {
13147 var cc = b.charCodeAt();
13149 (cc >= 0x4E00 && cc < 0xA000 ) ||
13150 (cc >= 0x3400 && cc < 0x4E00 ) ||
13151 (cc >= 0xf900 && cc < 0xfb00 )
13157 if(this.owner.fireEvent('beforesync', this, html) !== false){
13158 this.el.dom.value = html;
13159 this.owner.fireEvent('sync', this, html);
13165 * Protected method that will not generally be called directly. Pushes the value of the textarea
13166 * into the iframe editor.
13168 pushValue : function(){
13169 if(this.initialized){
13170 var v = this.el.dom.value.trim();
13172 // if(v.length < 1){
13176 if(this.owner.fireEvent('beforepush', this, v) !== false){
13177 var d = (this.doc.body || this.doc.documentElement);
13179 this.cleanUpPaste();
13180 this.el.dom.value = d.innerHTML;
13181 this.owner.fireEvent('push', this, v);
13187 deferFocus : function(){
13188 this.focus.defer(10, this);
13192 focus : function(){
13193 if(this.win && !this.sourceEditMode){
13200 assignDocWin: function()
13202 var iframe = this.iframe;
13205 this.doc = iframe.contentWindow.document;
13206 this.win = iframe.contentWindow;
13208 if (!Roo.get(this.frameId)) {
13211 this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
13212 this.win = Roo.get(this.frameId).dom.contentWindow;
13217 initEditor : function(){
13218 //console.log("INIT EDITOR");
13219 this.assignDocWin();
13223 this.doc.designMode="on";
13225 this.doc.write(this.getDocMarkup());
13228 var dbody = (this.doc.body || this.doc.documentElement);
13229 //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
13230 // this copies styles from the containing element into thsi one..
13231 // not sure why we need all of this..
13232 var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
13233 ss['background-attachment'] = 'fixed'; // w3c
13234 dbody.bgProperties = 'fixed'; // ie
13235 Roo.DomHelper.applyStyles(dbody, ss);
13236 Roo.EventManager.on(this.doc, {
13237 //'mousedown': this.onEditorEvent,
13238 'mouseup': this.onEditorEvent,
13239 'dblclick': this.onEditorEvent,
13240 'click': this.onEditorEvent,
13241 'keyup': this.onEditorEvent,
13246 Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
13248 if(Roo.isIE || Roo.isSafari || Roo.isOpera){
13249 Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
13251 this.initialized = true;
13253 this.owner.fireEvent('initialize', this);
13258 onDestroy : function(){
13264 //for (var i =0; i < this.toolbars.length;i++) {
13265 // // fixme - ask toolbars for heights?
13266 // this.toolbars[i].onDestroy();
13269 //this.wrap.dom.innerHTML = '';
13270 //this.wrap.remove();
13275 onFirstFocus : function(){
13277 this.assignDocWin();
13280 this.activated = true;
13283 if(Roo.isGecko){ // prevent silly gecko errors
13285 var s = this.win.getSelection();
13286 if(!s.focusNode || s.focusNode.nodeType != 3){
13287 var r = s.getRangeAt(0);
13288 r.selectNodeContents((this.doc.body || this.doc.documentElement));
13293 this.execCmd('useCSS', true);
13294 this.execCmd('styleWithCSS', false);
13297 this.owner.fireEvent('activate', this);
13301 adjustFont: function(btn){
13302 var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
13303 //if(Roo.isSafari){ // safari
13306 var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
13307 if(Roo.isSafari){ // safari
13308 var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
13309 v = (v < 10) ? 10 : v;
13310 v = (v > 48) ? 48 : v;
13311 v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
13316 v = Math.max(1, v+adjust);
13318 this.execCmd('FontSize', v );
13321 onEditorEvent : function(e){
13322 this.owner.fireEvent('editorevent', this, e);
13323 // this.updateToolbar();
13324 this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
13327 insertTag : function(tg)
13329 // could be a bit smarter... -> wrap the current selected tRoo..
13330 if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
13332 range = this.createRange(this.getSelection());
13333 var wrappingNode = this.doc.createElement(tg.toLowerCase());
13334 wrappingNode.appendChild(range.extractContents());
13335 range.insertNode(wrappingNode);
13342 this.execCmd("formatblock", tg);
13346 insertText : function(txt)
13350 var range = this.createRange();
13351 range.deleteContents();
13352 //alert(Sender.getAttribute('label'));
13354 range.insertNode(this.doc.createTextNode(txt));
13360 * Executes a Midas editor command on the editor document and performs necessary focus and
13361 * toolbar updates. <b>This should only be called after the editor is initialized.</b>
13362 * @param {String} cmd The Midas command
13363 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13365 relayCmd : function(cmd, value){
13367 this.execCmd(cmd, value);
13368 this.owner.fireEvent('editorevent', this);
13369 //this.updateToolbar();
13370 this.owner.deferFocus();
13374 * Executes a Midas editor command directly on the editor document.
13375 * For visual commands, you should use {@link #relayCmd} instead.
13376 * <b>This should only be called after the editor is initialized.</b>
13377 * @param {String} cmd The Midas command
13378 * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
13380 execCmd : function(cmd, value){
13381 this.doc.execCommand(cmd, false, value === undefined ? null : value);
13388 * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
13390 * @param {String} text | dom node..
13392 insertAtCursor : function(text)
13397 if(!this.activated){
13403 var r = this.doc.selection.createRange();
13414 if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
13418 // from jquery ui (MIT licenced)
13420 var win = this.win;
13422 if (win.getSelection && win.getSelection().getRangeAt) {
13423 range = win.getSelection().getRangeAt(0);
13424 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
13425 range.insertNode(node);
13426 } else if (win.document.selection && win.document.selection.createRange) {
13427 // no firefox support
13428 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13429 win.document.selection.createRange().pasteHTML(txt);
13431 // no firefox support
13432 var txt = typeof(text) == 'string' ? text : text.outerHTML;
13433 this.execCmd('InsertHTML', txt);
13442 mozKeyPress : function(e){
13444 var c = e.getCharCode(), cmd;
13447 c = String.fromCharCode(c).toLowerCase();
13461 this.cleanUpPaste.defer(100, this);
13469 e.preventDefault();
13477 fixKeys : function(){ // load time branching for fastest keydown performance
13479 return function(e){
13480 var k = e.getKey(), r;
13483 r = this.doc.selection.createRange();
13486 r.pasteHTML('    ');
13493 r = this.doc.selection.createRange();
13495 var target = r.parentElement();
13496 if(!target || target.tagName.toLowerCase() != 'li'){
13498 r.pasteHTML('<br />');
13504 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13505 this.cleanUpPaste.defer(100, this);
13511 }else if(Roo.isOpera){
13512 return function(e){
13513 var k = e.getKey();
13517 this.execCmd('InsertHTML','    ');
13520 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13521 this.cleanUpPaste.defer(100, this);
13526 }else if(Roo.isSafari){
13527 return function(e){
13528 var k = e.getKey();
13532 this.execCmd('InsertText','\t');
13536 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
13537 this.cleanUpPaste.defer(100, this);
13545 getAllAncestors: function()
13547 var p = this.getSelectedNode();
13550 a.push(p); // push blank onto stack..
13551 p = this.getParentElement();
13555 while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
13559 a.push(this.doc.body);
13563 lastSelNode : false,
13566 getSelection : function()
13568 this.assignDocWin();
13569 return Roo.isIE ? this.doc.selection : this.win.getSelection();
13572 getSelectedNode: function()
13574 // this may only work on Gecko!!!
13576 // should we cache this!!!!
13581 var range = this.createRange(this.getSelection()).cloneRange();
13584 var parent = range.parentElement();
13586 var testRange = range.duplicate();
13587 testRange.moveToElementText(parent);
13588 if (testRange.inRange(range)) {
13591 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
13594 parent = parent.parentElement;
13599 // is ancestor a text element.
13600 var ac = range.commonAncestorContainer;
13601 if (ac.nodeType == 3) {
13602 ac = ac.parentNode;
13605 var ar = ac.childNodes;
13608 var other_nodes = [];
13609 var has_other_nodes = false;
13610 for (var i=0;i<ar.length;i++) {
13611 if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ?
13614 // fullly contained node.
13616 if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
13621 // probably selected..
13622 if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
13623 other_nodes.push(ar[i]);
13627 if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0)) {
13632 has_other_nodes = true;
13634 if (!nodes.length && other_nodes.length) {
13635 nodes= other_nodes;
13637 if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
13643 createRange: function(sel)
13645 // this has strange effects when using with
13646 // top toolbar - not sure if it's a great idea.
13647 //this.editor.contentWindow.focus();
13648 if (typeof sel != "undefined") {
13650 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
13652 return this.doc.createRange();
13655 return this.doc.createRange();
13658 getParentElement: function()
13661 this.assignDocWin();
13662 var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
13664 var range = this.createRange(sel);
13667 var p = range.commonAncestorContainer;
13668 while (p.nodeType == 3) { // text node
13679 * Range intersection.. the hard stuff...
13683 * [ -- selected range --- ]
13687 * if end is before start or hits it. fail.
13688 * if start is after end or hits it fail.
13690 * if either hits (but other is outside. - then it's not
13696 // @see http://www.thismuchiknow.co.uk/?p=64.
13697 rangeIntersectsNode : function(range, node)
13699 var nodeRange = node.ownerDocument.createRange();
13701 nodeRange.selectNode(node);
13703 nodeRange.selectNodeContents(node);
13706 var rangeStartRange = range.cloneRange();
13707 rangeStartRange.collapse(true);
13709 var rangeEndRange = range.cloneRange();
13710 rangeEndRange.collapse(false);
13712 var nodeStartRange = nodeRange.cloneRange();
13713 nodeStartRange.collapse(true);
13715 var nodeEndRange = nodeRange.cloneRange();
13716 nodeEndRange.collapse(false);
13718 return rangeStartRange.compareBoundaryPoints(
13719 Range.START_TO_START, nodeEndRange) == -1 &&
13720 rangeEndRange.compareBoundaryPoints(
13721 Range.START_TO_START, nodeStartRange) == 1;
13725 rangeCompareNode : function(range, node)
13727 var nodeRange = node.ownerDocument.createRange();
13729 nodeRange.selectNode(node);
13731 nodeRange.selectNodeContents(node);
13735 range.collapse(true);
13737 nodeRange.collapse(true);
13739 var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
13740 var ee = range.compareBoundaryPoints( Range.END_TO_END, nodeRange);
13742 //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
13744 var nodeIsBefore = ss == 1;
13745 var nodeIsAfter = ee == -1;
13747 if (nodeIsBefore && nodeIsAfter)
13749 if (!nodeIsBefore && nodeIsAfter)
13750 return 1; //right trailed.
13752 if (nodeIsBefore && !nodeIsAfter)
13753 return 2; // left trailed.
13758 // private? - in a new class?
13759 cleanUpPaste : function()
13761 // cleans up the whole document..
13762 Roo.log('cleanuppaste');
13764 this.cleanUpChildren(this.doc.body);
13765 var clean = this.cleanWordChars(this.doc.body.innerHTML);
13766 if (clean != this.doc.body.innerHTML) {
13767 this.doc.body.innerHTML = clean;
13772 cleanWordChars : function(input) {// change the chars to hex code
13773 var he = Roo.HtmlEditorCore;
13775 var output = input;
13776 Roo.each(he.swapCodes, function(sw) {
13777 var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
13779 output = output.replace(swapper, sw[1]);
13786 cleanUpChildren : function (n)
13788 if (!n.childNodes.length) {
13791 for (var i = n.childNodes.length-1; i > -1 ; i--) {
13792 this.cleanUpChild(n.childNodes[i]);
13799 cleanUpChild : function (node)
13802 //console.log(node);
13803 if (node.nodeName == "#text") {
13804 // clean up silly Windows -- stuff?
13807 if (node.nodeName == "#comment") {
13808 node.parentNode.removeChild(node);
13809 // clean up silly Windows -- stuff?
13813 if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1 && this.clearUp) {
13815 node.parentNode.removeChild(node);
13820 var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
13822 // remove <a name=....> as rendering on yahoo mailer is borked with this.
13823 // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
13825 //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
13826 // remove_keep_children = true;
13829 if (remove_keep_children) {
13830 this.cleanUpChildren(node);
13831 // inserts everything just before this node...
13832 while (node.childNodes.length) {
13833 var cn = node.childNodes[0];
13834 node.removeChild(cn);
13835 node.parentNode.insertBefore(cn, node);
13837 node.parentNode.removeChild(node);
13841 if (!node.attributes || !node.attributes.length) {
13842 this.cleanUpChildren(node);
13846 function cleanAttr(n,v)
13849 if (v.match(/^\./) || v.match(/^\//)) {
13852 if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
13855 if (v.match(/^#/)) {
13858 // Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
13859 node.removeAttribute(n);
13863 function cleanStyle(n,v)
13865 if (v.match(/expression/)) { //XSS?? should we even bother..
13866 node.removeAttribute(n);
13869 var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
13870 var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
13873 var parts = v.split(/;/);
13876 Roo.each(parts, function(p) {
13877 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
13881 var l = p.split(':').shift().replace(/\s+/g,'');
13882 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
13884 if ( cblack.indexOf(l) > -1) {
13885 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13886 //node.removeAttribute(n);
13890 // only allow 'c whitelisted system attributes'
13891 if ( cwhite.length && cwhite.indexOf(l) < 0) {
13892 // Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
13893 //node.removeAttribute(n);
13903 if (clean.length) {
13904 node.setAttribute(n, clean.join(';'));
13906 node.removeAttribute(n);
13912 for (var i = node.attributes.length-1; i > -1 ; i--) {
13913 var a = node.attributes[i];
13916 if (a.name.toLowerCase().substr(0,2)=='on') {
13917 node.removeAttribute(a.name);
13920 if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
13921 node.removeAttribute(a.name);
13924 if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
13925 cleanAttr(a.name,a.value); // fixme..
13928 if (a.name == 'style') {
13929 cleanStyle(a.name,a.value);
13932 /// clean up MS crap..
13933 // tecnically this should be a list of valid class'es..
13936 if (a.name == 'class') {
13937 if (a.value.match(/^Mso/)) {
13938 node.className = '';
13941 if (a.value.match(/body/)) {
13942 node.className = '';
13953 this.cleanUpChildren(node);
13959 // hide stuff that is not compatible
13973 * @event specialkey
13977 * @cfg {String} fieldClass @hide
13980 * @cfg {String} focusClass @hide
13983 * @cfg {String} autoCreate @hide
13986 * @cfg {String} inputType @hide
13989 * @cfg {String} invalidClass @hide
13992 * @cfg {String} invalidText @hide
13995 * @cfg {String} msgFx @hide
13998 * @cfg {String} validateOnBlur @hide
14002 Roo.HtmlEditorCore.white = [
14003 'area', 'br', 'img', 'input', 'hr', 'wbr',
14005 'address', 'blockquote', 'center', 'dd', 'dir', 'div',
14006 'dl', 'dt', 'h1', 'h2', 'h3', 'h4',
14007 'h5', 'h6', 'hr', 'isindex', 'listing', 'marquee',
14008 'menu', 'multicol', 'ol', 'p', 'plaintext', 'pre',
14009 'table', 'ul', 'xmp',
14011 'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th',
14014 'dir', 'menu', 'ol', 'ul', 'dl',
14020 Roo.HtmlEditorCore.black = [
14021 // 'embed', 'object', // enable - backend responsiblity to clean thiese
14023 'base', 'basefont', 'bgsound', 'blink', 'body',
14024 'frame', 'frameset', 'head', 'html', 'ilayer',
14025 'iframe', 'layer', 'link', 'meta', 'object',
14026 'script', 'style' ,'title', 'xml' // clean later..
14028 Roo.HtmlEditorCore.clean = [
14029 'script', 'style', 'title', 'xml'
14031 Roo.HtmlEditorCore.remove = [
14036 Roo.HtmlEditorCore.ablack = [
14040 Roo.HtmlEditorCore.aclean = [
14041 'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc'
14045 Roo.HtmlEditorCore.pwhite= [
14046 'http', 'https', 'mailto'
14049 // white listed style attributes.
14050 Roo.HtmlEditorCore.cwhite= [
14051 // 'text-align', /// default is to allow most things..
14057 // black listed style attributes.
14058 Roo.HtmlEditorCore.cblack= [
14059 // 'font-size' -- this can be set by the project
14063 Roo.HtmlEditorCore.swapCodes =[
14082 * @class Roo.bootstrap.HtmlEditor
14083 * @extends Roo.bootstrap.TextArea
14084 * Bootstrap HtmlEditor class
14087 * Create a new HtmlEditor
14088 * @param {Object} config The config object
14091 Roo.bootstrap.HtmlEditor = function(config){
14092 Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
14093 if (!this.toolbars) {
14094 this.toolbars = [];
14096 this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
14099 * @event initialize
14100 * Fires when the editor is fully initialized (including the iframe)
14101 * @param {HtmlEditor} this
14106 * Fires when the editor is first receives the focus. Any insertion must wait
14107 * until after this event.
14108 * @param {HtmlEditor} this
14112 * @event beforesync
14113 * Fires before the textarea is updated with content from the editor iframe. Return false
14114 * to cancel the sync.
14115 * @param {HtmlEditor} this
14116 * @param {String} html
14120 * @event beforepush
14121 * Fires before the iframe editor is updated with content from the textarea. Return false
14122 * to cancel the push.
14123 * @param {HtmlEditor} this
14124 * @param {String} html
14129 * Fires when the textarea is updated with content from the editor iframe.
14130 * @param {HtmlEditor} this
14131 * @param {String} html
14136 * Fires when the iframe editor is updated with content from the textarea.
14137 * @param {HtmlEditor} this
14138 * @param {String} html
14142 * @event editmodechange
14143 * Fires when the editor switches edit modes
14144 * @param {HtmlEditor} this
14145 * @param {Boolean} sourceEdit True if source edit, false if standard editing.
14147 editmodechange: true,
14149 * @event editorevent
14150 * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
14151 * @param {HtmlEditor} this
14155 * @event firstfocus
14156 * Fires when on first focus - needed by toolbars..
14157 * @param {HtmlEditor} this
14162 * Auto save the htmlEditor value as a file into Events
14163 * @param {HtmlEditor} this
14167 * @event savedpreview
14168 * preview the saved version of htmlEditor
14169 * @param {HtmlEditor} this
14176 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea, {
14180 * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
14185 * @cfg {String} resizable 's' or 'se' or 'e' - wrapps the element in a
14190 * @cfg {Number} height (in pixels)
14194 * @cfg {Number} width (in pixels)
14199 * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
14202 stylesheets: false,
14207 // private properties
14208 validationEvent : false,
14210 initialized : false,
14213 onFocus : Roo.emptyFn,
14215 hideMode:'offsets',
14218 tbContainer : false,
14220 toolbarContainer :function() {
14221 return this.wrap.select('.x-html-editor-tb',true).first();
14225 * Protected method that will not generally be called directly. It
14226 * is called when the editor creates its toolbar. Override this method if you need to
14227 * add custom toolbar buttons.
14228 * @param {HtmlEditor} editor
14230 createToolbar : function(){
14232 Roo.log("create toolbars");
14234 this.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard({editor: this} ) ];
14235 this.toolbars[0].render(this.toolbarContainer());
14239 // if (!editor.toolbars || !editor.toolbars.length) {
14240 // editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
14243 // for (var i =0 ; i < editor.toolbars.length;i++) {
14244 // editor.toolbars[i] = Roo.factory(
14245 // typeof(editor.toolbars[i]) == 'string' ?
14246 // { xtype: editor.toolbars[i]} : editor.toolbars[i],
14247 // Roo.bootstrap.HtmlEditor);
14248 // editor.toolbars[i].init(editor);
14254 onRender : function(ct, position)
14256 // Roo.log("Call onRender: " + this.xtype);
14258 Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
14260 this.wrap = this.inputEl().wrap({
14261 cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
14264 this.editorcore.onRender(ct, position);
14266 if (this.resizable) {
14267 this.resizeEl = new Roo.Resizable(this.wrap, {
14271 minHeight : this.height,
14272 height: this.height,
14273 handles : this.resizable,
14276 resize : function(r, w, h) {
14277 _t.onResize(w,h); // -something
14283 this.createToolbar(this);
14286 if(!this.width && this.resizable){
14287 this.setSize(this.wrap.getSize());
14289 if (this.resizeEl) {
14290 this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
14291 // should trigger onReize..
14297 onResize : function(w, h)
14299 Roo.log('resize: ' +w + ',' + h );
14300 Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
14304 if(this.inputEl() ){
14305 if(typeof w == 'number'){
14306 var aw = w - this.wrap.getFrameWidth('lr');
14307 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
14310 if(typeof h == 'number'){
14311 var tbh = -11; // fixme it needs to tool bar size!
14312 for (var i =0; i < this.toolbars.length;i++) {
14313 // fixme - ask toolbars for heights?
14314 tbh += this.toolbars[i].el.getHeight();
14315 //if (this.toolbars[i].footer) {
14316 // tbh += this.toolbars[i].footer.el.getHeight();
14324 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
14325 ah -= 5; // knock a few pixes off for look..
14326 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
14330 Roo.log('onResize:' + [w,h,ew,eh].join(',') );
14331 this.editorcore.onResize(ew,eh);
14336 * Toggles the editor between standard and source edit mode.
14337 * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
14339 toggleSourceEdit : function(sourceEditMode)
14341 this.editorcore.toggleSourceEdit(sourceEditMode);
14343 if(this.editorcore.sourceEditMode){
14344 Roo.log('editor - showing textarea');
14347 // Roo.log(this.syncValue());
14349 this.inputEl().removeClass('hide');
14350 this.inputEl().dom.removeAttribute('tabIndex');
14351 this.inputEl().focus();
14353 Roo.log('editor - hiding textarea');
14355 // Roo.log(this.pushValue());
14358 this.inputEl().addClass('hide');
14359 this.inputEl().dom.setAttribute('tabIndex', -1);
14360 //this.deferFocus();
14363 if(this.resizable){
14364 this.setSize(this.wrap.getSize());
14367 this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
14370 // private (for BoxComponent)
14371 adjustSize : Roo.BoxComponent.prototype.adjustSize,
14373 // private (for BoxComponent)
14374 getResizeEl : function(){
14378 // private (for BoxComponent)
14379 getPositionEl : function(){
14384 initEvents : function(){
14385 this.originalValue = this.getValue();
14389 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14392 // markInvalid : Roo.emptyFn,
14394 // * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
14397 // clearInvalid : Roo.emptyFn,
14399 setValue : function(v){
14400 Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
14401 this.editorcore.pushValue();
14406 deferFocus : function(){
14407 this.focus.defer(10, this);
14411 focus : function(){
14412 this.editorcore.focus();
14418 onDestroy : function(){
14424 for (var i =0; i < this.toolbars.length;i++) {
14425 // fixme - ask toolbars for heights?
14426 this.toolbars[i].onDestroy();
14429 this.wrap.dom.innerHTML = '';
14430 this.wrap.remove();
14435 onFirstFocus : function(){
14436 //Roo.log("onFirstFocus");
14437 this.editorcore.onFirstFocus();
14438 for (var i =0; i < this.toolbars.length;i++) {
14439 this.toolbars[i].onFirstFocus();
14445 syncValue : function()
14447 this.editorcore.syncValue();
14450 pushValue : function()
14452 this.editorcore.pushValue();
14456 // hide stuff that is not compatible
14470 * @event specialkey
14474 * @cfg {String} fieldClass @hide
14477 * @cfg {String} focusClass @hide
14480 * @cfg {String} autoCreate @hide
14483 * @cfg {String} inputType @hide
14486 * @cfg {String} invalidClass @hide
14489 * @cfg {String} invalidText @hide
14492 * @cfg {String} msgFx @hide
14495 * @cfg {String} validateOnBlur @hide
14506 * @class Roo.bootstrap.HtmlEditorToolbar1
14511 new Roo.bootstrap.HtmlEditor({
14514 new Roo.bootstrap.HtmlEditorToolbar1({
14515 disable : { fonts: 1 , format: 1, ..., ... , ...],
14521 * @cfg {Object} disable List of elements to disable..
14522 * @cfg {Array} btns List of additional buttons.
14526 * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
14529 Roo.bootstrap.HtmlEditor.ToolbarStandard = function(config)
14532 Roo.apply(this, config);
14534 // default disabled, based on 'good practice'..
14535 this.disable = this.disable || {};
14536 Roo.applyIf(this.disable, {
14539 specialElements : true
14541 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.constructor.call(this, config);
14543 this.editor = config.editor;
14544 this.editorcore = config.editor.editorcore;
14546 this.buttons = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
14548 //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
14549 // dont call parent... till later.
14551 Roo.extend(Roo.bootstrap.HtmlEditor.ToolbarStandard, Roo.bootstrap.Navbar, {
14557 editorcore : false,
14562 "h1","h2","h3","h4","h5","h6",
14564 "abbr", "acronym", "address", "cite", "samp", "var",
14568 onRender : function(ct, position)
14570 // Roo.log("Call onRender: " + this.xtype);
14572 Roo.bootstrap.HtmlEditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
14574 this.el.dom.style.marginBottom = '0';
14576 var editorcore = this.editorcore;
14577 var editor= this.editor;
14580 var btn = function(id,cmd , toggle, handler){
14582 var event = toggle ? 'toggle' : 'click';
14587 xns: Roo.bootstrap,
14590 enableToggle:toggle !== false,
14592 pressed : toggle ? false : null,
14595 a.listeners[toggle ? 'toggle' : 'click'] = function() {
14596 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd || id);
14605 xns: Roo.bootstrap,
14606 glyphicon : 'font',
14610 xns: Roo.bootstrap,
14614 Roo.each(this.formats, function(f) {
14615 style.menu.items.push({
14617 xns: Roo.bootstrap,
14618 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
14623 editorcore.insertTag(this.tagname);
14630 children.push(style);
14633 btn('bold',false,true);
14634 btn('italic',false,true);
14635 btn('align-left', 'justifyleft',true);
14636 btn('align-center', 'justifycenter',true);
14637 btn('align-right' , 'justifyright',true);
14638 btn('link', false, false, function(btn) {
14639 //Roo.log("create link?");
14640 var url = prompt(this.createLinkText, this.defaultLinkValue);
14641 if(url && url != 'http:/'+'/'){
14642 this.editorcore.relayCmd('createlink', url);
14645 btn('list','insertunorderedlist',true);
14646 btn('pencil', false,true, function(btn){
14649 this.toggleSourceEdit(btn.pressed);
14655 xns: Roo.bootstrap,
14660 xns: Roo.bootstrap,
14665 cog.menu.items.push({
14667 xns: Roo.bootstrap,
14668 html : Clean styles,
14673 editorcore.insertTag(this.tagname);
14682 this.xtype = 'Navbar';
14684 for(var i=0;i< children.length;i++) {
14686 this.buttons.add(this.addxtypeChild(children[i]));
14690 editor.on('editorevent', this.updateToolbar, this);
14692 onBtnClick : function(id)
14694 this.editorcore.relayCmd(id);
14695 this.editorcore.focus();
14699 * Protected method that will not generally be called directly. It triggers
14700 * a toolbar update by reading the markup state of the current selection in the editor.
14702 updateToolbar: function(){
14704 if(!this.editorcore.activated){
14705 this.editor.onFirstFocus(); // is this neeed?
14709 var btns = this.buttons;
14710 var doc = this.editorcore.doc;
14711 btns.get('bold').setActive(doc.queryCommandState('bold'));
14712 btns.get('italic').setActive(doc.queryCommandState('italic'));
14713 //btns.get('underline').setActive(doc.queryCommandState('underline'));
14715 btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
14716 btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
14717 btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
14719 //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
14720 btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
14723 var ans = this.editorcore.getAllAncestors();
14724 if (this.formatCombo) {
14727 var store = this.formatCombo.store;
14728 this.formatCombo.setValue("");
14729 for (var i =0; i < ans.length;i++) {
14730 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
14732 this.formatCombo.setValue(ans[i].tagName.toLowerCase());
14740 // hides menus... - so this cant be on a menu...
14741 Roo.bootstrap.MenuMgr.hideAll();
14743 Roo.bootstrap.MenuMgr.hideAll();
14744 //this.editorsyncValue();
14746 onFirstFocus: function() {
14747 this.buttons.each(function(item){
14751 toggleSourceEdit : function(sourceEditMode){
14754 if(sourceEditMode){
14755 Roo.log("disabling buttons");
14756 this.buttons.each( function(item){
14757 if(item.cmd != 'pencil'){
14763 Roo.log("enabling buttons");
14764 if(this.editorcore.initialized){
14765 this.buttons.each( function(item){
14771 Roo.log("calling toggole on editor");
14772 // tell the editor that it's been pressed..
14773 this.editor.toggleSourceEdit(sourceEditMode);
14783 * @class Roo.bootstrap.Table.AbstractSelectionModel
14784 * @extends Roo.util.Observable
14785 * Abstract base class for grid SelectionModels. It provides the interface that should be
14786 * implemented by descendant classes. This class should not be directly instantiated.
14789 Roo.bootstrap.Table.AbstractSelectionModel = function(){
14790 this.locked = false;
14791 Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
14795 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable, {
14796 /** @ignore Called by the grid automatically. Do not call directly. */
14797 init : function(grid){
14803 * Locks the selections.
14806 this.locked = true;
14810 * Unlocks the selections.
14812 unlock : function(){
14813 this.locked = false;
14817 * Returns true if the selections are locked.
14818 * @return {Boolean}
14820 isLocked : function(){
14821 return this.locked;
14825 * @class Roo.bootstrap.Table.ColumnModel
14826 * @extends Roo.util.Observable
14827 * This is the default implementation of a ColumnModel used by the bootstrap table. It defines
14828 * the columns in the table.
14831 * @param {Object} config An Array of column config objects. See this class's
14832 * config objects for details.
14834 Roo.bootstrap.Table.ColumnModel = function(config){
14836 * The config passed into the constructor
14838 this.config = config;
14841 // if no id, create one
14842 // if the column does not have a dataIndex mapping,
14843 // map it to the order it is in the config
14844 for(var i = 0, len = config.length; i < len; i++){
14846 if(typeof c.dataIndex == "undefined"){
14849 if(typeof c.renderer == "string"){
14850 c.renderer = Roo.util.Format[c.renderer];
14852 if(typeof c.id == "undefined"){
14855 // if(c.editor && c.editor.xtype){
14856 // c.editor = Roo.factory(c.editor, Roo.grid);
14858 // if(c.editor && c.editor.isFormField){
14859 // c.editor = new Roo.grid.GridEditor(c.editor);
14862 this.lookup[c.id] = c;
14866 * The width of columns which have no width specified (defaults to 100)
14869 this.defaultWidth = 100;
14872 * Default sortable of columns which have no sortable specified (defaults to false)
14875 this.defaultSortable = false;
14879 * @event widthchange
14880 * Fires when the width of a column changes.
14881 * @param {ColumnModel} this
14882 * @param {Number} columnIndex The column index
14883 * @param {Number} newWidth The new width
14885 "widthchange": true,
14887 * @event headerchange
14888 * Fires when the text of a header changes.
14889 * @param {ColumnModel} this
14890 * @param {Number} columnIndex The column index
14891 * @param {Number} newText The new header text
14893 "headerchange": true,
14895 * @event hiddenchange
14896 * Fires when a column is hidden or "unhidden".
14897 * @param {ColumnModel} this
14898 * @param {Number} columnIndex The column index
14899 * @param {Boolean} hidden true if hidden, false otherwise
14901 "hiddenchange": true,
14903 * @event columnmoved
14904 * Fires when a column is moved.
14905 * @param {ColumnModel} this
14906 * @param {Number} oldIndex
14907 * @param {Number} newIndex
14909 "columnmoved" : true,
14911 * @event columlockchange
14912 * Fires when a column's locked state is changed
14913 * @param {ColumnModel} this
14914 * @param {Number} colIndex
14915 * @param {Boolean} locked true if locked
14917 "columnlockchange" : true
14919 Roo.bootstrap.Table.ColumnModel.superclass.constructor.call(this);
14921 Roo.extend(Roo.bootstrap.Table.ColumnModel, Roo.util.Observable, {
14923 * @cfg {String} header The header text to display in the Grid view.
14926 * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
14927 * {@link Roo.data.Record} definition from which to draw the column's value. If not
14928 * specified, the column's index is used as an index into the Record's data Array.
14931 * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
14932 * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
14935 * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
14936 * Defaults to the value of the {@link #defaultSortable} property.
14937 * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
14940 * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid. Defaults to false.
14943 * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed. Defaults to false.
14946 * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
14949 * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
14952 * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
14953 * given the cell's data value. See {@link #setRenderer}. If not specified, the
14954 * default renderer uses the raw data value.
14957 * @cfg {String} align (Optional) Set the CSS text-align property of the column. Defaults to undefined.
14961 * Returns the id of the column at the specified index.
14962 * @param {Number} index The column index
14963 * @return {String} the id
14965 getColumnId : function(index){
14966 return this.config[index].id;
14970 * Returns the column for a specified id.
14971 * @param {String} id The column id
14972 * @return {Object} the column
14974 getColumnById : function(id){
14975 return this.lookup[id];
14980 * Returns the column for a specified dataIndex.
14981 * @param {String} dataIndex The column dataIndex
14982 * @return {Object|Boolean} the column or false if not found
14984 getColumnByDataIndex: function(dataIndex){
14985 var index = this.findColumnIndex(dataIndex);
14986 return index > -1 ? this.config[index] : false;
14990 * Returns the index for a specified column id.
14991 * @param {String} id The column id
14992 * @return {Number} the index, or -1 if not found
14994 getIndexById : function(id){
14995 for(var i = 0, len = this.config.length; i < len; i++){
14996 if(this.config[i].id == id){
15004 * Returns the index for a specified column dataIndex.
15005 * @param {String} dataIndex The column dataIndex
15006 * @return {Number} the index, or -1 if not found
15009 findColumnIndex : function(dataIndex){
15010 for(var i = 0, len = this.config.length; i < len; i++){
15011 if(this.config[i].dataIndex == dataIndex){
15019 moveColumn : function(oldIndex, newIndex){
15020 var c = this.config[oldIndex];
15021 this.config.splice(oldIndex, 1);
15022 this.config.splice(newIndex, 0, c);
15023 this.dataMap = null;
15024 this.fireEvent("columnmoved", this, oldIndex, newIndex);
15027 isLocked : function(colIndex){
15028 return this.config[colIndex].locked === true;
15031 setLocked : function(colIndex, value, suppressEvent){
15032 if(this.isLocked(colIndex) == value){
15035 this.config[colIndex].locked = value;
15036 if(!suppressEvent){
15037 this.fireEvent("columnlockchange", this, colIndex, value);
15041 getTotalLockedWidth : function(){
15042 var totalWidth = 0;
15043 for(var i = 0; i < this.config.length; i++){
15044 if(this.isLocked(i) && !this.isHidden(i)){
15045 this.totalWidth += this.getColumnWidth(i);
15051 getLockedCount : function(){
15052 for(var i = 0, len = this.config.length; i < len; i++){
15053 if(!this.isLocked(i)){
15060 * Returns the number of columns.
15063 getColumnCount : function(visibleOnly){
15064 if(visibleOnly === true){
15066 for(var i = 0, len = this.config.length; i < len; i++){
15067 if(!this.isHidden(i)){
15073 return this.config.length;
15077 * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
15078 * @param {Function} fn
15079 * @param {Object} scope (optional)
15080 * @return {Array} result
15082 getColumnsBy : function(fn, scope){
15084 for(var i = 0, len = this.config.length; i < len; i++){
15085 var c = this.config[i];
15086 if(fn.call(scope||this, c, i) === true){
15094 * Returns true if the specified column is sortable.
15095 * @param {Number} col The column index
15096 * @return {Boolean}
15098 isSortable : function(col){
15099 if(typeof this.config[col].sortable == "undefined"){
15100 return this.defaultSortable;
15102 return this.config[col].sortable;
15106 * Returns the rendering (formatting) function defined for the column.
15107 * @param {Number} col The column index.
15108 * @return {Function} The function used to render the cell. See {@link #setRenderer}.
15110 getRenderer : function(col){
15111 if(!this.config[col].renderer){
15112 return Roo.bootstrap.Table.ColumnModel.defaultRenderer;
15114 return this.config[col].renderer;
15118 * Sets the rendering (formatting) function for a column.
15119 * @param {Number} col The column index
15120 * @param {Function} fn The function to use to process the cell's raw data
15121 * to return HTML markup for the grid view. The render function is called with
15122 * the following parameters:<ul>
15123 * <li>Data value.</li>
15124 * <li>Cell metadata. An object in which you may set the following attributes:<ul>
15125 * <li>css A CSS style string to apply to the table cell.</li>
15126 * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
15127 * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
15128 * <li>Row index</li>
15129 * <li>Column index</li>
15130 * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
15132 setRenderer : function(col, fn){
15133 this.config[col].renderer = fn;
15137 * Returns the width for the specified column.
15138 * @param {Number} col The column index
15141 getColumnWidth : function(col){
15142 return this.config[col].width * 1 || this.defaultWidth;
15146 * Sets the width for a column.
15147 * @param {Number} col The column index
15148 * @param {Number} width The new width
15150 setColumnWidth : function(col, width, suppressEvent){
15151 this.config[col].width = width;
15152 this.totalWidth = null;
15153 if(!suppressEvent){
15154 this.fireEvent("widthchange", this, col, width);
15159 * Returns the total width of all columns.
15160 * @param {Boolean} includeHidden True to include hidden column widths
15163 getTotalWidth : function(includeHidden){
15164 if(!this.totalWidth){
15165 this.totalWidth = 0;
15166 for(var i = 0, len = this.config.length; i < len; i++){
15167 if(includeHidden || !this.isHidden(i)){
15168 this.totalWidth += this.getColumnWidth(i);
15172 return this.totalWidth;
15176 * Returns the header for the specified column.
15177 * @param {Number} col The column index
15180 getColumnHeader : function(col){
15181 return this.config[col].header;
15185 * Sets the header for a column.
15186 * @param {Number} col The column index
15187 * @param {String} header The new header
15189 setColumnHeader : function(col, header){
15190 this.config[col].header = header;
15191 this.fireEvent("headerchange", this, col, header);
15195 * Returns the tooltip for the specified column.
15196 * @param {Number} col The column index
15199 getColumnTooltip : function(col){
15200 return this.config[col].tooltip;
15203 * Sets the tooltip for a column.
15204 * @param {Number} col The column index
15205 * @param {String} tooltip The new tooltip
15207 setColumnTooltip : function(col, tooltip){
15208 this.config[col].tooltip = tooltip;
15212 * Returns the dataIndex for the specified column.
15213 * @param {Number} col The column index
15216 getDataIndex : function(col){
15217 return this.config[col].dataIndex;
15221 * Sets the dataIndex for a column.
15222 * @param {Number} col The column index
15223 * @param {Number} dataIndex The new dataIndex
15225 setDataIndex : function(col, dataIndex){
15226 this.config[col].dataIndex = dataIndex;
15232 * Returns true if the cell is editable.
15233 * @param {Number} colIndex The column index
15234 * @param {Number} rowIndex The row index
15235 * @return {Boolean}
15237 isCellEditable : function(colIndex, rowIndex){
15238 return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
15242 * Returns the editor defined for the cell/column.
15243 * return false or null to disable editing.
15244 * @param {Number} colIndex The column index
15245 * @param {Number} rowIndex The row index
15248 getCellEditor : function(colIndex, rowIndex){
15249 return this.config[colIndex].editor;
15253 * Sets if a column is editable.
15254 * @param {Number} col The column index
15255 * @param {Boolean} editable True if the column is editable
15257 setEditable : function(col, editable){
15258 this.config[col].editable = editable;
15263 * Returns true if the column is hidden.
15264 * @param {Number} colIndex The column index
15265 * @return {Boolean}
15267 isHidden : function(colIndex){
15268 return this.config[colIndex].hidden;
15273 * Returns true if the column width cannot be changed
15275 isFixed : function(colIndex){
15276 return this.config[colIndex].fixed;
15280 * Returns true if the column can be resized
15281 * @return {Boolean}
15283 isResizable : function(colIndex){
15284 return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
15287 * Sets if a column is hidden.
15288 * @param {Number} colIndex The column index
15289 * @param {Boolean} hidden True if the column is hidden
15291 setHidden : function(colIndex, hidden){
15292 this.config[colIndex].hidden = hidden;
15293 this.totalWidth = null;
15294 this.fireEvent("hiddenchange", this, colIndex, hidden);
15298 * Sets the editor for a column.
15299 * @param {Number} col The column index
15300 * @param {Object} editor The editor object
15302 setEditor : function(col, editor){
15303 this.config[col].editor = editor;
15307 Roo.bootstrap.Table.ColumnModel.defaultRenderer = function(value){
15308 if(typeof value == "string" && value.length < 1){
15314 // Alias for backwards compatibility
15315 Roo.bootstrap.Table.DefaultColumnModel = Roo.bootstrap.Table.ColumnModel;
15318 * @extends Roo.bootstrap.Table.AbstractSelectionModel
15319 * @class Roo.bootstrap.Table.RowSelectionModel
15320 * The default SelectionModel used by {@link Roo.bootstrap.Table}.
15321 * It supports multiple selections and keyboard selection/navigation.
15323 * @param {Object} config
15326 Roo.bootstrap.Table.RowSelectionModel = function(config){
15327 Roo.apply(this, config);
15328 this.selections = new Roo.util.MixedCollection(false, function(o){
15333 this.lastActive = false;
15337 * @event selectionchange
15338 * Fires when the selection changes
15339 * @param {SelectionModel} this
15341 "selectionchange" : true,
15343 * @event afterselectionchange
15344 * Fires after the selection changes (eg. by key press or clicking)
15345 * @param {SelectionModel} this
15347 "afterselectionchange" : true,
15349 * @event beforerowselect
15350 * Fires when a row is selected being selected, return false to cancel.
15351 * @param {SelectionModel} this
15352 * @param {Number} rowIndex The selected index
15353 * @param {Boolean} keepExisting False if other selections will be cleared
15355 "beforerowselect" : true,
15358 * Fires when a row is selected.
15359 * @param {SelectionModel} this
15360 * @param {Number} rowIndex The selected index
15361 * @param {Roo.data.Record} r The record
15363 "rowselect" : true,
15365 * @event rowdeselect
15366 * Fires when a row is deselected.
15367 * @param {SelectionModel} this
15368 * @param {Number} rowIndex The selected index
15370 "rowdeselect" : true
15372 Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
15373 this.locked = false;
15376 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel, {
15378 * @cfg {Boolean} singleSelect
15379 * True to allow selection of only one row at a time (defaults to false)
15381 singleSelect : false,
15384 initEvents : function(){
15386 if(!this.grid.enableDragDrop && !this.grid.enableDrag){
15387 this.grid.on("mousedown", this.handleMouseDown, this);
15388 }else{ // allow click to work like normal
15389 this.grid.on("rowclick", this.handleDragableRowClick, this);
15392 this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
15393 "up" : function(e){
15395 this.selectPrevious(e.shiftKey);
15396 }else if(this.last !== false && this.lastActive !== false){
15397 var last = this.last;
15398 this.selectRange(this.last, this.lastActive-1);
15399 this.grid.getView().focusRow(this.lastActive);
15400 if(last !== false){
15404 this.selectFirstRow();
15406 this.fireEvent("afterselectionchange", this);
15408 "down" : function(e){
15410 this.selectNext(e.shiftKey);
15411 }else if(this.last !== false && this.lastActive !== false){
15412 var last = this.last;
15413 this.selectRange(this.last, this.lastActive+1);
15414 this.grid.getView().focusRow(this.lastActive);
15415 if(last !== false){
15419 this.selectFirstRow();
15421 this.fireEvent("afterselectionchange", this);
15426 var view = this.grid.view;
15427 view.on("refresh", this.onRefresh, this);
15428 view.on("rowupdated", this.onRowUpdated, this);
15429 view.on("rowremoved", this.onRemove, this);
15433 onRefresh : function(){
15434 var ds = this.grid.dataSource, i, v = this.grid.view;
15435 var s = this.selections;
15436 s.each(function(r){
15437 if((i = ds.indexOfId(r.id)) != -1){
15446 onRemove : function(v, index, r){
15447 this.selections.remove(r);
15451 onRowUpdated : function(v, index, r){
15452 if(this.isSelected(r)){
15453 v.onRowSelect(index);
15459 * @param {Array} records The records to select
15460 * @param {Boolean} keepExisting (optional) True to keep existing selections
15462 selectRecords : function(records, keepExisting){
15464 this.clearSelections();
15466 var ds = this.grid.dataSource;
15467 for(var i = 0, len = records.length; i < len; i++){
15468 this.selectRow(ds.indexOf(records[i]), true);
15473 * Gets the number of selected rows.
15476 getCount : function(){
15477 return this.selections.length;
15481 * Selects the first row in the grid.
15483 selectFirstRow : function(){
15488 * Select the last row.
15489 * @param {Boolean} keepExisting (optional) True to keep existing selections
15491 selectLastRow : function(keepExisting){
15492 this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
15496 * Selects the row immediately following the last selected row.
15497 * @param {Boolean} keepExisting (optional) True to keep existing selections
15499 selectNext : function(keepExisting){
15500 if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
15501 this.selectRow(this.last+1, keepExisting);
15502 this.grid.getView().focusRow(this.last);
15507 * Selects the row that precedes the last selected row.
15508 * @param {Boolean} keepExisting (optional) True to keep existing selections
15510 selectPrevious : function(keepExisting){
15512 this.selectRow(this.last-1, keepExisting);
15513 this.grid.getView().focusRow(this.last);
15518 * Returns the selected records
15519 * @return {Array} Array of selected records
15521 getSelections : function(){
15522 return [].concat(this.selections.items);
15526 * Returns the first selected record.
15529 getSelected : function(){
15530 return this.selections.itemAt(0);
15535 * Clears all selections.
15537 clearSelections : function(fast){
15538 if(this.locked) return;
15540 var ds = this.grid.dataSource;
15541 var s = this.selections;
15542 s.each(function(r){
15543 this.deselectRow(ds.indexOfId(r.id));
15547 this.selections.clear();
15554 * Selects all rows.
15556 selectAll : function(){
15557 if(this.locked) return;
15558 this.selections.clear();
15559 for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
15560 this.selectRow(i, true);
15565 * Returns True if there is a selection.
15566 * @return {Boolean}
15568 hasSelection : function(){
15569 return this.selections.length > 0;
15573 * Returns True if the specified row is selected.
15574 * @param {Number/Record} record The record or index of the record to check
15575 * @return {Boolean}
15577 isSelected : function(index){
15578 var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
15579 return (r && this.selections.key(r.id) ? true : false);
15583 * Returns True if the specified record id is selected.
15584 * @param {String} id The id of record to check
15585 * @return {Boolean}
15587 isIdSelected : function(id){
15588 return (this.selections.key(id) ? true : false);
15592 handleMouseDown : function(e, t){
15593 var view = this.grid.getView(), rowIndex;
15594 if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
15597 if(e.shiftKey && this.last !== false){
15598 var last = this.last;
15599 this.selectRange(last, rowIndex, e.ctrlKey);
15600 this.last = last; // reset the last
15601 view.focusRow(rowIndex);
15603 var isSelected = this.isSelected(rowIndex);
15604 if(e.button !== 0 && isSelected){
15605 view.focusRow(rowIndex);
15606 }else if(e.ctrlKey && isSelected){
15607 this.deselectRow(rowIndex);
15608 }else if(!isSelected){
15609 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
15610 view.focusRow(rowIndex);
15613 this.fireEvent("afterselectionchange", this);
15616 handleDragableRowClick : function(grid, rowIndex, e)
15618 if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
15619 this.selectRow(rowIndex, false);
15620 grid.view.focusRow(rowIndex);
15621 this.fireEvent("afterselectionchange", this);
15626 * Selects multiple rows.
15627 * @param {Array} rows Array of the indexes of the row to select
15628 * @param {Boolean} keepExisting (optional) True to keep existing selections
15630 selectRows : function(rows, keepExisting){
15632 this.clearSelections();
15634 for(var i = 0, len = rows.length; i < len; i++){
15635 this.selectRow(rows[i], true);
15640 * Selects a range of rows. All rows in between startRow and endRow are also selected.
15641 * @param {Number} startRow The index of the first row in the range
15642 * @param {Number} endRow The index of the last row in the range
15643 * @param {Boolean} keepExisting (optional) True to retain existing selections
15645 selectRange : function(startRow, endRow, keepExisting){
15646 if(this.locked) return;
15648 this.clearSelections();
15650 if(startRow <= endRow){
15651 for(var i = startRow; i <= endRow; i++){
15652 this.selectRow(i, true);
15655 for(var i = startRow; i >= endRow; i--){
15656 this.selectRow(i, true);
15662 * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
15663 * @param {Number} startRow The index of the first row in the range
15664 * @param {Number} endRow The index of the last row in the range
15666 deselectRange : function(startRow, endRow, preventViewNotify){
15667 if(this.locked) return;
15668 for(var i = startRow; i <= endRow; i++){
15669 this.deselectRow(i, preventViewNotify);
15675 * @param {Number} row The index of the row to select
15676 * @param {Boolean} keepExisting (optional) True to keep existing selections
15678 selectRow : function(index, keepExisting, preventViewNotify){
15679 if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
15680 if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
15681 if(!keepExisting || this.singleSelect){
15682 this.clearSelections();
15684 var r = this.grid.dataSource.getAt(index);
15685 this.selections.add(r);
15686 this.last = this.lastActive = index;
15687 if(!preventViewNotify){
15688 this.grid.getView().onRowSelect(index);
15690 this.fireEvent("rowselect", this, index, r);
15691 this.fireEvent("selectionchange", this);
15697 * @param {Number} row The index of the row to deselect
15699 deselectRow : function(index, preventViewNotify){
15700 if(this.locked) return;
15701 if(this.last == index){
15704 if(this.lastActive == index){
15705 this.lastActive = false;
15707 var r = this.grid.dataSource.getAt(index);
15708 this.selections.remove(r);
15709 if(!preventViewNotify){
15710 this.grid.getView().onRowDeselect(index);
15712 this.fireEvent("rowdeselect", this, index);
15713 this.fireEvent("selectionchange", this);
15717 restoreLast : function(){
15719 this.last = this._last;
15724 acceptsNav : function(row, col, cm){
15725 return !cm.isHidden(col) && cm.isCellEditable(col, row);
15729 onEditorKey : function(field, e){
15730 var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
15735 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
15737 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
15739 }else if(k == e.ENTER && !e.ctrlKey){
15743 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
15745 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
15747 }else if(k == e.ESC){
15751 g.startEditing(newCell[0], newCell[1]);
15762 * @class Roo.bootstrap.MessageBar
15763 * @extends Roo.bootstrap.Component
15764 * Bootstrap MessageBar class
15765 * @cfg {String} html contents of the MessageBar
15766 * @cfg {String} weight (info | success | warning | danger) default info
15767 * @cfg {String} beforeClass insert the bar before the given class
15768 * @cfg {Boolean} closable (true | false) default false
15769 * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
15772 * Create a new Element
15773 * @param {Object} config The config object
15776 Roo.bootstrap.MessageBar = function(config){
15777 Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
15780 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component, {
15786 beforeClass: 'bootstrap-sticky-wrap',
15788 getAutoCreate : function(){
15792 cls: 'alert alert-dismissable alert-' + this.weight,
15797 html: this.html || ''
15803 cfg.cls += ' alert-messages-fixed';
15817 onRender : function(ct, position)
15819 Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
15822 var cfg = Roo.apply({}, this.getAutoCreate());
15826 cfg.cls += ' ' + this.cls;
15829 cfg.style = this.style;
15831 this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
15833 this.el.setVisibilityMode(Roo.Element.DISPLAY);
15836 this.el.select('>button.close').on('click', this.hide, this);
15842 if (!this.rendered) {
15848 this.fireEvent('show', this);
15854 if (!this.rendered) {
15860 this.fireEvent('hide', this);
15863 update : function()
15865 // var e = this.el.dom.firstChild;
15867 // if(this.closable){
15868 // e = e.nextSibling;
15871 // e.data = this.html || '';
15873 this.el.select('>.message', true).first().dom.innerHTML = this.html || '';